JSON序列化-System.Text.Json
- 序列化
- 序列化为字符串
- 序列化为字节数组
- 中文转码
- 自定义属性名
- 1、JsonPropertyName实现
- 2、JsonSerializerOptions.PropertyNamingPolicy参数
- 3、自定义命名策略
- 4、字典key使用命名策略
- 输出字段顺序控制
- 枚举展示
- 属性忽略
- 1、使用JsonIgnore
- 2、使用JsonSerializerOptions
- 包含字段(field)
- `JsonInclude`
- `JsonSerializerOptions`的`IncludeFields`字段
- 格式化输出
- 日期格式化
- 反序列化
- 注意值类型触发异常
- 示例1
- 示例2
- 示例3
- 必须属性
- 示例1
- 示例2
- 示例3
序列化
序列化为字符串
UserDTO user = new UserDTO
{UserName = "张三",Age = 18,Birthday = new DateTime(2000, 1, 1, 1, 1, 1),IsCheck = true
};
string json = JsonSerializer.Serialize(user);
Console.WriteLine(json);// {"UserName":"\u5F20\u4E09","Age":18,"Birthday":"2000-01-01T01:01:01","IsCheck":true}
输出的中文,竟然被转码了,需要自定义序列化选项来控制输出
序列化为字节数组
UserDTO user = new UserDTO
{UserName = "张三",Age = 18,Birthday = new DateTime(2000, 1, 1, 1, 1, 1),IsCheck = true
};byte[] data = JsonSerializer.SerializeToUtf8Bytes(user);
// 转为字符串
Console.WriteLine(System.Text.UTF8Encoding.UTF8.GetString(data));// {"UserName":"\u5F20\u4E09","Age":18,"Birthday":"2000-01-01T01:01:01","IsCheck":true}
中文转码
设置一下
JsonSerializerOptions
中的Encoder
字段
UserDTO user = new UserDTO
{UserName = "张三",Age = 18,Birthday = new DateTime(2000, 1, 1, 1, 1, 1),IsCheck = true
};JsonSerializerOptions options = new JsonSerializerOptions
{Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
};
string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);// {"UserName":"张三","Age":18,"Birthday":"2000-01-01T01:01:01","IsCheck":true}
自定义属性名
1、JsonPropertyName实现
class UserDTO
{[JsonPropertyName("user_name")]public string UserName { get; set; }public int? Age { get; set; }public DateTime? Birthday { get; set; }public bool? IsCheck { get; set; }
}
//
UserDTO user = new UserDTO{UserName = "张三",Age = 18,Birthday = new DateTime(2000, 1, 1, 1, 1, 1),IsCheck = true};string json = JsonSerializer.Serialize(user);Console.WriteLine(json);
// {"user_name":"\u5F20\u4E09","Age":18,"Birthday":"2000-01-01T01:01:01","IsCheck":true}
2、JsonSerializerOptions.PropertyNamingPolicy参数
系统内置了几种名称转换方式(C#默认是大驼峰规则,所以内置策略里面没有这个选项)
微软PropertyNamingPolicy对比
命名策略 | 说明 | 原始属性名称 | 经过转换的属性名称 |
---|---|---|---|
CamelCase | 第一个单词以小写字符开头。连续单词以大写字符开头 | TempCelsius | tempCelsius |
KebabCaseLower | 单词由连字符分隔,所有字符均为小写 | TempCelsius | temp-celsius |
KebabCaseUpper | 单词由连字符分隔,所有字符均为大写 | TempCelsius | TEMP-CELSIUS |
SnakeCaseLower | 单词用下划线分隔,所有字符均为小写 | TempCelsius | temp_celsius |
SnakeCaseUpper | 单词用下划线分隔,所有字符均为大写 | TempCelsius | TEMP_CELSIUS |
UserDTO user = new UserDTO
{UserName = "张三",Age = 18,Birthday = new DateTime(2000, 1, 1, 1, 1, 1),IsCheck = true
};JsonSerializerOptions options = new JsonSerializerOptions
{PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);// {"user_name":"\u5F20\u4E09","age":18,"birthday":"2000-01-01T01:01:01","is_check":true}
3、自定义命名策略
扩展抽象类
JsonNamingPolicy
- 自定义命名策略
统一在名称前面加前缀
public class UserNamingPolicy : JsonNamingPolicy{public override string ConvertName(string name){return "ext_" + name;}}
- 使用自定义命名策略
UserDTO user = new UserDTO
{UserName = "张三",Age = 18,Birthday = new DateTime(2000, 1, 1, 1, 1, 1),IsCheck = true
};JsonSerializerOptions options = new JsonSerializerOptions
{PropertyNamingPolicy = new UserNamingPolicy()
};string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);// {"ext_UserName":"\u5F20\u4E09","ext_Age":18,"ext_Birthday":"2000-01-01T01:01:01","ext_IsCheck":true}
4、字典key使用命名策略
字典对象的key是动态的,重命名需要单独设置一下,命名策略是公用的
class UserDTO{[JsonPropertyName("user_name")]public string UserName { get; set; }public int? Age { get; set; }public DateTime? Birthday { get; set; }public bool? IsCheck { get; set; }public Dictionary<String, object> Ext { get; set; }}///
UserDTO user = new UserDTO{UserName = "张三",Age = 18,Birthday = new DateTime(2000, 1, 1, 1, 1, 1),IsCheck = true,Ext = new Dictionary<string, object> {{ "width", 10 },{ "height", 200 },{ "desc", "测试" }}};JsonSerializerOptions options = new JsonSerializerOptions{//DictionaryKeyPolicy= JsonNamingPolicy.CamelCaseDictionaryKeyPolicy = new UserNamingPolicy()};string json = JsonSerializer.Serialize(user, options);Console.WriteLine(json);// {"user_name":"\u5F20\u4E09","Age":18,"Birthday":"2000-01-01T01:01:01","IsCheck":true,"Ext":{"ext_width":10,"ext_height":200,"ext_desc":"\u6D4B\u8BD5"}}
输出字段顺序控制
通过
JsonPropertyOrder
来控制,由小到大现实,不设置默认是0
class UserDTO{[JsonPropertyOrder(100)]public string UserName { get; set; }public int? Age { get; set; }[JsonPropertyOrder(99)]public DateTime? Birthday { get; set; }[JsonPropertyOrder(-100)]public bool? IsCheck { get; set; }[JsonPropertyOrder(-99)]public Dictionary<String, object> Ext { get; set; }}//
UserDTO user = new UserDTO
{UserName = "张三",Age = 18,Birthday = new DateTime(2000, 1, 1, 1, 1, 1),IsCheck = true,Ext = new Dictionary<string, object> {{ "width", 10 },{ "height", 200 },{ "desc", "测试" }}
};JsonSerializerOptions options = new JsonSerializerOptions
{//DictionaryKeyPolicy= JsonNamingPolicy.CamelCaseDictionaryKeyPolicy = new UserNamingPolicy()
};string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);// {"IsCheck":true,"Ext":{"ext_width":10,"ext_height":200,"ext_desc":"\u6D4B\u8BD5"},"Age":18,"Birthday":"2000-01-01T01:01:01","UserName":"\u5F20\u4E09"}
枚举展示
默认情况下,枚举会序列化为数字(默认从0开始,也可以自定义)。 若要将枚举名称序列化为字符串,需要使用
JsonStringEnumConverter
,设置JsonSerializerOptions
类的Converters
字段
JsonStringEnumConverter
有两个关键参数
1、namingPolicy,输出的字符串可以配置命名策略,默认为null,原样输出
2、allowIntegerValues,默认为true
,当为true
时,如果枚举值未定义,它将以数字形式输出,而不是字符串形式
-定义Gender
枚举
public enum Gender{Male, Female}
- 定义
UserDTO
class UserDTO{[JsonPropertyOrder(100)]public string UserName { get; set; }public int? Age { get; set; }[JsonPropertyOrder(99)]public DateTime? Birthday { get; set; }[JsonPropertyOrder(-100)]public bool? IsCheck { get; set; }[JsonPropertyOrder(-99)]public Dictionary<String, object> Ext { get; set; }public Gender? UserGender { get; set; }}
- 使用枚举
UserDTO user = new UserDTO
{UserName = "张三",Age = 18,Birthday = new DateTime(2000, 1, 1, 1, 1, 1),IsCheck = true,Ext = new Dictionary<string, object> {{ "width", 10 },{ "height", 200 },{ "desc", "测试" }},UserGender = Gender.Male
};JsonSerializerOptions options = new JsonSerializerOptions
{Converters = {new JsonStringEnumConverter()}
};string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);// {"IsCheck":true,"Ext":{"width":10,"height":200,"desc":"\u6D4B\u8BD5"},"Age":18,"UserGender":"Male","Birthday":"2000-01-01T01:01:01","UserName":"\u5F20\u4E09"}
属性忽略
默认情况下,所有公共属性都会序列化。 如果不想让某些属性出,可以使用如下方法控制输出
1、使用JsonIgnore
主要针对单个属性,个性化配置
Never
,序列化、反序列化Always
,永远会被忽略WhenWritingDefault
,默认值场景,会被忽略(例如引用类型时的null)WhenWritingNull
,适用引用类型,为null时被忽略- 定义UserDTO
class UserDTO{[JsonIgnore(Condition = JsonIgnoreCondition.Always)]public string UserName { get; set; }[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]public int Age { get; set; }[JsonIgnore(Condition = JsonIgnoreCondition.Never)]public DateTime? Birthday { get; set; }[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]public bool? IsCheck { get; set; }public Dictionary<String, object> Ext { get; set; }public Gender? UserGender { get; set; }}
- 具体使用
UserDTO user = new UserDTO{UserName = "张三",Age = 0,UserGender = Gender.Male};JsonSerializerOptions options = new JsonSerializerOptions{Converters = {new JsonStringEnumConverter()}};string json = JsonSerializer.Serialize(user, options);Console.WriteLine(json);// {"Birthday":null,"Ext":null,"UserGender":"Male"}
UserName : 设置为Always
,永不显示,无论是否有值
Birthday:设置为Never
,永远显示,为null
也会显示
Age:设置为WhenWritingDefault
,默认值不显示,值类型,默认为0,所以不显示
IsCheck:设置为WhenWritingNull
,null不显示,可空类型,默认为null
2、使用JsonSerializerOptions
IgnoreReadOnlyProperties
忽略所有只读属性
JsonSerializerOptionsoptions = new JsonSerializerOptions
{IgnoreReadOnlyProperties = true
};
DefaultIgnoreCondition
,与JsonIgnore
的条件通用
JsonSerializerOptions options = new JsonSerializerOptions
{DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
- 定义UserDTO
class UserDTO{public string UserName { get; set; }public int Age { get; set; }public DateTime? Birthday { get; private set; } = new DateTime(2000, 12, 20, 1, 1, 1);public bool? IsCheck { get; set; }public Dictionary<String, object> Ext { get; set; }public Gender? UserGender { get; set; }}
- 使用
UserDTO user = new UserDTO{UserName = "张三",};JsonSerializerOptions options = new JsonSerializerOptions{IgnoreReadOnlyProperties = true,DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull};string json = JsonSerializer.Serialize(user, options);Console.WriteLine(json);
// {"UserName":"\u5F20\u4E09","Age":0}
Birthday:set方法是私有的,属于只读属性,虽然有值,但是也会被忽略
Ext :引用类型,null,被忽略
包含字段(field)
一般情况,公共属性字段默认会被序列化,但是字段(
field
)默认不会被序列化,如果需要把指定的field
序列化,需要特殊设置
JsonInclude
针对单个
field
进行设置
[JsonInclude]public String Hello;
JsonSerializerOptions
的IncludeFields
字段
全局
field
设置
JsonSerializerOptions options = new JsonSerializerOptions
{IncludeFields = true
};
- 定义UserDTO
class UserDTO{public string UserName { get; set; }[JsonInclude]public String Hello;public String World;}
- 使用
UserDTO user = new UserDTO
{UserName = "张三",Hello = "hello",World = "world"
};JsonSerializerOptions options = new JsonSerializerOptions
{IncludeFields = true
};string json = JsonSerializer.Serialize(user, options );
Console.WriteLine(json);
格式化输出
JsonSerializerOptions
类的WriteIndented
字段
UserDTO user = new UserDTO{UserName = "张三",Birthday = new DateTime(2000, 12, 20, 1, 1, 1)};JsonSerializerOptions options = new JsonSerializerOptions{WriteIndented = true,DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull};string json = JsonSerializer.Serialize(user, options);Console.WriteLine(json);
{"UserName": "\u5F20\u4E09","Age": 0,"Birthday": "2000-12-20T01:01:01"
}
日期格式化
需要扩展
JsonConverter
抽象类,实现自定义类型转换输出
- 定义日期转换器
public class DateTimJsonConverter : JsonConverter<DateTime>{public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options){return DateTime.Parse(reader.GetString());}public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options){writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));}}
- 使用
UserDTO user = new UserDTO
{UserName = "张三",Birthday = new DateTime(2000, 12, 20, 1, 1, 1)
};JsonSerializerOptions options = new JsonSerializerOptions
{DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,Converters = {new DateTimJsonConverter()}
};string json = JsonSerializer.Serialize(user, options);
Console.WriteLine(json);// {"UserName":"\u5F20\u4E09","Age":0,"Birthday":"2000-12-20 01:01:01"}
反序列化
注意值类型触发异常
- 对于值类型属性,例如
int
等,json文本中该属性最好有合法的值,否则整个key-value数据就不要提供,否则会抛异常 - 将值类型定义为可空类型,例如
int
换成int?
或则Nullable<int>
(不确定上游系统返回的数据格式,尽量做好兼容)
class UserDTO{public string UserName { get; set; }public int Age { get; set; }public DateTime? Birthday { get; set; }public bool? IsCheck { get; set; }public Dictionary<String, object> Ext { get; set; }public Gender? UserGender { get; set; }}
示例1
这里是一个正常返回的示例,
Age
虽然是值类型,但是提供的数据,有正常的值
string json = """{"UserName":"张三","Age":18,"IsCheck":true,"Birthday":"2000-12-20T01:01:01"}""";
UserDTO user = JsonSerializer.Deserialize<UserDTO>(json);
示例2
这里没有提供
Age
数据,反序列化不会解析这个字段,会给默认值0
string json = """{"UserName":"张三","IsCheck":true,"Birthday":"2000-12-20T01:01:01"}""";
UserDTO user = JsonSerializer.Deserialize<UserDTO>(json);
示例3
这里给值类型的
Age
提供了null
,肯定会抛异常;类似场景在Java
中有int
与Integer
如果Age
属性定义为int?
,就当成引用类型处理,最终解析为null
string json = """{"UserName":"张三","Age":null,"IsCheck":true,"Birthday":"2000-12-20T01:01:01"}""";
UserDTO user = JsonSerializer.Deserialize<UserDTO>(json);
必须属性
必须的要求哪怕提供
null
也是可以的,否则验证不通过
required
关键字JsonRequiredAttribute
属性JsonSerializerOptions
的TypeInfoResolver
字段
class UserDTO
{public required string UserName { get; set; }[JsonRequired]public Nullable<int> Age { get; set; }public DateTime? Birthday { get; set; }public bool? IsCheck { get; set; }public Dictionary<String, object> Ext { get; set; }public Gender? UserGender { get; set; }
}
示例1
Age
字段标记必须,提供一个null
也代表赋值了,可以通过验证
string json = """{"UserName":"张三","Age":null,"IsCheck":true,"Birthday":"2000-12-20T01:01:01"}""";
UserDTO user = JsonSerializer.Deserialize<UserDTO>(json);
示例2
UserName
字段标记必须,输入数据没提供任何数据,验证不通过
string json = """{"Age":null,"IsCheck":true,"Birthday":"2000-12-20T01:01:01"}""";
UserDTO user = JsonSerializer.Deserialize<UserDTO>(json);
示例3
通过
TypeInfoResolver
设置某个属性是否必须
JsonSerializerOptions options = new JsonSerializerOptions{TypeInfoResolver = new DefaultJsonTypeInfoResolver{Modifiers ={static typeInfo =>{if (typeInfo.Kind != JsonTypeInfoKind.Object){return;}foreach (JsonPropertyInfo propertyInfo in typeInfo.Properties){if("UserName".Equals(propertyInfo.Name)){propertyInfo.IsRequired = true;}}}}}};string json = """{"Age":null,"IsCheck":true,"Birthday":"2000-12-20T01:01:01"}""";UserDTO user = JsonSerializer.Deserialize<UserDTO>(json, options);