Как преобразовать специальные значения в Option::None при использовании Serde для десериализации?

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

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

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

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

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

Трудно ответить на ваш вопрос, потому что он не включает минимальный воспроизводимый пример. Мы не можем сообщить точно, как выглядит ваш ввод, или даже если это JSON, YAML или .... Нам будет проще помочь вам, если вы попытаетесь воспроизвести свою ошибку в Детская площадка ржавчины, если это возможно, в противном случае в совершенно новый проект Cargo, затем редактировать ваш вопрос, чтобы включить дополнительную информацию. Есть Советы по MCVE для Rust, которые вы можете использовать, чтобы уменьшить исходный код для публикации здесь. Спасибо!

Shepmaster 31.05.2019 01:12

Возможный дубликат Как преобразовать поля при десериализации с помощью Serde?

Stargateur 31.05.2019 05:39
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
2
737
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

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

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

Больше информации в документации:

Это не только отвечает на мой вопрос, но и показывает, как создать обобщенную оболочку для существующих десериализаторов serde.

njaard 31.05.2019 05:30

@njaard это именно то, что говорит stackoverflow.com/questions/46753955/…

Stargateur 31.05.2019 05:40

Другие вопросы по теме