【问题标题】:How to "deserialize with" for a container using serde in Rust如何在 Rust 中使用 serde 为容器“反序列化”
【发布时间】:2019-02-19 08:30:13
【问题描述】:

MVCE:

use std::collections::HashMap;
use std::fmt;
use std::marker::PhantomData;
use std::str::FromStr;

use serde; // 1.0.85
use serde::de::{self, MapAccess, Visitor}; // 1.0.85
use serde_derive::Deserialize; // 1.0.85
use toml; // 0.4.10
use void::Void; // 1.0.2

// See: https://serde.rs/string-or-struct.html
fn string_or_struct<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
    T: serde::Deserialize<'de> + FromStr<Err = Void>,
    D: serde::Deserializer<'de>,
{
    // This is a Visitor that forwards string types to T's `FromStr` impl and
    // forwards map types to T's `Deserialize` impl. The `PhantomData` is to
    // keep the compiler from complaining about T being an unused generic type
    // parameter. We need T in order to know the Value type for the Visitor
    // impl.
    struct StringOrStruct<T>(PhantomData<fn() -> T>);

    impl<'de, T> Visitor<'de> for StringOrStruct<T>
    where
        T: serde::Deserialize<'de> + FromStr<Err = Void>,
    {
        type Value = T;

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

        fn visit_str<E>(self, value: &str) -> Result<T, E>
        where
            E: de::Error,
        {
            Ok(FromStr::from_str(value).unwrap())
        }

        fn visit_map<M>(self, visitor: M) -> Result<T, M::Error>
        where
            M: MapAccess<'de>,
        {
            // `MapAccessDeserializer` is a wrapper that turns a `MapAccess`
            // into a `Deserializer`, allowing it to be used as the input to T's
            // `Deserialize` implementation. T then deserializes itself using
            // the entries from the map visitor.
            serde::Deserialize::deserialize(de::value::MapAccessDeserializer::new(visitor))
        }
    }

    deserializer.deserialize_any(StringOrStruct(PhantomData))
}

impl FromStr for Obj {
    type Err = Void;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Obj {
            x: 0,
            y: s.to_owned(),
        })
    }
}

// ----------------------------------------------------------------------------

#[derive(Debug, Deserialize)]
struct Obj {
    x: isize,
    y: String,
}

#[derive(Debug, Deserialize)]
struct Simple {
    #[serde(deserialize_with = "string_or_struct")]
    obj: Obj,
}

#[derive(Debug, Deserialize)]
struct InsideHashMap {
    objs: HashMap<String, Obj>,
}

fn main() {
    // Basic deserialization of Obj
    let toml = r#"
        x = 5
        y = "hello"
    "#;
    let obj: Obj = toml::from_str(toml).unwrap();
    println!("{:?}", obj);

    // Basic deserialization of Obj as a field in a struct
    let toml = r#"
        [obj]
        x = 5
        y = "hello"
    "#;
    let simple: Simple = toml::from_str(toml).unwrap();
    println!("{:?}", simple);

    // Basic deserialization of Obj as a field in a struct as a string or struct
    let toml = r#"
        obj = "hello"
    "#;
    let simple: Simple = toml::from_str(toml).unwrap();
    println!("{:?}", simple);

    // Deserialization of an Obj inside a HashMap
    let toml = r#"
        [objs]
        a = { x = 5, y = "hello" }
    "#;
    let working: InsideHashMap = toml::from_str(toml).unwrap();
    println!("{:?}", working);

    // Deserialization of Obj inside a HashMap field as a string or struct
    let toml = r#"
        [objs]
        a = "hello"
    "#;
    let not_working: InsideHashMap = toml::from_str(toml).unwrap();
    println!("{:?}", not_working);
}

我想使用 serde 反序列化 TOML 格式,其中结构可以指定为字符串或普通结构规范

a = "a string"
b = { x = 5, y = "another string" }

在这个例子中,我最终会得到一个看起来像这样的 HashMap

{
   "a": Obj { x: 0, y: "a string" },
   "b": Obj { x: 5, y: "another string" }
}

我已阅读https://serde.rs/string-or-struct.html,了解如何在结构字段上使用“deserialize_with”属性。但是当结构位于像 HashMap 这样的容器中时,我该怎么做呢?

#[derive(Debug, Deserialize)]
struct Obj {
    x: isize,
    y: String
}

