有条件地根据JSON中的字段解码JSON

eye*_*ick 6 json rust serde

我从API接收JSON,响应可以是30种类型之一.每种类型都有一组唯一的字段,但所有响应都有一个字段type,说明它是哪种类型.

我的方法是使用serde.我为每个响应类型创建一个结构,并使它们可解码.一旦我知道如何选择应该用于新收到的消息的结构?

目前,我已经创建了另一个TypeStruct只有一个字段的结构type.我将JSON解码为a TypeStruct,然后根据类型值为接收的消息选择适当的结构,并再次解码消息.

我想摆脱这种解码重复.

oli*_*obk 8

您可以使用现有的枚举反序列化.我将给出一个分步示例,将您的格式反序列化为以下枚举:

#[derive(Debug, PartialEq, Eq, Deserialize)]
enum MyType {
    A {gar: ()},
    B {test: i32},
    C {blub: String},
}
Run Code Online (Sandbox Code Playgroud)
  1. 以示例json字符串开头:

    let json = r#"{"type": "B", "test": 42}"#;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 把它变成Value枚举

    let mut json: serde_json::Value = serde_json::from_str(json).unwrap();
    
    Run Code Online (Sandbox Code Playgroud)
  3. 撕掉type场地

    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");
        }
    };
    
    Run Code Online (Sandbox Code Playgroud)
  4. 创建"适当的"枚举json.具有单个字段的结构,其中字段的名称是枚举变体,字段的值是变量值

    let mut enum_obj = std::collections::BTreeMap::new();
    enum_obj.insert(type_, json);
    let json = serde_json::Value::Object(enum_obj);
    
    Run Code Online (Sandbox Code Playgroud)
  5. 使用生成的json反序列化器将json转换为枚举值

    let obj: MyType = serde_json::from_value(json).unwrap();
    
    Run Code Online (Sandbox Code Playgroud)

  • 它更像是一个干燥的解决方案.这样您就不需要在两个位置处理任何类型.你只需要声明枚举.永远不要匹配所有可能的类型或任何东西.Speedwise这可能比问题中提供的解决方案更糟糕(除非有很多`String`字段且没有struct/enum字段,那么它可能比问题中的解决方案更快).没有看到确切的代码就很难知道.更快的一般解决方案需要精确了解json字符串中的字段顺序和/或serde上的hack (3认同)