如何使用serde_json将“NaN”反序列化为`nan`?

6

我有一些数据类型,长这样:

#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Matrix {
    #[serde(rename = "numColumns")]
    pub num_cols: usize,
    #[serde(rename = "numRows")]
    pub num_rows: usize,
    pub data: Vec<f64>,
}

我的JSON主体看起来像这样:

{
    "numRows": 2,
    "numColumns": 1,
    "data": [1.0, "NaN"]
}

这是由我们使用的Java服务器提供的Jackson序列化,是有效的JSON格式。不幸的是,如果我们调用serde_json::from_str(&blob),会出现错误:
Error("invalid type: string "NaN", expected f64", [snip]

我知道浮点数有微妙之处,人们对正确的方式持有不同意见,我尊重这一点。Rust 特别喜欢表达观点,而我也很喜欢这种风格。
然而到了最后,这些 JSON 块就是我要收到的东西,我需要将字符串“NaN”反序列化为某个 f64 值,其中 is_nan() 为 true,并且该值可以被序列化回字符串“NaN”,因为其它生态系统使用 Jackson 并且那里没问题。
有合理的方法可以实现这一点吗?
编辑:建议中的相关问题涉及覆盖派生解析器,但它们没有说明如何特别反序列化浮点数。

1
我相信你的问题已经在以下两个回答中得到了解答:如何使用Serde在反序列化期间转换字段?如何使用Serde在序列化期间转换字段?。如果你有不同意见,请**[编辑]**你的问题以解释差异。否则,我们可以将此问题标记为已回答。 - Shepmaster
可能是如何在使用Serde进行反序列化时转换字段?的重复问题。 - Stargateur
1
将所有这些建议的帖子加起来,可以得出一个答案(即编写自定义序列化程序/反序列化程序并自行解决问题),但考虑到这是一种不罕见的用例,并且在serde项目上有一些已关闭但未解决的github问题,似乎有一个规范的答案(或更好的是,一个serde标志)会更好一些。 - Richard Rast
标题有些误导:反序列化NaN并不是全部的问题,问题在于你把这个浮点数放在了列表里,这就使得问题尤其棘手。修改一下标题会更有帮助。 - Cornelius Roemer
1个回答

1

实际上,使用自定义反序列化器来处理Vec(或Map等)内部的内容似乎是Serde上的一个未解决问题,截至撰写本文时已经有一年多了:https://github.com/serde-rs/serde/issues/723

我认为解决方案是编写一个自定义的f64反序列化器(这很好),以及所有使用f64作为子项的内容(例如Vec<f64>HashMap<K,f64>等)。不幸的是,这些方法的实现似乎不可组合,因为它们的实现看起来像是:

deserialize<'de, D>(deserializer: D) -> Result<Vec<f64>, D::Error>
where D: Deserializer<'de> { /* snip */ }

一旦您拥有了反序列化器,您只能通过访问者与其进行交互。

长话短说,我最终让它工作了,但似乎需要很多不必要的代码。在此发布它,希望(a)有人知道如何清理它,或者(b)这确实是应该完成的方式,并且这个答案对某人有用。我已经热情地阅读文档并进行试错猜测了整整一天,因此也许对其他人有用。函数(de)serialize_float(s)应与适当的#[serde( (de)serialize_with="etc." )]一起使用在字段名称上方。

use serde::de::{self, SeqAccess, Visitor};
use serde::ser::SerializeSeq;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;

type Float = f64;

const NAN: Float = std::f64::NAN;

struct NiceFloat(Float);

impl Serialize for NiceFloat {
    #[inline]
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serialize_float(&self.0, serializer)
    }
}

pub fn serialize_float<S>(x: &Float, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    if x.is_nan() {
        serializer.serialize_str("NaN")
    } else {
        serializer.serialize_f64(*x)
    }
}

pub fn serialize_floats<S>(floats: &[Float], serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
{
    let mut seq = serializer.serialize_seq(Some(floats.len()))?;

    for f in floats {
        seq.serialize_element(&NiceFloat(*f))?;
    }

    seq.end()
}

struct FloatDeserializeVisitor;

impl<'de> Visitor<'de> for FloatDeserializeVisitor {
    type Value = Float;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a float or the string \"NaN\"")
    }

    fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(v as Float)
    }

    fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(v as Float)
    }

    fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(v as Float)
    }

    fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(v as Float)
    }

    fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(v as Float)
    }

    fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(v as Float)
    }

    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        if v == "NaN" {
            Ok(NAN)
        } else {
            Err(E::invalid_value(de::Unexpected::Str(v), &self))
        }
    }
}

struct NiceFloatDeserializeVisitor;

impl<'de> Visitor<'de> for NiceFloatDeserializeVisitor {
    type Value = NiceFloat;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("a float or the string \"NaN\"")
    }

    fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(NiceFloat(v as Float))
    }

    fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(NiceFloat(v as Float))
    }

    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        if v == "NaN" {
            Ok(NiceFloat(NAN))
        } else {
            Err(E::invalid_value(de::Unexpected::Str(v), &self))
        }
    }
}

pub fn deserialize_float<'de, D>(deserializer: D) -> Result<Float, D::Error>
where
    D: Deserializer<'de>,
{
    deserializer.deserialize_any(FloatDeserializeVisitor)
}

impl<'de> Deserialize<'de> for NiceFloat {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let raw = deserialize_float(deserializer)?;
        Ok(NiceFloat(raw))
    }
}

pub struct VecDeserializeVisitor<T>(std::marker::PhantomData<T>);

impl<'de, T> Visitor<'de> for VecDeserializeVisitor<T>
where
    T: Deserialize<'de> + Sized,
{
    type Value = Vec<T>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("A sequence of floats or \"NaN\" string values")
    }

    fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
    where
        S: SeqAccess<'de>,
    {
        let mut out = Vec::with_capacity(seq.size_hint().unwrap_or(0));

        while let Some(value) = seq.next_element()? {
            out.push(value);
        }

        Ok(out)
    }
}

pub fn deserialize_floats<'de, D>(deserializer: D) -> Result<Vec<Float>, D::Error>
where
    D: Deserializer<'de>,
{
    let visitor: VecDeserializeVisitor<NiceFloat> = VecDeserializeVisitor(std::marker::PhantomData);

    let seq: Vec<NiceFloat> = deserializer.deserialize_seq(visitor)?;

    let raw: Vec<Float> = seq.into_iter().map(|nf| nf.0).collect::<Vec<Float>>();

    Ok(raw)
}

实际上自2017年以来一直开放:https://github.com/serde-rs/json/issues/202 - til

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接