【问题标题】:Conditionally decoding JSON based on a field in the JSON根据 JSON 中的字段有条件地解码 JSON
【发布时间】:2015-10-24 22:10:07
【问题描述】:

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

我的方法是使用serde。我为每种响应类型创建一个结构并使它们可解码。一旦我有了这个,我该如何选择应该为新收到的消息使用哪个结构?

目前,我创建了另一个结构 TypeStruct,其中只有一个字段用于 type。我将 JSON 解码为 TypeStruct,然后根据类型值为接收到的消息选择适当的结构,然后再次解码消息。

我想摆脱这种解码重复。

【问题讨论】:

  • 您当前的解决方案是我会做的。您认为如何避免“解码重复”会更好或更有效?
  • 我来自 python,所以我对静态类型的语言感到不舒服。但如果你说我的方法没问题,我会坚持下去。
  • 只是我的看法,但这听起来像是一个过早担心优化的好例子。让你的代码运行,看看你是否认为它足够快。如果没有,请在更改代码之前对其进行分析——也许 JSON 解码甚至不是问题。

标签: json rust serde


【解决方案1】:

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

#[derive(Debug, PartialEq, Eq, Deserialize)]
enum MyType {
    A {gar: ()},
    B {test: i32},
    C {blub: String},
}
  1. 从一个示例 json 字符串开始:

    let json = r#"{"type": "B", "test": 42}"#;
    
  2. 把它变成Value枚举

    let mut json: serde_json::Value = serde_json::from_str(json).unwrap();
    
  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");
        }
    };
    
  4. 创建“正确的”枚举 json。具有单个字段的结构,其中字段的名称是枚举变量,字段的值是变量值

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

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

【讨论】:

  • 与问题中概述的“解码两次”解决方案相比,有任何数字或直觉吗?
  • 它更像是一个 DRY 解决方案。这样您就不需要在两个位置处理任何类型。您只需要声明枚举。永远不要匹配所有可能的类型或任何东西。 Speedwise 这可能比问题中提供的解决方案更糟糕(除非有很多 String 字段并且没有结构/枚举字段,那么它可能比问题中的解决方案更快)。没有看到确切的代码很难知道。更快的通用解决方案需要准确了解 json 字符串中的字段顺序和/或 serde 上的 hack
  • 谢谢,我怀疑,像你建议的那样是可能的。我可以对 rust-serialize 使用相同的技术吗?
  • 我相信这也有效,是的。它还有一个用于原始 json 的 Json 枚举,并且它具有用于枚举的特殊格式(很可能与 serde 相同?)。所以我上面的代码到rustc-serialize的转换应该是直截了当的
  • 不幸的是,rustc-serialize 无法做到这一点。 stackoverflow.com/questions/25604339/… 所以这个例子 (is.gd/XB1TiG) 产生 {"variant":"Message","fields":["123","superuser","some text","beginning of epoch"]}
猜你喜欢
  • 2018-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-18
  • 2021-05-20
  • 2019-11-04
  • 2017-04-13
相关资源
最近更新 更多