JSON序列化-System.Text.Json
序列化
序列化为字符串
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#默认是大驼峰规则,所以内置策略里面没有这个选项)
命名策略 | 说明 | 原始属性名称 | 经过转换的属性名称 |
---|---|---|---|
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.CamelCase
DictionaryKeyPolicy = 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.CamelCase
DictionaryKeyPolicy = 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);