如何编写Serde访问者以将字符串数组转换为Vec< Vec< f64>&gt ;? [英] How do I write a Serde Visitor to convert an array of arrays of strings to a Vec<Vec<f64>>?

查看:104
本文介绍了如何编写Serde访问者以将字符串数组转换为Vec< Vec< f64>&gt ;?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要将JSON反序列化为具有Vec<Vec<f64>>字段的结构. JSON包含数字字符串,因此我需要一个自定义反序列化器,以便在反序列化期间将字符串转换为f64.

I need to deserialize a JSON into a struct that has a Vec<Vec<f64>> field. The JSON has strings for numbers so I need a custom deserializer to convert the strings to f64 during the deserialization.

我想反序列化的示例JSON:

A sample JSON that I'd like to deserialize:

{
  "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>>,
}

在Serde的示例中,我看到您可能会对访问者做到这一点,所以我已经实施了此访客:

I saw you could probably do this with visitors in the examples of Serde, so I've implemented this visitor:

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)
}

游乐场

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

The compiler complains that visit_str and visit_string have an incompatible type for the trait:

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?

I think I don't have the correct understanding of how visitors work. Can I have only one visitor for deserializing the array of arrays of strings, or do I need one visitor for deserializing the arrays and one visitor for deserializing the strings to f64?

我读过:

  • How to transform fields before deserialization using serde?
  • Is there is a simpler way to convert a type upon deserialization?

推荐答案

中已描述的,如何在使用Serde反序列化之前转换字段? ,最简单的解决方案是为您的字符串作为浮点值引入 newtype .然后,您可以利用Deserialize的现有实现和字符串解析来为此实现Deserialize:

As already described in How to transform fields before deserialization using serde?, the easiest solution is to introduce a newtype for your string-as-a-floating-point-value. You can then implement Deserialize for that, leveraging existing implementations of Deserialize and string parsing:

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);
}

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

I prefer this solution because in many cases I want that new type to play a role in my system.

如果您确实确实需要一次反序列化并精确到Vec<Vec<f64>>,则必须实现两个访问者.一个将反序列化外部Vec,一个将反序列化内部Vec.我们将重用以前的Value新类型,但是内部访问者会将其删除.外部访问者会对内部访问者周围的新类型执行相同的操作:

If you really, truly need to deserialize once and to exactly a Vec<Vec<f64>>, you have to implement two visitors. One will deserialize the outer Vec, one will deserialize the inner Vec. We will reuse the previous Value newtype, but the inner visitor will strip it away. The outer visitor will do the same thing for a newtype around the inner visitor:

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);
}

这篇关于如何编写Serde访问者以将字符串数组转换为Vec&lt; Vec&lt; f64&gt;&gt ;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