Trailing characters in Rust when trying to serialize API response - rust

I'm trying to wrap an API using rust, so long no problems but one endpoint is giving me a big pain in the head.
the endpoint returns a valid JSON as the other endpoints from this API.
The problem is when I try to work with the response I get the following error:
error decoding response body: trailing characters at line 1 column 146
Get function:
pub fn get<T: DeserializeOwned>(&self, endpoint: API, request: Option<String>) -> Result<T> {
let mut url: String = format!("{}{}", self.host, String::from(endpoint));
if let Some(request) = request {
if !request.is_empty() {
url.push_str(format!("?{}", request).as_str());
}
}
let client = &self.inner_client;
let response = client.get(url.as_str()).send()?;
self.handler(response)
}
Handler function:
fn handler<T: DeserializeOwned>(&self, response: Response) -> Result<T> {
match response.status() {
StatusCode::OK => {
let l = response.json::<T>()?;
Ok(l)
},
s => {
bail!(format!("Received response: {:?}", s));
}
}
}
Enpoint funcion:
pub fn get_depth<S>(&self, symbol: S) -> Result<OrderBook>
where
S: Into<String>,
{
let mut parameters: BTreeMap<String, String> = BTreeMap::new();
parameters.insert("symbol".into(), symbol.into());
let request = build_request(parameters);
self.client.get(API::Spot(Spot::Depth), Some(request))
}
Models:
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct OrderBook {
pub last_update_id: u64,
pub asks: Vec<Asks>,
pub bids: Vec<Bids>,
}
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
pub struct Bids {
#[serde(with = "string_or_float")]
pub price: f64,
#[serde(with = "string_or_float")]
pub qty: f64,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Asks {
#[serde(with = "string_or_float")]
pub price: f64,
#[serde(with = "string_or_float")]
pub qty: f64,
}
API response:
{
"lastUpdateId": 1595750078,
"bids": [
[
"0.00001661",
"24250.00000000",
[]
],
[
"0.00001660",
"65159.00000000",
[]
]
],
"asks": [
[
"0.00001662",
"26397.00000000",
[]
],
[
"0.00001663",
"54421.00000000",
[]
]
]
}
I was testing this mocking the API response and removing the empty arrays from asks and bids and it worked flawless. I don't know why the [] is a problem and I can figure out how to solve this problem. So really I will appreciate any help.

Your problem boils down to matching your rust struct with the response structure. I'm using the example with String from your playground link.
Response
{
"lastUpdateId":1595750078,
"bids":[
[
"0.00001661",
"24250.00000000",
[
]
],
[
"0.00001660",
"65159.00000000",
[
]
]
],
"asks":[
[
"0.00001662",
"26397.00000000"
],
[
"0.00001663",
"54421.00000000"
]
]
}
Your Structs
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct OrderBook {
pub last_update_id: u64,
pub asks: Vec<Asks>,
pub bids: Vec<Bids>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Bids {
pub price: String,
pub qty: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Asks {
pub price: String,
pub qty: String,
}
Now if you look at the bids json, the empty array is not represented in the Bids structure in your code. Hence calling serde_json::from_str::<OrderBook>(json) fails, as it's encountering a new array where nothing is expected anymore.
Solution
Introduce a new (optional) field in Bids, representing the array. I cannot say which structure this would be, as your example only shows the empty representation. pub x: Vec<()> would also work, though I'm certain the API might sent real data if available.
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Bids {
pub price: String,
pub qty: String,
pub something_else: Vec<()>, // or Vec<AnotherStruct>
}

Related

How to deserialize nested struct?

Assuming the following JSON should be read:
let json = r#"{
"scjson": [
{ "StateMachine": { "id": "sm_1" } },
{ "StateMachine": { "id": "sm_2" } }
]
}"#;
In words: An array of StateMachine, with each StateMachine has a field "id" from type string.
How can I deserialize this with serde?
I tried:
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct StateMachine {
id: String,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct Scjson {
scjson: Vec<StateMachine>,
}
But the ID is never deserialized.
In the end, I would like to parse:
scjson:
- StateMachine:
id: "sm_1"
states:
- AtomicState:
id: atomic_1
- AtomicState:
id: atomic_2
transitions:
- Transition: { event: "E1", executable_content: "asdf" }
- ParallelState:
InitialTransition: { }
- CompoundState:
id: compound_1
initialTransition: { event: "E2", condition: "some condition" }
- StateMachine:
id: "sm_2"
states:
- FinalState:
id: "asdf"
onEntry: "17"
You are missing one layer of indirection. The scjson key contains a list of YAML dictionaries, where each dictionary has a single key StateMachine, and its value is yet another dictionary with one key id.
Here's the fixed version:
use serde::{Deserialize, Serialize};
use serde_yaml;
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct StateMachine {
id: String,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct Scjson {
scjson: Vec<ScjsonElement>,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct ScjsonElement {
StateMachine: StateMachine,
}
fn main() {
let message = r#"
scjson:
- StateMachine:
id: "sm_1"
- StateMachine:
id: "sm_2"
"#;
let result: Scjson = serde_yaml::from_str(message).unwrap();
println!("{:?}", result)
}
This is the straightforward solution, but it seems that whatever produces the YAML/JSON uses StateMachine and similar keys to encode the element type. If that is the case, then enum is the proper answer:
use serde::{Deserialize, Serialize};
use serde_yaml;
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct StateMachine {
id: String,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct Scjson {
scjson: Vec<ScjsonElement>,
}
#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub enum ScjsonElement {
StateMachine(StateMachine),
}
fn main() {
let message = r#"
scjson:
- StateMachine:
id: "sm_1"
- StateMachine:
id: "sm_2"
"#;
let result: Scjson = serde_yaml::from_str(message).unwrap();
println!("{:?}", result)
}

how to ignore null field in rust web request [duplicate]

This question already has answers here:
How to deserialize a JSON file which contains null values using Serde?
(3 answers)
Closed 1 year ago.
I am using a structure to receive data from a http request body in rust. This is the struct define:
use rocket::serde::Deserialize;
use rocket::serde::Serialize;
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
#[allow(non_snake_case)]
pub struct LearningWordRequest {
pub wordId: i64,
pub word: String,
pub wordStatus: i32
}
but I have to put all of the fields into the body to parse, if I did not do it like that. The server will show:
2021-11-10T15:02:43 [WARN] - `Json < LearningWordRequest >` data guard failed: Parse("{\"word\":\"hood\",\"wordId\":-1}", Error("missing field `wordStatus`", line: 1, column: 27)).
is it possible to make the parse just ignore the field if I did not put this field? Sometimes it is no need to transfer all fields in the http body, this is my server side receive code:
#[post("/v1/add",data = "<record>")]
pub fn add_learning_word(record: Json<LearningWordRequest>) -> content::Json<String> {
let res = ApiResponse {
result: "ok",
..Default::default()
};
let response_json = serde_json::to_string(&res).unwrap();
return content::Json(response_json);
}
Change the struct member to be optional:
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
#[allow(non_snake_case)]
pub struct LearningWordRequest {
pub wordId: i64,
pub word: String,
pub wordStatus: Option<i32>
}
For example:
fn main() {
let present = r#" {
"wordId": 43,
"word": "foo",
"wordStatus": 1337
}"#;
let absent = r#" {
"wordId": 43,
"word": "foo"
}"#;
println!("{:?}", serde_json::from_str::<LearningWordRequest>(present));
println!("{:?}", serde_json::from_str::<LearningWordRequest>(absent));
}
Outputs:
Ok(LearningWordRequest { wordId: 43, word: "foo", wordStatus: Some(1337) })
Ok(LearningWordRequest { wordId: 43, word: "foo", wordStatus: None })

Multiple possible types for a serializable struct's field

I'm writing a simple bookmark manager which will save data in a JSON format like this;
{
"children": [
{
"name": "Social",
"children": [
{
"name": "Facebook",
"url": "https://facebook.com",
"faviconUrl": "",
"tags": [],
"keyword": "",
"createdAt": 8902351,
"modifiedAt": 90981235
}
],
"createdAt": 235123534,
"modifiedAt": 23531235
}
]
}
I've tried to write children field to allow two possible types (Directory and Bookmark) by creating a common Entry trait but I'm hitting a wall as I can't implement Serialize trait from serde for the Entry trait.
use serde::{Serialize, Deserialize, Serializer};
#[derive(Serialize, Deserialize)]
struct Root<'a> {
children: Vec<&'a dyn Entry>,
}
#[derive(Serialize, Deserialize)]
struct Directory<'a> {
name: String,
created_at: u64,
children: Vec<&'a dyn Entry>,
modified_at: u64
}
#[derive(Serialize, Deserialize)]
struct Bookmark {
name: String,
url: String,
favicon_url: String,
tags: Vec<String>,
keyword: String,
created_at: u64,
modified_at: u64,
}
trait Entry {
fn is_directory(&self) -> bool;
}
impl Entry for Directory<'_> {
fn is_directory(&self) -> bool {
true
}
}
impl Entry for Bookmark {
fn is_directory(&self) -> bool {
false
}
}
// can't do this
impl Serialize for Entry {}
Is it even possible to make this work or I should create a different structure which wouldn't contain a field with multiple possible values? I was thinking about loading the JSON as HashMap<String, serde_json::Value> and looping through the hash map but I was wondering if there is some more elegant way to do this.
If you just make the Entry an enum rather than a trait, and change the &dyn Entry to just Entry, then everything should just work, except you will end up with one extra level in your JSON, and an additional tag entry telling you what type the entry is. As Masklinn pointed out in the comments, the case is also incorrect, but can be fixed using #[serde(rename_all = "camelCase")].
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Root {
children: Vec<Entry>,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Directory {
name: String,
created_at: u64,
children: Vec<Entry>,
modified_at: u64,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Bookmark {
name: String,
url: String,
favicon_url: String,
tags: Vec<String>,
keyword: String,
created_at: u64,
modified_at: u64,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
enum Entry {
Directory(Directory),
Bookmark(Bookmark),
}
If you really don't want the extra level and the tag, then you can use the serde(untagged) annotation to Entry.
#[derive(Deserialize, Serialize, Debug)]
#[serde(untagged)]
enum Entry {
Directory(Directory),
Bookmark(Bookmark),
}
If you need a bit more flexibility you can create an intermediate struct BookmarkOrDirectory that contains all the fields of both, with the fields that only occur in one as Option and then implement TryFrom<BookmarkOrDirectory> for Entry and use serde(try_from=...) and serde(into=...) to convert to/from the appropriate form. An example implementation is below. It compiles, but has a few todo! scattered in it, and uses String as an error type, which is hacky - and is of course untested.
use core::convert::TryFrom;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct Root {
children: Vec<Entry>,
}
#[derive(Clone)]
struct Directory {
name: String,
created_at: u64,
children: Vec<Entry>,
modified_at: u64,
}
#[derive(Clone)]
struct Bookmark {
name: String,
url: String,
favicon_url: String,
tags: Vec<String>,
keyword: String,
created_at: u64,
modified_at: u64,
}
#[derive(Serialize, Deserialize, Clone)]
#[serde(try_from = "BookmarkOrDirectory", into = "BookmarkOrDirectory")]
enum Entry {
Directory(Directory),
Bookmark(Bookmark),
}
#[derive(Serialize, Deserialize)]
struct BookmarkOrDirectory {
name: String,
url: Option<String>,
favicon_url: Option<String>,
tags: Option<Vec<String>>,
keyword: Option<String>,
created_at: u64,
modified_at: u64,
children: Option<Vec<Entry>>,
}
impl BookmarkOrDirectory {
pub fn to_directory(self) -> Result<Directory, (Self, String)> {
// Check all the fields are there
if !self.children.is_some() {
return Err((self, "children is not set".to_string()));
}
// TODO: Check extra fields are not there
Ok(Directory {
name: self.name,
created_at: self.created_at,
children: self.children.unwrap(),
modified_at: self.modified_at,
})
}
pub fn to_bookmark(self) -> Result<Bookmark, (Self, String)> {
todo!()
}
}
impl TryFrom<BookmarkOrDirectory> for Entry {
type Error = String;
fn try_from(v: BookmarkOrDirectory) -> Result<Self, String> {
// Try to parse it as direcory
match v.to_directory() {
Ok(directory) => Ok(Entry::Directory(directory)),
Err((v, mesg1)) => {
// if that fails try to parse it as bookmark
match v.to_bookmark() {
Ok(bookmark) => Ok(Entry::Bookmark(bookmark)),
Err((_v, mesg2)) => Err(format!("unable to convert to entry - not a bookmark since '{}', not a directory since '{}'", mesg2, mesg1))
}
}
}
}
}
impl Into<BookmarkOrDirectory> for Bookmark {
fn into(self) -> BookmarkOrDirectory {
todo!()
}
}
impl Into<BookmarkOrDirectory> for Directory {
fn into(self) -> BookmarkOrDirectory {
todo!()
}
}
impl Into<BookmarkOrDirectory> for Entry {
fn into(self) -> BookmarkOrDirectory {
match self {
Entry::Bookmark(bookmark) => bookmark.into(),
Entry::Directory(directory) => directory.into(),
}
}
}

Rust - value of type cannot be built from `std::iter::Iterator<>`

I have struct from which I want to extract the data.
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunResult {
pub runs: Vec<RunDetails>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunDetails {
pub details: String,
pub name: String,
pub id: String,
pub type: String,
}
I am getting the data which got inserted in above struct in the variable result which I am iterating below
let my_result:RunResult = result
.runs
.into_iter()
.filter(|line| line.details.contains(&"foo"))
.collect();
I am getting the error as
value of type `RunResult` cannot be built from `std::iter::Iterator<Item=RunDetails>`
I want to get the record where type=OK, details contains foo with highest id.
How can I achieve this?
Check out the documentation for collect here.
Your struct RunResult should implement FromIterator<RunDetails>.
Try adding this (untested by me):
impl FromIterator<RunDetails> for RunResult {
fn from_iter<I: IntoIterator<Item=RunDetails>>(iter: I) -> Self {
Self {
runs: Vec::from_iter(iter);
}
}
}

How do I change Serde's default implementation to return an empty object instead of null?

I'm developing an API wrapper and I'm having some troubles with the deserialization of an empty JSON object.
The API returns this JSON object. Mind the empty object at entities:
{
"object": "page",
"entry": [
{
"id": "1158266974317788",
"messaging": [
{
"sender": {
"id": "some_id"
},
"recipient": {
"id": "some_id"
},
"message": {
"mid": "mid.$cAARHhbMo8SBllWARvlfZBrJc3wnP",
"seq": 5728,
"text": "test",
"nlp": {
"entities": {} // <-- here
}
}
}
]
}
]
}
This is my equivalent struct of the message property (edited):
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TextMessage {
pub mid: String,
pub seq: u64,
pub text: String,
pub nlp: NLP,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NLP {
pub entities: Intents,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Intents {
intent: Option<Vec<Intent>>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Intent {
confidence: f64,
value: String,
}
Serde's default is to deserialize Options, which are None, with ::serde_json::Value::Null.
I solved this problem differently with no need to change the default implementation. I used serde's field attributes to skip the intent property when the Option is None. Because there is only one property in the struct Intents, this will create an empty object.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TextMessage {
pub mid: String,
pub seq: u64,
pub text: String,
pub nlp: NLP,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct NLP {
pub entities: Intents,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Intents {
#[serde(skip_serializing_if="Option::is_none")]
intent: Option<Vec<Intent>>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Intent {
confidence: f64,
value: String,
}

Resources