1

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"
Ken White
  • 123,280
  • 14
  • 225
  • 444
user3054986
  • 417
  • 7
  • 18
  • Is the format of the YAML fixed or are you defining it? The `StateMachine:` in yaml does not seem to achieve what you want it to in the latter case. Your Rust code assumes "`scjson` is a list of `{id: foo}` objects", but your YAML represents "`scjson` is a list of `{StateMachine: {id: foo}}`" objects. – justinas Dec 21 '22 at 13:59
  • @justinas I am defining it. Just switched it to json equivalent. – user3054986 Dec 21 '22 at 14:09
  • 1
    What is the purpose of the `StateMachine` key? The equivalent of the structure you defined in Rust would be `{"scjson": [{"id": "sm_1"}, {"id": "sm_2"}]}` – justinas Dec 21 '22 at 14:18
  • @justinas, ok I am confused. The idea is parse the json - and I am not capable of defining the equivalent Rust code. – user3054986 Dec 21 '22 at 14:22
  • I am trying to learn more about the structure of the JSON you're trying to parse. Can the list elements have some other types, e.g. `{"scjson": [{"StateMachine": {"id : "sm_1"}}, {"AnotherObject": {"foo": "bar"}}]}`? Depending on whether that's the case, I might recommend different solutions. – justinas Dec 21 '22 at 14:24
  • In this case, the list elements will all be StateMachine - but the next case, will have a list with different elements – user3054986 Dec 21 '22 at 14:26

1 Answers1

2

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)
}
justinas
  • 6,287
  • 3
  • 26
  • 36
  • just to be sure: this has nothing to do with yaml != json? And why do I need the indirection? – user3054986 Dec 21 '22 at 14:33
  • No, both YAML and JSON examples are equivalent. Please try to read my comments and answer again: each element of the `scjson` list is of form `{"StateMachine": {"id": "some_value"}}`. Your initial code assumes that the form of these list elements is simply `{"id": "some_value"}` – justinas Dec 21 '22 at 14:36
  • The initial code was wrong. I want to be explicit, hence the word StateMachine in the yaml file. – user3054986 Dec 21 '22 at 14:39
  • That's why I initially asked whether you are designing your own JSON file, or have to parse something that you can not change the structure of. JSON/YAML does not really do "type information", but if you want to be explicit, and also support e.g. several different "types" of elements in a single list, the enum option is the best - it is exactly what you need to then implement `AtomicState`, `ParallelState`, etc. – justinas Dec 21 '22 at 14:41