【问题标题】:How do I write a Serde Visitor to convert an array of arrays of strings to a Vec<Vec<f64>>?如何编写 Serde 访问者将字符串数组转换为 Vec<Vec<f64>>?
【发布时间】:2018-01-16 20:04:12
【问题描述】:

我需要将 JSON 反序列化为具有 Vec&lt;Vec&lt;f64&gt;&gt; 字段的结构。 JSON 包含数字字符串,因此我需要一个自定义反序列化器在反序列化过程中将字符串转换为 f64

我想要反序列化的 JSON 示例:

{
  "values": [["2", "1.4"], ["8.32", "1.5"]]
}

我的结构是这样的:

#[derive(Deserialize)]
struct Payload {
    #[serde(default, deserialize_with = "from_array_of_arrays_of_strs")]
    values: Vec<Vec<f64>>,
}

我看到您可能可以对访问者 in the examples of Serde 执行此操作,因此我实现了此访问者:

fn from_array_of_arrays_of_strs<'de, T, D>(deserializer: D) -> Result<Vec<Vec<f64>>, D::Error>
where
    T: Deserialize<'de>,
    D: Deserializer<'de>,
{
    struct F64Visitor(PhantomData<fn() -> Vec<Vec<f64>>>);

    impl<'de> Visitor<'de> for F64Visitor {
        type Value = Vec<Vec<f64>>;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a nonempty sequence of numbers")
        }

        #[inline]
        fn visit_str<E>(self, value: &str) -> Result<f64, E>
        where
            E: serde::de::Error,
        {
            self.visit_string(String::from(value))
        }

        #[inline]
        fn visit_string<E>(self, value: String) -> Result<f64, E> {
            Ok(value.parse::<f64>().unwrap())
        }

        #[inline]
        fn visit_seq<V, T>(self, mut visitor: V) -> Result<Vec<T>, V::Error>
        where
            V: SeqAccess<'de>,
        {
            let mut vec = Vec::new();

            while let Some(elem) = try!(visitor.next_element()) {
                vec.push(elem);
            }

            Ok(vec)
        }
    }

    let visitor = F64Visitor(PhantomData);
    deserializer.deserialize_seq(visitor)
}

playground

编译器抱怨visit_strvisit_string 的特征类型不兼容:

error[E0053]: method `visit_str` has an incompatible type for trait
  --> src/main.rs:32:9
   |
32 | /         fn visit_str<E>(self, value: &str) -> Result<f64, E>
33 | |             where
34 | |             E: serde::de::Error,
35 | |         {
36 | |             self.visit_string(String::from(value))
37 | |         }
   | |_________^ expected struct `std::vec::Vec`, found f64
   |
   = note: expected type `fn(from_array_of_arrays_of_strs::F64Visitor, &str) -> std::result::Result<std::vec::Vec<std::vec::Vec<f64>>, E>`
              found type `fn(from_array_of_arrays_of_strs::F64Visitor, &str) -> std::result::Result<f64, E>`

error[E0053]: method `visit_string` has an incompatible type for trait
  --> src/main.rs:40:9
   |
40 | /         fn visit_string<E>(self, value: String) -> Result<f64, E> {
41 | |             Ok(value.parse::<f64>().unwrap())
42 | |         }
   | |_________^ expected struct `std::vec::Vec`, found f64
   |
   = note: expected type `fn(from_array_of_arrays_of_strs::F64Visitor, std::string::String) -> std::result::Result<std::vec::Vec<std::vec::Vec<f64>>, E>`
              found type `fn(from_array_of_arrays_of_strs::F64Visitor, std::string::String) -> std::result::Result<f64, E>`

error[E0049]: method `visit_seq` has 2 type parameters but its trait declaration has 1 type parameter
  --> src/main.rs:45:21
   |
45 |         fn visit_seq<V, T>(self, mut visitor: V) -> Result<Vec<T>, V::Error>
   |                     ^^^^^^ found 2 type parameters, expected 1

我认为我对访问者的工作方式没有正确的理解。我可以只有一个访问者来反序列化字符串数组,还是需要一个访问者来反序列化数组,而需要一个访问者来反序列化字符串到f64

我读过:

