Как преобразовать специальные значения в Option :: None при использовании Serde для десериализации? - PullRequest
1 голос
/ 30 мая 2019

Я анализирую данные в:

struct Data {
    field1: Option<f32>,
    field2: Option<u64>,
    // more ...
}

Проблема в том, что мой формат входных данных форматирует то, что в Rust будет None, как "n/a".

Как сказать Серде, что Option<T> должно быть None для конкретной строки n/a, в отличие от ошибки? Мы можем предположить, что это не относится к String.

Это не тот же вопрос, что и Как десериализовать "NaN" как `nan` с помощью serde_json? , потому что это создает f32 из специального значения, тогда как мой вопрос - создание Option<Anything> от особой стоимости. Это также не Как преобразовать поля во время десериализации с использованием Serde? , поскольку это все еще касается определенного типа.

1 Ответ

3 голосов
/ 30 мая 2019

Вы можете написать свою собственную функцию десериализации, которая обрабатывает этот случай:

use serde::de::Deserializer;
use serde::Deserialize;

// custom deserializer function
fn deserialize_maybe_nan<'de, D, T: Deserialize<'de>>(
    deserializer: D,
) -> Result<Option<T>, D::Error>
where
    D: Deserializer<'de>,
{
    // we define a local enum type inside of the function
    // because it is untagged, serde will deserialize as the first variant
    // that it can
    #[derive(Deserialize)]
    #[serde(untagged)]
    enum MaybeNA<U> {
        // if it can be parsed as Option<T>, it will be
        Value(Option<U>),
        // otherwise try parsing as a string
        NAString(String),
    }

    // deserialize into local enum
    let value: MaybeNA<T> = Deserialize::deserialize(deserializer)?;
    match value {
        // if parsed as T or None, return that
        MaybeNA::Value(value) => Ok(value),

        // otherwise, if value is string an "n/a", return None
        // (and fail if it is any other string)
        MaybeNA::NAString(string) => {
            if string == "n/a" {
                Ok(None)
            } else {
                Err(serde::de::Error::custom("Unexpected string"))
            }
        }
    }
}

Затем вы можете пометить свои поля как #[serde(default, deserialize_with = "deserialize_maybe_nan")], чтобы использовать эту функцию вместо функции по умолчанию:

#[serde(Deserialize)]
struct Data {
    #[serde(default, deserialize_with = "deserialize_maybe_nan")]
    field1: Option<f32>,
    #[serde(default, deserialize_with = "deserialize_maybe_nan")]
    field2: Option<u64>,
    // more ...
}

Пример рабочей площадки

Более подробная информация в документации:

...