You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 03:05:19