【问题讨论】:

    标签: json serialization rust serde


    【解决方案1】:

    正如How to transform fields before deserialization using serde? 中所述,最简单的解决方案是为您的字符串作为浮点值引入一个newtype。然后,您可以为此实现Deserialize,利用Deserialize 的现有实现和字符串解析:

    extern crate serde;
    #[macro_use]
    extern crate serde_derive;
    extern crate serde_json;
    
    use serde::de::{Deserialize, Deserializer, Error, Unexpected};
    
    #[derive(Debug, Deserialize)]
    struct Payload {
        #[serde(default)]
        values: Vec<Vec<Value>>,
    }
    
    #[derive(Debug)]
    struct Value(f64);
    
    impl<'de> Deserialize<'de> for Value {
        fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
            where D: Deserializer<'de>
        {
            let s: &str = Deserialize::deserialize(deserializer)?;
            s.parse()
                .map(Value)
                .map_err(|_| D::Error::invalid_value(Unexpected::Str(s), &"a floating point number as a string"))
        }
    }
    
    fn main() {
        let input = r#"
    {
      "values": [["2", "1.4"], ["8.32", "1.5"]]
    }
    "#;
    
        let out: Payload = serde_json::from_str(input).unwrap();
    
        println!("{:?}", out);
    }
    

    我更喜欢这种解决方案,因为在许多情况下我希望这种新类型在我的系统中发挥作用。


    如果您真的需要反序列化一次并精确到 Vec&lt;Vec&lt;f64&gt;&gt;,您必须实现两个访问者。一个将反序列化外部Vec,一个将反序列化内部Vec。我们将重用之前的Value newtype,但内部访问者会将其剥离。外部访问者会为内部访问者周围的新类型做同样的事情:

    extern crate serde;
    #[macro_use]
    extern crate serde_derive;
    extern crate serde_json;
    
    use serde::de::{Deserialize, Deserializer, Error, SeqAccess, Unexpected, Visitor};
    use std::fmt;
    
    #[derive(Debug, Deserialize)]
    struct Payload {
        #[serde(default, deserialize_with = "from_array_of_arrays_of_strs")]
        values: Vec<Vec<f64>>,
    }
    
    fn from_array_of_arrays_of_strs<'de, D>(deserializer: D) -> Result<Vec<Vec<f64>>, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct OuterVisitor;
    
        impl<'de> Visitor<'de> for OuterVisitor {
            type Value = Vec<Vec<f64>>;
    
            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("a nonempty sequence of a sequence of numbers")
            }
    
            #[inline]
            fn visit_seq<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
            where
                V: SeqAccess<'de>,
            {
                let mut vec = Vec::new();
    
                while let Some(Inner(elem)) = try!(visitor.next_element()) {
                    vec.push(elem);
                }
    
                Ok(vec)
            }
        }
    
        deserializer.deserialize_seq(OuterVisitor)
    }
    
    struct Inner(Vec<f64>);
    
    impl<'de> Deserialize<'de> for Inner {
        fn deserialize<D>(deserializer: D) -> Result<Inner, D::Error>
        where
            D: Deserializer<'de>,
        {
            struct InnerVisitor;
    
            impl<'de> Visitor<'de> for InnerVisitor {
                type Value = Inner;
    
                fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                    formatter.write_str("a nonempty sequence of numbers")
                }
    
                #[inline]
                fn visit_seq<V>(self, mut visitor: V) -> Result<Inner, V::Error>
                where
                    V: SeqAccess<'de>,
                {
                    let mut vec = Vec::new();
    
                    while let Some(Value(elem)) = try!(visitor.next_element()) {
                        vec.push(elem);
                    }
    
                    Ok(Inner(vec))
                }
            }
    
            deserializer.deserialize_seq(InnerVisitor)
        }
    }
    
    struct Value(f64);
    
    impl<'de> Deserialize<'de> for Value {
        fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
        where
            D: Deserializer<'de>,
        {
            let s: &str = Deserialize::deserialize(deserializer)?;
            s.parse().map(Value).map_err(|_| {
                D::Error::invalid_value(Unexpected::Str(s), &"a floating point number as a string")
            })
        }
    }
    
    fn main() {
        let input = r#"
    {
      "values": [["2", "1.4"], ["8.32", "1.5"]]
    }
    "#;
    
        let out: Payload = serde_json::from_str(input).unwrap();
    
        println!("{:?}", out);
    }
    

    【讨论】:

      【解决方案2】:

      无需自己编写访问者,也可以使用字符串而不是数字来解析 JSON 文件。

      use serde_with::{serde_as, DisplayFromStr};
      
      #[serde_as]
      #[derive(Debug, serde::Deserialize)]
      struct Payload {
          #[serde_as(as = "Vec<Vec<DisplayFromStr>>")]
          #[serde(default)]
          values: Vec<Vec<f64>>,
      }
      
      let j = serde_json::json!({
        "values": [["2", "1.4"], ["8.32", "1.5"]]
      });
      
      let p: Payload = serde_json::from_value(j)?;
      assert_eq!(p.values, vec![vec![2.0, 1.4], vec![8.32, 1.5]]);
      

      注释意味着我们在Vec 中有一个Vec,最里面的元素应该使用FromStr 反序列化并使用Display 序列化。该注解支持任何具有DisplayFromStr 实现的类型,因此它也可以用于u64Url 类型。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-03-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-06
        • 2021-03-27
        • 1970-01-01
        相关资源
        最近更新 更多