[英]How do I write a Serde Visitor to convert an array of arrays of strings to a Vec<Vec<f64>>?
我需要將 JSON 反序列化為具有Vec<Vec<f64>>
字段的結構。 JSON 有數字字符串,所以我需要一個自定義反序列化器在反序列f64
過程中將字符串轉換為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>>,
}
我在 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)
}
編譯器抱怨visit_str
和visit_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
?
我讀過:
正如如何在使用 serde 反序列化之前轉換字段? ,最簡單的解決方案是為您的 string-as-a-floating-point-value 引入一個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<Vec<f64>>
,你必須實現兩個訪問者。 一個將反序列化外部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);
}
也可以使用字符串而不是數字來解析 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
序列化。 注釋支持任何類型的Display
和FromStr
實現,因此它也可以用於u64
或Url
類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.