Conditionally decoding field-based JSON in JSON

I get JSON from the API, and the response can be one of 30 types. Each type has a unique set of fields, but all answers have a type field, which indicates what type it is.

My approach is to use serde . I create a structure for each type of response and make them decodeable. Once I understood how to choose which structure should be used for the message just received?

At the moment, I have created another TypeStruct structure with a single field for type . I decode the JSON in TypeStruct and then select the appropriate structure for the received message based on the type value and decodes the message again.

I would like to get rid of this duplication of decoding.

+5
source share
1 answer

You can use existing enumeration deserialization. I will give a step-by-step example to deserialize your format with the following enumeration:

 #[derive(Debug, PartialEq, Eq, Deserialize)] enum MyType { A {gar: ()}, B {test: i32}, C {blub: String}, } 
  • Start with an example json line:

     let json = r#"{"type": "B", "test": 42}"#; 
  • turn it into a Value enum

     let mut json: serde_json::Value = serde_json::from_str(json).unwrap(); 
  • Extract the type field

     let type_ = { let obj = json.as_object_mut().expect("object"); let type_ = obj.remove("type").expect("`type` field"); if let serde_json::Value::String(s) = type_ { s } else { panic!("type field not a string"); } }; 
  • Create the "correct" enum json. A single field structure where the field name is an enumeration option and the field value is a variant value

     let mut enum_obj = std::collections::BTreeMap::new(); enum_obj.insert(type_, json); let json = serde_json::Value::Object(enum_obj); 
  • Use the generated json deserializer to turn json into the value of your enum

     let obj: MyType = serde_json::from_value(json).unwrap(); 
+6
source

Source: https://habr.com/ru/post/1234448/


All Articles