Как десериализовать NaN как `nan` с помощью serde_json?

У меня есть типы данных, которые выглядят так:

#[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, который мы используем), и является допустимым JSON. К сожалению, если мы позвоним serde_json::from_str(&blob), мы получим ошибку:

Error("invalid type: string "NaN", expected f64", [snip]

Я понимаю, что с числами с плавающей запятой есть свои тонкости, и люди очень самоуверенно относятся к тому, как все должно быть. Я уважаю это. В частности, Rust любит быть очень самоуверенным, и мне это нравится.

Однако в конце дня эти капли JSON - это то, что я собираюсь получить, и мне нужна эта строка "NaN" для десериализации до некоторого значения f64, где is_nan() истинно, и которое сериализуется обратно в строку "NaN", потому что остальная часть экосистема использует Джексона и там это нормально.

Можно ли этого добиться разумным путем?

Изменить: предлагаемые связанные вопросы говорят о переопределении производного дериализатора, но они не объясняют, как конкретно десериализовать поплавки.


person Richard Rast    schedule 31.01.2019    source источник
comment
Я считаю, что на ваш вопрос ответят ответы Как преобразовать поля во время десериализации с помощью Serde? и Как преобразовать поля во время сериализации с помощью Serde?. Если вы не согласны, отредактируйте свой вопрос, чтобы объяснить различия. В противном случае мы можем отметить этот вопрос как уже полученный.   -  person Shepmaster    schedule 31.01.2019
comment
Возможный дубликат Как преобразовать поля во время десериализации с помощью Serde?   -  person Stargateur    schedule 31.01.2019
comment
Добавление всех этих предложенных сообщений как бы приводит к ответу (этот ответ заключается в том, что напишите собственный сериализатор / десериализатор и выясните это самостоятельно), но учитывая, что это не редкий вариант использования с некоторым закрытым, но не решенным github проблем в проекте serde, похоже, что иметь канонический ответ (или еще лучше, флаг serde) было бы немного лучше.   -  person Richard Rast    schedule 31.01.2019


Ответы (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 */ }

и когда у вас есть десериализатор, вы можете взаимодействовать с ним только через посетителей.

Короче говоря, я в конце концов заставил это работать, но похоже, что много кода не должно быть необходимо. Публикуем его здесь в надежде, что либо (а) кто-то знает, как это очистить, либо (б) это действительно так, как это должно быть сделано, и этот ответ будет кому-то полезен. Я провел целый день, усердно читая документы и делая предположения методом проб и ошибок, так что, может быть, это будет полезно кому-то еще. Функции (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)
}
person Richard Rast    schedule 31.01.2019