如何修改serde_json行为将Option序列化为数组形式JSON?
如何让serde_json将Option序列化为数组(None→[],Some(x)→[x])且不省略空字段
问题描述
在包含Haskell代码的大规模代码库中,需要将Rust的Option<T>类型序列化为JSON数组:
None对应空数组[]Some(thing)对应单元素数组[thing]
默认serde_json会省略值为None的字段,且没有内置选项修改这一行为。例如:
struct SomeData { foo: Some(1), bar: None }
需要序列化为:
{ "foo": [1], "bar": [] }
希望避免为每个字段手动实现自定义序列化,也不想fork serde_json库。
解决方案:自定义序列化器重写Option逻辑
可以通过包装serde_json的默认序列化器,仅重写Option类型的序列化逻辑,同时强制结构体输出所有字段。
步骤1:定义自定义序列化器
创建一个包装serde_json序列化器的新类型,重写serialize_option方法,将Option转为数组格式:
use serde::{Serialize, Serializer}; use serde_json::ser::Serializer as JsonSerializer; // 包装serde_json的序列化器,仅重写Option的序列化逻辑 struct OptionArraySerializer<S>(S); impl<S> Serializer for OptionArraySerializer<S> where S: Serializer, { type Ok = S::Ok; type Error = S::Error; type SerializeSeq = S::SerializeSeq; type SerializeTuple = S::SerializeTuple; type SerializeTupleStruct = S::SerializeTupleStruct; type SerializeTupleVariant = S::SerializeTupleVariant; type SerializeMap = S::SerializeMap; type SerializeStruct = S::SerializeStruct; type SerializeStructVariant = S::SerializeStructVariant; // 核心:将Option序列化为数组 fn serialize_option<T>(self, value: Option<&T>) -> Result<Self::Ok, Self::Error> where T: ?Sized + Serialize, { let mut seq = self.0.serialize_seq(Some(value.is_some() as usize))?; if let Some(v) = value { seq.serialize_element(v)?; } seq.end() } // 其余方法直接转发给内部序列化器 fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> { self.0.serialize_bool(v) } fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> { self.0.serialize_i8(v) } fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> { self.0.serialize_i16(v) } fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> { self.0.serialize_i32(v) } fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> { self.0.serialize_i64(v) } fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> { self.0.serialize_u8(v) } fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> { self.0.serialize_u16(v) } fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> { self.0.serialize_u32(v) } fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> { self.0.serialize_u64(v) } fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> { self.0.serialize_f32(v) } fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> { self.0.serialize_f64(v) } fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> { self.0.serialize_char(v) } fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> { self.0.serialize_str(v) } fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> { self.0.serialize_bytes(v) } fn serialize_none(self) -> Result<Self::Ok, Self::Error> { self.0.serialize_seq(Some(0))?.end() } fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error> where T: ?Sized + Serialize, { let mut seq = self.0.serialize_seq(Some(1))?; seq.serialize_element(value)?; seq.end() } fn serialize_unit(self) -> Result<Self::Ok, Self::Error> { self.0.serialize_unit() } fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> { self.0.serialize_unit_struct(name) } fn serialize_unit_variant( self, name: &'static str, variant_index: u32, variant: &'static str, ) -> Result<Self::Ok, Self::Error> { self.0.serialize_unit_variant(name, variant_index, variant) } fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<Self::Ok, Self::Error> where T: ?Sized + Serialize, { self.0.serialize_newtype_struct(name, value) } fn serialize_newtype_variant<T>( self, name: &'static str, variant_index: u32, variant: &'static str, value: &T, ) -> Result<Self::Ok, Self::Error> where T: ?Sized + Serialize, { self.0.serialize_newtype_variant(name, variant_index, variant, value) } fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> { self.0.serialize_seq(len) } fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> { self.0.serialize_tuple(len) } fn serialize_tuple_struct( self, name: &'static str, len: usize, ) -> Result<Self::SerializeTupleStruct, Self::Error> { self.0.serialize_tuple_struct(name, len) } fn serialize_tuple_variant( self, name: &'static str, variant_index: u32, variant: &'static str, len: usize, ) -> Result<Self::SerializeTupleVariant, Self::Error> { self.0.serialize_tuple_variant(name, variant_index, variant, len) } fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> { self.0.serialize_map(len) } fn serialize_struct( self, name: &'static str, len: usize, ) -> Result<Self::SerializeStruct, Self::Error> { self.0.serialize_struct(name, len) } fn serialize_struct_variant( self, name: &'static str, variant_index: u32, variant: &'static str, len: usize, ) -> Result<Self::SerializeStructVariant, Self::Error> { self.0.serialize_struct_variant(name, variant_index, variant, len) } }
步骤2:创建序列化辅助函数
定义一个函数,使用自定义序列化器将值转为JSON字符串:
// 用自定义序列化器序列化值 fn serialize_with_option_array<T: Serialize>(value: &T) -> Result<String, serde_json::Error> { let mut buf = Vec::new(); let serializer = OptionArraySerializer(JsonSerializer::new(&mut buf)); value.serialize(serializer)?; Ok(String::from_utf8(buf).unwrap()) }
步骤3:配置结构体强制输出所有字段
serde默认会省略None的Option字段,因此需要给结构体的Option字段添加skip_serializing_if = "never",强制输出所有字段:
use serde::Serialize; // 定义一个始终返回false的函数,用于强制序列化字段 fn never<T>(_: &T) -> bool { false } #[derive(Serialize)] struct SomeData { #[serde(skip_serializing_if = "never")] foo: Option<i32>, #[serde(skip_serializing_if = "never")] bar: Option<i32>, }
步骤4:测试序列化
fn main() { let data = SomeData { foo: Some(1), bar: None, }; let json = serialize_with_option_array(&data).unwrap(); println!("{}", json); // 输出:{"foo":[1],"bar":[]} }
替代方案:新类型包装Option
如果不想自定义序列化器,可以用新类型包装Option,并为其实现自定义序列化:
use serde::{Serialize, Serializer}; #[derive(Serialize)] #[serde(transparent)] struct OptionArray<T>(#[serde(with = "option_as_array")] Option<T>); mod option_as_array { use serde::{Serialize, Serializer}; pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error> where T: Serialize, S: Serializer, { let mut seq = serializer.serialize_seq(Some(value.is_some() as usize))?; if let Some(v) = value { seq.serialize_element(v)?; } seq.end() } } // 使用新类型定义结构体 #[derive(Serialize)] struct SomeData { foo: OptionArray<i32>, bar: OptionArray<i32>, } // 测试 fn main() { let data = SomeData { foo: OptionArray(Some(1)), bar: OptionArray(None), }; let json = serde_json::to_string_pretty(&data).unwrap(); println!("{}", json); }
这种方法不需要自定义序列化器,但需要将代码库中的Option替换为OptionArray,适合小规模修改场景。
内容的提问来源于stack exchange,提问作者Max Murphy
相关产品推荐
相关产品推荐

