How to keep order after using serde_json::from_str to deserialize a string to struct?

This is my struct

#[derive(Serialize, Deserialize, Debug)]
pub struct JsonSchema {
  pub title: Option<String>,
  #[serde(rename(deserialize = "type"))]
  pub json_type: Option<String>,
  pub properties: Option<HashMap<String, JsonSchema>>,
  pub items: Option<Box<JsonSchema>>,
  #[serde(rename(deserialize = "enum"))]
  pub enum_vals: Option<Vec<EnumType>>,
  pub description: Option<String>,
}

This is my parsing code

pub fn parse_json(schema: &str) -> Option<JsonSchema> {
  match serde_json::from_str(schema) {
    Ok(parsed) => Some(parsed),
    Err(e) => {
      eprintln!("{}", e);
      None
    }
  }
}

After parsing json string, it can't keep the order, and I have added "preserve_order" to the features.

This is my json schema:

{
  "title": "Schema",
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string"
    },
    "lastName": {
      "type": "string"
    },
    "age": {
      "type": "number"
    },
    "hairColor": {
      "enum": [
        {
          "title": "hair color1",
          "value": "color1"
        },
        {
          "title": "hair color2",
          "value": "color2"
        },
        {
          "title": "hair color3",
          "value": "color3"
        }
      ],
      "type": "string"
    },
    "obj": {
      "type": "object",
      "properties": {
        "key1": {
          "type": "string"
        },
        "key2": {
          "type": "number"
        },
        "key3": {
          "type": "boolean"
        }
      }
    },
    "arr": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "arr1": {
            "type": "string"
          },
          "arr2": {
            "type": "number"
          },
          "arr3": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "enen1": {
                  "type": "string"
                },
                "enen2": {
                  "type": "number"
                },
                "enen3": {
                  "type": "boolean"
                }
              }
            }
          }
        }
      }
    }
  }
}

After parsing:

JsonSchema {
    title: Some(
        "Schema",
    ),
    json_type: Some(
        "object",
    ),
    properties: Some(
        {
            "lastName": JsonSchema {
                title: None,
                json_type: Some(
                    "string",
                ),
                properties: None,
                items: None,
                enum_vals: None,
                description: None,
            },
            "age": JsonSchema {
                title: None,
                json_type: Some(
                    "number",
                ),
                properties: None,
                items: None,
                enum_vals: None,
                description: None,
            },
            "obj": JsonSchema {
                title: None,
                json_type: Some(
                    "object",
                ),
                properties: Some(
                    {
                        "key3": JsonSchema {
                            title: None,
                            json_type: Some(
                                "boolean",
                            ),
                            properties: None,
                            items: None,
                            enum_vals: None,
                            description: None,
                        },
                        "key1": JsonSchema {
                            title: None,
                            json_type: Some(
                                "string",
                            ),
                            properties: None,
                            items: None,
                            enum_vals: None,
                            description: None,
                        },
                        "key2": JsonSchema {
                            title: None,
                            json_type: Some(
                                "number",
                            ),
                            properties: None,
                            items: None,
                            enum_vals: None,
                            description: None,
                        },
                    },
                ),
                items: None,
                enum_vals: None,
                description: None,
            },
            "hairColor": JsonSchema {
                title: None,
                json_type: Some(
                    "string",
                ),
                properties: None,
                items: None,
                enum_vals: Some(
                    [
                        EnumType {
                            title: Some(
                                "hair color1",
                            ),
                            value: Some(
                                "color1",
                            ),
                        },
                        EnumType {
                            title: Some(
                                "hair color2",
                            ),
                            value: Some(
                                "color2",
                            ),
                        },
                        EnumType {
                            title: Some(
                                "hair color3",
                            ),
                            value: Some(
                                "color3",
                            ),
                        },
                    ],
                ),
                description: None,
            },
            "arr": JsonSchema {
                title: None,
                json_type: Some(
                    "array",
                ),
                properties: None,
                items: Some(
                    JsonSchema {
                        title: None,
                        json_type: Some(
                            "object",
                        ),
                        properties: Some(
                            {
                                "arr2": JsonSchema {
                                    title: None,
                                    json_type: Some(
                                        "number",
                                    ),
                                    properties: None,
                                    items: None,
                                    enum_vals: None,
                                    description: None,
                                },
                                "arr1": JsonSchema {
                                    title: None,
                                    json_type: Some(
                                        "string",
                                    ),
                                    properties: None,
                                    items: None,
                                    enum_vals: None,
                                    description: None,
                                },
                                "arr3": JsonSchema {
                                    title: None,
                                    json_type: Some(
                                        "array",
                                    ),
                                    properties: None,
                                    items: Some(
                                        JsonSchema {
                                            title: None,
                                            json_type: Some(
                                                "object",
                                            ),
                                            properties: Some(
                                                {
                                                    "enen1": JsonSchema {
                                                        title: None,
                                                        json_type: Some(
                                                            "string",
                                                        ),
                                                        properties: None,
                                                        items: None,
                                                        enum_vals: None,
                                                        description: None,
                                                    },
                                                    "enen3": JsonSchema {
                                                        title: None,
                                                        json_type: Some(
                                                            "boolean",
                                                        ),
                                                        properties: None,
                                                        items: None,
                                                        enum_vals: None,
                                                        description: None,
                                                    },
                                                    "enen2": JsonSchema {
                                                        title: None,
                                                        json_type: Some(
                                                            "number",
                                                        ),
                                                        properties: None,
                                                        items: None,
                                                        enum_vals: None,
                                                        description: None,
                                                    },
                                                },
                                            ),
                                            items: None,
                                            enum_vals: None,
                                            description: None,
                                        },
                                    ),
                                    enum_vals: None,
                                    description: None,
                                },
                            },
                        ),
                        items: None,
                        enum_vals: None,
                        description: None,
                    },
                ),
                enum_vals: None,
                description: None,
            },
            "firstName": JsonSchema {
                title: None,
                json_type: Some(
                    "string",
                ),
                properties: None,
                items: None,
                enum_vals: None,
                description: None,
            },
        },
    ),
    items: None,
    enum_vals: None,

The order is not match to the original json after parsing, how should I do?

stdlib‘s HashMap doesn’t keep insertion order.
On top of using the preserve_order feature of serde_json you also need to use its serde_json::Map type to get a map that keeps insertion order.

1 Like

When I try to change the HashMap to serde_json::Map, I occur this error, I don't know how to do... I am noob

To preserve order you have to use something like indexmap, which is what serde_json uses for its Map type when you enable the preserve_order feature (cf the doc for object). In your own code you have to use IndexMap.

NB: remember to enable the serde feature of indexmap

2 Likes

Thank you so much, this solve my problem :grin:

indexmap = {version = "2.0.0", features = ["serde"]}
1 Like

World is not black and white. We have bunch of JSON files in our repository which describe various things.

They are proper JSON in a sense that tools that process these don't care about order.

Nonetheless tools that change these files are required to preserve order, because doing things differently would turn review of changes into nightmare.

That's not uncommon situation in my experience.

2 Likes