简体   繁体   English

Rust 自定义反序列化实现

[英]Rust custom deserialize implementation

I'm having a hard time understanding how to implement deserialize for a custom mapping using Rust's serde.我很难理解如何使用 Rust 的 serde 为自定义映射实现反序列化。 I'd be glad if someone could help me with this example:如果有人可以帮助我解决这个例子,我会很高兴:

I've got the following struct:我有以下结构:

#[derive(Debug, Clone, PartialEq)]
pub struct ConnectorTopics {
    pub name: String,
    pub topics: Vec<String>,
}

And the JSON data comes in the following format: JSON 数据格式如下:

{
  "test-name": {
    "topics": [
      "topic1",
      "topic2"
    ]
  }
}

As you can see, name field is a wrapper for the topics, so in my case this should deserialize to:如您所见, name字段是主题的包装,因此在我的情况下,这应该反序列化为:

let _ = ConnectorTopics {
    name: "test-name".into(),
    topics: vec!["topic1".into(), "topic2".into()]
}

My first attempt was to use a custom structure inside Deserialize implementation, however, that wouldn't compile and doesn't seem to be the correct approach.我的第一次尝试是在 Deserialize 实现中使用自定义结构,但是,这不会编译并且似乎不是正确的方法。

impl<'de> Deserialize<'de> for ConnectorTopics {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        #[derive(Debug, Deserialize)]
        struct Inner {
            topics: Vec<String>,
        }

        let a = deserializer.deserialize_map(HashMap<String, Inner>).unwrap();

        

        let value = Deserialize::deserialize::<HashMap<String, Inner>>(deserializer)?;

        let (connector, inner) = value.iter().nth(0).ok_or("invalid")?.0;

        Ok(ConnectorTopics {
            name: connector,
            topics: vec![],
        })
    }
}

What you was doing is the correct approach but your json is quite odd:您所做的是正确的方法,但您的 json 很奇怪:

use serde::de;
use serde::Deserialize;
use std::fmt;

#[derive(Debug, Clone, PartialEq)]
pub struct ConnectorTopics {
    pub name: String,
    pub topics: Vec<String>,
}

#[derive(Debug, Deserialize)]
struct Inner {
    topics: Vec<String>,
}

impl<'de> de::Deserialize<'de> for ConnectorTopics {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        struct ConnectorTopicsVisitor;

        impl<'de> de::Visitor<'de> for ConnectorTopicsVisitor {
            type Value = ConnectorTopics;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("ConnectorTopics")
            }

            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
            where
                V: de::MapAccess<'de>,
            {
                if let Some(key) = map.next_key()? {
                    let value: Inner = map.next_value()?;
                    if let Some(_) = map.next_key::<&str>()? {
                        Err(de::Error::duplicate_field("name"))
                    } else {
                        Ok(Self::Value {
                            name: key,
                            topics: value.topics,
                        })
                    }
                } else {
                    Err(de::Error::missing_field("name"))
                }
            }
        }

        deserializer.deserialize_map(ConnectorTopicsVisitor {})
    }
}

fn main() {
    let input = r#"{
      "test-name": {
        "topics": [
          "topic1",
          "topic2"
        ]
      }
    }"#;

    let result: ConnectorTopics = serde_json::from_str(input).unwrap();

    let expected = ConnectorTopics {
        name: "test-name".into(),
        topics: vec!["topic1".into(), "topic2".into()],
    };

    assert_eq!(result, expected);
}

You can use a custom function for it:您可以使用自定义 function :

pub fn deserialize_connector_topics(data: &str) -> Result<ConnectorTopics> {
    let value: Value = serde_json::from_str(data)?;
    if let Some(object) = value.as_object() {
        let mut it = object.into_iter();
        if let Some((name, topics)) = it.next() {
            let topics: Vec<String> = serde_json::from_value(topics.get("topics").unwrap().clone())?;
            return Ok(ConnectorTopics {
                name: name.to_string(),
                topics: topics,
            });
        }
    };
    Err(Error::custom("Invalid ConnectorTopics data"))
}

Please, notice it is pretty ad-hoc and it even has some unwraps and (maybe unnecessary) clones here and there.请注意,它是非常临时的,它甚至在这里和那里都有一些展开和(可能是不必要的)克隆。 You should consider modifying it for your needs.您应该考虑根据需要对其进行修改。 But as an example, it should be enough.但作为一个例子,它应该足够了。

Playground 操场

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM