.NET Core 3.1中的Json互操作最全解读-收藏级

序列化和反序列化

 

基本知识已经介绍完成,下面我们进入 System.Text.Json 的内部世界一探究竟。

互操作

 

思考下面的代码

// 序列化
var user = new UserInfo { Name = "Ron", Money = 4.5m, Age = 30 };
var json = JsonSerializer.Serialize(user);

// 输出
{"Name":"Ron","Money":4.5,"Age":30}

// 反序列化
user = JsonSerializer.Deserialize<UserInfo>(json);

 

目前为止,上面的代码工作良好。让我们对上面的代码稍作修改,将 JSON 字符串进行一个转小写的操作后再进行反序列化的操作

// 输出
{"name":"Ron","money":4.5,"age":30}

// 反序列化
user = JsonSerializer.Deserialize<UserInfo>(json);

 

上面的代码可以正常运行,也不会抛出异常,你可以得到一个完整的 user 对象;但是,user对象的属性值将会丢失!这是因为 System.Text.Json 默认采用的是区分大小写匹配的方式,为了解决这个问题,我们需要引入序列化操作个性化设置,请参考下面的代码,启用忽略大小写的设置

// 输出
{"name":"Ron","money":4.5,"age":30}

var options = new JsonSerializerOptions()
{
    PropertyNameCaseInsensitive = true
};
// 反序列化
user = JsonSerializer.Deserialize<UserInfo>(json,options);

 

格式化 JSON

 

现在你可以选择对序列化的 JSON 文本进行美化,而不是输出上面的压缩后的 JSON 文本,为了实现美化的效果,你仅仅需要在序列化的时候加入一个 WriteIndented 设置

var options = new JsonSerializerOptions()
    options.WriteIndented = true;
var user = new UserInfo { Name = "Ron", Money = 4.5m, Age = 30, Remark = "你好,欢迎!" };
var json = JsonSerializer.Serialize(user, options);

// 输出
{
"Name": "Ron",
"Money": 4.5,
"Age": 30,
"Remark": "u4F60u597DuFF0Cu6B22u8FCEuFF01"
}

 

你看,就是这么简单,但是你也发现了,上面的 Remark 属性在序列化后,中文被转义了,这就是接下来要解决的问题

字符转义的问题

 

在默认情况下,System.Text.Json 序列化程序对所有非 ASCII 字符进行转义;这就是中文被转义的根本原因。但是在内部,他又允许你自定义控制字符集的转义行为,这个设置就是:Encoder,比如下面的代码,对中文进行转义的例外设置,需要创建一个 TextEncoderSettings 对象,并将 UnicodeRanges.All 加入允许例外范围内,并使用 JavaScriptEncoder 根据 TextEncoderSettings 创建一个 JavaScriptEncoder 对象即可。

var encoderSettings = new TextEncoderSettings();
encoderSettings.AllowRanges(UnicodeRanges.All);
var options = new JsonSerializerOptions();
options.Encoder = JavaScriptEncoder.Create(encoderSettings);
options.WriteIndented = true;
var user = new UserInfo { Name = "Ron", Money = 4.5m, Age = 30, Remark = "你好,欢迎!" };
var json = JsonSerializer.Serialize(user, options);
// 输出
{
"Name": "Ron",
"Money": 4.5,
"Age": 30,
"Remark": "你好,欢迎!"
}

 

还有另外一种模式,可以不必设置例外而达到不转义的效果,这个模式就是“非严格 JSON”模式,将上面的 JavaScriptEncoder.Create(encoderSettings) 替换为下面的代码

options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;

 

序列化相关-异步/流式

 

System.Text.Josn 提供了一系列丰富的 JSON 互操作,这其中包含异步和流式处理,这点也是和 Newtonsoft.Json 最大的不同,但不管是那种方式,都要牢记,最后都是通过下面的两个类来实现

System.Text.Json.Utf8JsonReader
System.Text.Json.Utf8JsonWriter

 

自定义 JSON 名称和值

 

在默认情况下,输出的 JSON 属性名称保持和实体对象相同,包括大小写的都是一致的,枚举类型在默认情况下被序列化为数值类型。System.Text.JSON 提供了一系列的设置和扩展来帮助开发者实现各种自定义的需求。下面的代码可以设置默认的 JSON 属性名称,这个设置和 Newtonsoft.Json 基本一致。

public class UserInfo
{
    [JsonPropertyName("name")] public string Name { get; set; }
public decimal Money { get; set; }
public int Age { get; set; }
public string Remark { get; set; }
}

 

UserInfo 的 属性 Name 在输出为 JSON 的时候,其字段名称将为:name,其他属性保持大小写不变

对所有属性设置为 camel 大小写

var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
jsonSerializer.Serialize(user, options);

 

自定义名称策略

 

比如我们的系统,目前采用全小写的模式,那么我可以自定义一个转换器,并应用到序列化行为中。

