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)
}
Related
Here is my setup
#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize)]
pub struct Foo {}
#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize)]
pub struct Bar {}
pub trait MyTrait {
type CreateMsg;
type DeleteMsg;
}
impl MyTrait for Foo {
type CreateMsg = foo::CreateMsg;
type DeleteMsg = foo::DeleteMsg;
}
impl MyTrait for Bar {
type CreateMsg = bar::CreateMsg;
type DeleteMsg = bar::DeleteMsg;
}
pub fn contains_create_msg(my_trait: impl MyTrait, string: String) -> bool {
my_trait::CreateMsg::contains(string)
}
lazy_static! {
pub static ref MY_TRAIT_OBJECTS:[Box<dyn ContractTrait + Sync>; 2] = [
Box::new(Foo::default()),
Box::new(Bar::default()),
];
}
In foo.rs:
#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize)]
pub enum CreateMsg {
CreateThis { a_field: String },
CreateThat { a_field: String }
}
#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize)]
pub enum DeleteMsg {
DeleteThis { a_field: String },
DeleteThat { a_field: String }
}
In bar.rs:
#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize)]
pub enum CreateMsg {
CreateOne { a_different_field: String },
CreateTwo { a_different_field: String }
}
#[derive(Copy, Clone, Debug, Default, Deserialize, Serialize)]
pub enum DeleteMsg {
DeleteOne { a_different_field: String },
DeleteTwo { a_different_field: String }
}
I get an error, "the value of the associated types must be specified". How can I pass a list of MyTrait objects to other places in my program? My intention is to iterate of these My Traits, and perform different functions using the types that they are associated to. Here is an example of something I would like to do:
let msg = "hello".to_string();
for object in MY_TRAIT_OBJECTS {
println!("{}", object.contains_create_msg(msg.clone()));
}
Sorry if there are syntax errors in there, wrote without an editor
In my rust project, I'm trying to make a db like implementation that can store the state of a user from different integrations that all implement same functions through a trait, and can be written to a file later using serde with this code:
#[derive(Serialize, Deserialize, Debug)]
pub struct State {
pub current: HashMap<Bytes, Box<dyn UserState>>
pub changes: HashMap<u64, Vec<UserStateChange>>
}
pub trait UserState {
fn id(&self) -> Bytes;
fn apply_change(&mut self, change: UserStateChange);
}
#[derive(Serialize, Deserialize, Debug)]
pub enum UserStateChange {
Foo(FooChange),
Bar(BarChange)
}
#[derive(Serialize, Deserialize, Debug)]
pub struct FooState {
id: Bytes,
count: u64,
start: u64
}
#[derive(Serialize, Deserialize, Debug)]
pub struct FooChange {
count_delta: i64
}
impl UserState for FooState {
fn id(&self) -> Bytes {self.id.clone()}
fb apply_change(&mut self, change: UserStateChange) {
let UserStateChange::Foo(change) = change;
todo!();
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct BarState {
id: Bytes,
attempts: u64
}
#[derive(Serialize, Deserialize, Debug)]
pub struct BarChange {
attempt_delta: i64
}
impl UserState for BarState {
fn id(&self) -> Bytes {self.id.clone()}
fb apply_change(&mut self, change: UserStateChange) {
let UserStateChange::Bar(change) = change;
// apply logic
}
}
How can I make it so that it can store and serialize the dynamic trait, while ensuring that it supports the apply_change function, and can use its own structure for storing changes with relevant fields?
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>
}
I have the JSON roughly like this:
[
{
"commonA": 1,
"commonB": 2,
"type": "Foo",
"fooSpecificA": 3,
"fooSpecificB": 4
},
{
"commonA": 5,
"commonB": 6,
"type": "Bar",
"barSpecificA": 7,
"barSpecificB": 8
},
...
In other words I have internally tagged objects, but some of the fields are common to every type. I'd like to deserialise it to something like this:
struct Entry {
commonA: i64,
commonB: i64,
variant: EntryVariant,
}
enum EntryVariant {
Foo(FooSpecific),
Bar(BarSpecific),
}
struct FooSpecific {
fooSpecificA: i64,
fooSpecificB: i64,
}
struct BarSpecific {
barSpecificA: i64,
barSpecificB: i64,
}
Is that possible with Serde?
Combine internally tagged unions with struct flattening.
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
struct Entry {
#[serde(rename = "commonA")]
common_a: i64,
#[serde(rename = "commonB")]
common_b: i64,
#[serde(flatten)]
variant: EntryVariant,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
enum EntryVariant {
Foo(FooSpecific),
Bar(BarSpecific),
}
#[derive(Debug, Serialize, Deserialize)]
struct FooSpecific {
#[serde(rename = "fooSpecificA")]
foo_specific_a: i64,
#[serde(rename = "fooSpecificB")]
foo_specific_b: i64,
}
#[derive(Debug, Serialize, Deserialize)]
struct BarSpecific {
#[serde(rename = "barSpecificA")]
bar_specific_a: i64,
#[serde(rename = "barSpecificB")]
bar_specific_b: i64,
}
Playground
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,
}