#[derive(Debug, Deserialize)]
struct Simple {
    #[serde(deserialize_with = "string_or_struct")]
    obj: Obj
}

#[derive(Debug, Deserialize)]
struct InsideHashMap { 
    objs: HashMap<String, Obj> // <-- how can I use "deserialize_with" on Obj here
}

【问题讨论】:

    标签: rust deserialization serde toml


    【解决方案1】:

    首先,我们需要另一个结构来为我们的 HashMap 使用 deserialize_with

    #[derive(Debug, Deserialize)]
    struct Flatten {
        #[serde(deserialize_with = "string_or_struct", flatten)]
        obj: Obj,
    }
    

    所以我们可以这样写:

    #[derive(Debug, Deserialize)]
    struct InsideHashMap {
        objs: HashMap<String, Flatten>,
    }
    

    这应该可以,但不是因为(我还不知道为什么,看起来像 flatten 和 deserialize_with 不能一起工作,它似乎没有使用 deserialize_with 实现)

    所以,我们必须使用hard方式,让我们实现它:

    use std::collections::HashMap;
    use std::fmt;
    use std::str::FromStr;
    
    use serde; // 1.0.85
    use serde::de::{self, Deserialize, MapAccess, Visitor}; // 1.0.85
    use serde::Deserializer;
    use serde_derive::Deserialize; // 1.0.85
    use toml; // 0.4.10
    use void::Void; // 1.0.2
    
    #[derive(Debug)]
    struct Obj {
        x: isize,
        y: String,
    }
    
    struct ObjVisitor;
    
    // OjbAux is here to avoid implement the deserialiser of the map by hand we can't use
    // Obj cause it will cause infinite recursion
    #[derive(Debug, Deserialize)]
    struct ObjAux {
        x: isize,
        y: String,
    }
    
    impl<'de> Visitor<'de> for ObjVisitor {
        type Value = Obj;
    
        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("string or map")
        }
    
        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            Ok(FromStr::from_str(value).unwrap())
        }
    
        fn visit_map<M>(self, visitor: M) -> Result<Self::Value, M::Error>
        where
            M: MapAccess<'de>,
        {
            let aux: ObjAux = Deserialize::deserialize(de::value::MapAccessDeserializer::new(visitor))?;
            Ok(Obj { x: aux.x, y: aux.y })
        }
    }
    
    impl<'de> Deserialize<'de> for Obj {
        fn deserialize<D>(deserializer: D) -> Result<Obj, D::Error>
        where
            D: Deserializer<'de>,
        {
            deserializer.deserialize_any(ObjVisitor)
        }
    }
    
    impl FromStr for Obj {
        type Err = Void;
    
        fn from_str(s: &str) -> Result<Self, Self::Err> {
            Ok(Obj {
                x: 0,
                y: s.to_owned(),
            })
        }
    }
    
    #[derive(Debug, Deserialize)]
    struct Simple {
        obj: Obj,
    }
    
    #[derive(Debug, Deserialize)]
    struct InsideHashMap {
        objs: HashMap<String, Obj>,
    }
    
    fn main() {
        // Basic deserialization of Obj
        let toml = r#"
            x = 5
            y = "hello"
        "#;
        let obj: Obj = toml::from_str(toml).unwrap();
        println!("{:?}", obj);
    
        // Basic deserialization of Obj as a field in a struct
        let toml = r#"
            [obj]
            x = 5
            y = "hello"
        "#;
        let simple: Simple = toml::from_str(toml).unwrap();
        println!("{:?}", simple);
    
        // Basic deserialization of Obj as a field in a struct as a string or struct
        let toml = r#"
            obj = "hello"
        "#;
        let simple: Simple = toml::from_str(toml).unwrap();
        println!("{:?}", simple);
    
        // Deserialization of an Obj inside a HashMap
        let toml = r#"
            [objs]
            a = { x = 5, y = "hello" }
        "#;
        let working: InsideHashMap = toml::from_str(toml).unwrap();
        println!("{:?}", working);
    
        // Deserialization of Obj inside a HashMap field as a string or struct
        let toml = r#"
            [objs]
            a = "hello"
        "#;
        let not_working: InsideHashMap = toml::from_str(toml).unwrap();
        println!("{:?}", not_working);
    }
    

    按预期工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多