Reputation: 1973
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
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":[]}
See also:
Upvotes: 2