C#中Json序列化小技巧

Json序列化是项目开发中非常常见的需求
(现在那么多的API靠Json传递参数之类的)
而C#对Json序列化/反序列化的支持也是非常完善的
下面讲几个我在项目中使用到的技巧
需要注意的是,下面的代码均使用.net9以上,老版本的.net可能还不支持相关功能

Json序列化/反序列化主要通过添加Attribute或者设置option实现

<0x01> 通过Attribute可以实现的

属性值重命名

一般情况下,Json序列化/反系列化基于属性名
大部分情况下也没啥问题
但有时,Json属性名会和C#关键字冲突
比方这个Json

{
  "object": "list",
  "data": [
    {
      "id": "deepseek-chat",
      "object": "model",
      "owned_by": "deepseek"
    },
    {
      "id": "deepseek-reasoner",
      "object": "model",
      "owned_by": "deepseek"
    }
  ]
}

(Deepseek获取模型的json示例)

这时候就必须重命名了
可以通过JsonPropertyName特性给属性重命名,匹配Json的名字

public class ModelsObject
{
    [JsonPropertyName("object")]
    public string ObjectType { get; set; }

    public ModelData[] data { get; set; }

    public class ModelData
    {
        public string id { get; set; }
        
        [JsonPropertyName("object")]
        public string ObjectType { get; set; }
        
        public string owned_by { get; set; }
    }
}

这里就给object这个属性做了重命名

通常Json都是下划线命名,C#属性通常大驼峰命名
所以我基本上是都做重命名的

枚举重命名

跟上面差不多,Json的命名习惯跟C#有差异
我也习惯自己做一遍重命名
需要使用JsonStringEnumMemberName特性

public enum EnumTest
{
	[JsonStringEnumMemberName("enum_a")]
	AAA,
	[JsonStringEnumMemberName("enum_b")]
	BBB,
}

是否包含某属性

一般情况下C#的序列化会去序列化public属性,不序列化private/protected属性
这个是可以控制的,通过标记JsonInclude特性或者JsonIgnore特性

public class TestObject
{
	// 这个属性按默认是会被序列化的,但这里指定忽略
	[JsonIgnore]
	public string PropertyA { get; set; }
	// 这个属性默认是不会被序列化的,但这里指定包含
	[JsonInclude]
	private string PropertyB { get; set; }
}

多态类型的序列化

有些时候,会需要传一个List,List里面有不同类型的数据
理想的情况就是List指定基类类型,里面存放派生类型
这时候要给这个List做序列化,需要用JsonDerivedType特性做下派生类型标记
不然只会序列化基类的属性,即使List中的对象是多态的

var objectList = new List<ObjectBase>();
// 假设随便塞了点数据
var jsonString = JsonSerializer.Serialize(objectList);

[JsonDerivedType(typeof(ObjectA))]
[JsonDerivedType(typeof(ObjectB))]
public class ObjectBase
{
	public string Name { get; set; }
}
public class ObjectA : ObjectBase
{
	public int IntProperty { get; set; }
}
public class ObjectB : ObjectBase
{
	public float FloatProperty { get; set; }
}

<0x02> 通过option可以实现的

这里的option指的是JsonSerializerOptions类型对象
可以在序列化时传入的参数
比如这样

var option = new JsonSerializerOptions()
{
	//...
}
var jsonString = JsonSerializer.Serialize(obj, option);

忽略/包括空值属性

C#在处理空值时,默认会带上这个属性,输出为null
可以设置成如果是空值,就忽略这个属性
需要通过设置DefaultIgnoreCondition
比如说

// 当属性值为空时忽略这个属性输出
var option = new JsonSerializerOptions()
{
	DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
}

JsonIgnoreCondition有下面的值

  • Never
    • 总是序列化/反序列化,并且无视IgnoreNullValues的设置
  • Always
    • 总是忽略
  • WhenWritingDefault
    • 当属性值等于默认值时忽略
  • WhenWritingNull
    • 当属性值为null时忽略
    • 只有类型为引用类型时有效

从枚举中转换

很多时候,Json传递的string值是固定的几个选项
这时候,在C#中选择枚举是非常合适的
需要在设置中指定Converters
比如这样

// 这样指定即可
var option = new JsonSerializerOptions()
{
	Converters = { new JsonStringEnumConverter() },
}
Licensed under CC BY-NC-SA 4.0