如何对JSON对象进行密钥不可知的反序列化?

时间:2017-12-14 20:04:44

标签: json rust serde

我正在使用一种不太理想的API,它不遵循任何严格的标准来发送数据。每个有效负载在JSON之前都附带一些有效负载信息,然后是实际数据,其中可以是单个字符串或多个字段。

现在看来,如果我将每个不同的有效负载映射到一个结构,我最终会得到大约50个结构。我觉得这并不理想,因为除了关键之外,大量的这些结构都是重叠的。例如,我相信有6种不同版本的有效载荷可以映射到类似下面的内容,但它们都有不同的密钥。

我有两个JSON示例:

{"key": "string"}
{"key2": "string"}

我想将两者都序列化为这个结构:

#[derive(Debug, Deserialize)]
struct SimpleString {
    key: String,
}

对于两个字符串也可以这样说,甚至可以说三个字符串。有效载荷在很小的方面令人沮丧地独特,因此我目前的解决方案是在函数内部定义结构,对其进行反序列化,然后将数据传递到任何需要的位置(在我的情况下是缓存和事件处理程序)

有没有更好的方法来表示没有这么多重复?我已经尝试过寻找关键无关反序列化的东西,但我还没有找到任何东西。

1 个答案:

答案 0 :(得分:1)

您可以为您的类型实施Deserialize来解码"地图"并忽略密钥名称:

extern crate serde;
extern crate serde_json;

use std::fmt;
use serde::de::{Deserialize, Deserializer, Error, MapAccess, Visitor};

fn main() {
    let a = r#"{"key": "string"}"#;
    let b = r#"{"key2": "string"}"#;

    let a: SimpleString = serde_json::from_str(a).unwrap();
    let b: SimpleString = serde_json::from_str(b).unwrap();

    assert_eq!(a, b);
}

#[derive(Debug, PartialEq)]
struct SimpleString {
    key: String,
}

struct SimpleStringVisitor;

impl<'de> Visitor<'de> for SimpleStringVisitor {
    type Value = SimpleString;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("an object with a single string value of any key name")
    }

    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        if let Some((_, key)) = access.next_entry::<String, _>()? {
            if access.next_entry::<String, String>()?.is_some() {
                Err(M::Error::custom("too many values"))
            } else {
                Ok(SimpleString { key })
            }
        } else {
            Err(M::Error::custom("not enough values"))
        }
    }
}

impl<'de> Deserialize<'de> for SimpleString {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(SimpleStringVisitor)
    }
}