public class LowerCaseNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name) => name.ToLower();
}
var options = new JsonSerializerOptions();
// 应用策略
options.PropertyNamingPolicy = new LowerCaseNamingPolicy();
var user = new UserInfo { Name = "Ron", Money = 4.5m, Age = 30};
var json = JsonSerializer.Serialize(user, options);

 

将枚举序列化为名称字符串而不是数值

var options = new JsonSerializerOptions();
// 添加转换器
options.Converters.Add(new JsonStringEnumConverter());
var user = new UserInfo { Name = "Ron", Money = 4.5m, Age = 30;
var json = JsonSerializer.Serialize(user, options);

 

排除不需要序列化的属性

 

在默认情况下,所有公共属性将被序列化为 JSON。但是,如果你不想让某些属性出现在 JSON 中,可以通过下面的几种方式实现属性排除

排除所有属性值为 null 属性

var options = new JsonSerializerOptions();
options.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
options.IgnoreNullValues = true;
var user = new UserInfo { Name = "Ron", Money = 4.5m, Age = 30, Remark =null};
var json = JsonSerializer.Serialize(user, options);
// 输出,可以看到,Remark 属性被排除
{"name":"Ron","Money":4.5,"Age":30}

 

排除指定标记属性

 

可以为某个属性应用 JsonIgnore 特性,标记为不输出到 JSON

public class UserInfo
{
    [JsonPropertyName("name")] public string Name { get; set;}
public decimal Money { get; set; }
    [JsonIgnore]public int Age { get; set; }
public string Remark { get; set; }
}

var user = new UserInfo { Name = "Ron", Money = 4.5m, Age = 30, Remark =null};
var json = JsonSerializer.Serialize(user);

// 输出,属性 Age  已被排除
{"name":"Ron","Money":4.5,"Remark":null}

 

排除所有只读属性

 

还可以选择对所有只读属性进行排查输出 JSON,比如下面的代码,Password 是不需要输出的,那么我们只需要将 Password 设置为 getter,并应用 IgnoreReadOnlyProperties = true 即可

public class UserInfo
{
    [JsonPropertyName("name")] public string Name { get; set;}
public decimal Money { get; set; }
    [JsonIgnore] public int Age { get; set; }
public int Password { get; }
public string Remark { get; set; }
}

var options = new JsonSerializerOptions
{
    IgnoreReadOnlyProperties = true
};
var user = new UserInfo { Name = "Ron", Money = 4.5m, Age = 30, Remark = null };
var json = JsonSerializer.Serialize(user, options);
// 输出
{"name":"Ron","Money":4.5,"Remark":null}

 

排除派生类的属性

 

在某些情况下,由于业务需求的不同,需要实现实体对象的继承,但是在输出 JSON 的时候,希望只输出基类的属性,而不要输出派生类型的属性,以避免产生不可控制的数据泄露问题;那么,我们可以采用下面的序列化设置。比如下面的 UserInfoExtension 派生自 UserInfo,并扩展了一个属性为身份证的属性,在输出 JSON 的时候,我们希望不要序列化派生类,那么我们可以在 Serialize 序列化的时候,指定序列化的类型为基类:UserInfo,即可达到隐藏派生类属性的目的。

public class UserInfo
{
    [JsonPropertyName("name")] public string Name { get; set;}
public decimal Money { get; set; }
    [JsonIgnore] public int Age { get; set; }
public int Password { get; }
public string Remark { get; set; }
}
public class UserInfoExtension : UserInfo
{
public string IdCard { get; set; }
}

var user = new UserInfoExtension { Name = "Ron", Money = 4.5m, Age = 30, Remark = null };
var json = JsonSerializer.Serialize(user, typeof(UserInfo));
// 输出
{"name":"Ron","Money":4.5,"Password":0,"Remark":null}

 

仅输出指定属性(排除属性的逆向操作)

 

在 Newtonsoft.Json 中,我们可以通过指定 MemberSerialization 和 JsonProperty 来实现输出指定属性到 JSON 中,比如下面的代码

[Newtonsoft.Json.JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]
public class UserInfo
{
    [Newtonsoft.Json.JsonProperty("name")] public string Name { get; set; }
public int Age { get; set; }
}
var user = new UserInfo() { Age = 18, Name = "Ron" };
var json = Newtonsoft.Json.JsonConvert.SerializeObject(user);
// 输出
{"name":"Ron"}

 

不过,很遗憾的告诉大家,目前 System.Text.Json 不支持这种方式;为此,我特意去看了 corefx 的 issue,我看到了下面这个反馈

.NET Core 3.1 中的 Json 互操作最全解读-收藏级

现在可以方向了,当 .NETCore 合并到主分支 .NET 也就是 .NET5.0 的时候,官方将提供支持,在此之前,还是使用推荐 Newtonsoft.Json 。

© 版权声明

☆ END ☆
喜欢就点个赞吧
点赞0 分享
1 2 3 4
图片正在生成中,请稍后...