Max Murphy
Max Murphy

Reputation: 1973

How to serialize options to JSON as arrays

I am working in a relatively large codebase where options are represented in JSON as arrays, so None is represented in JSON as [] and Some(thing) as [thing]. (Yes, the codebase also contains Haskell, in case you are wondering.) How can I override the default serde_json behaviour, which is to omit optional fields, to match this?

E.g. a struct:

SomeData {
  foo: Some(1),
  bar: None
}

should be serialized to JSON as:

{
  "foo": [1],
  "bar": []
}

Of course, one could theoretically implement custom serialization for each and every optional field in every struct that interacts with the codebase but that would be a huge undertaking, even if it were possible.

There don't seem to be any options in the serde_json serialisation of some and none so I imagine that the solution will be creating a new serializer that inherits almost everything from serde_json apart from the Option serialization and deserialization. Are there any examples of projects that do this? It would also be possible to make a fork but maintaining a fork is never much fun.

Upvotes: 1

Views: 352

Answers (1)

E_net4
E_net4

Reputation: 30003

Of course, one could theoretically implement custom serialization for each and every optional field in every struct that interacts with the codebase

A custom implementation for each and every field is not necessary. By using serialize_with, you only need one transformation function describing the serialization of any serializable Option<T> as a sequence.

fn serialize_option_as_array<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
where
    T: Serialize,
    S: Serializer,
{
    let len = if value.is_some() { 1 } else { 0 };
    let mut seq = serializer.serialize_seq(Some(len))?;
    for element in value {
        seq.serialize_element(element)?;
    }
    seq.end()
}

Using it in your struct:

use serde_derive::Serialize;
use serde::ser::{Serialize, Serializer, SerializeSeq};
use serde_json;

#[derive(Debug, Serialize)]
struct SomeData {
    #[serde(serialize_with = "serialize_option_as_array")]
    foo: Option<i32>,
    #[serde(serialize_with = "serialize_option_as_array")]
    bar: Option<u32>,
}


let data = SomeData {
    foo: Some(5),
    bar: None,
};

println!("{}", serde_json::to_string(&data)?);

The output:

{"foo":[5],"bar":[]}

Playground

See also:

Upvotes: 2

Related Questions