4

I'm parsing data into:

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

The problem is that my input data format formats what would be a None in Rust as "n/a".

How do tell Serde that an Option<T> should be None for the specific string n/a, as opposed to an error? We can assume that this doesn't apply to a String.

This isn't the same question as How to deserialize "NaN" as `nan` with serde_json? because that's creating an f32 from a special value whereas my question is creating an Option<Anything> from a special value. It's also not How to transform fields during deserialization using Serde? as that still concerns a specific type.

1
  • It's hard to answer your question because it doesn't include a minimal reproducible example. We can't tell exactly what your input looks like, or even if it's JSON or YAML or .... It would make it easier for us to help you if you try to reproduce your error on the Rust Playground if possible, otherwise in a brand new Cargo project, then edit your question to include the additional info. There are Rust-specific MCVE tips you can use to reduce your original code for posting here. Thanks! Commented May 30, 2019 at 23:12

1 Answer 1

7

You can write your own deserialization function that handles this case:

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")) } } } } 

Then you can mark your fields with #[serde(default, deserialize_with = "deserialize_maybe_nan")] to use this function instead of the default function:

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

Working playground example

More information in the documentation:

Sign up to request clarification or add additional context in comments.

4 Comments

This not only answers my question, but shows how to make a generalized wrapper over serde's existing deserializers.
@njaard it's exactly what stackoverflow.com/questions/46753955/… say
This is useful but does not work when used for String and the value is a valid integer. Any idea how to proceed here?
@Manuel What do you mean? When the integer is encoded as a string?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.