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

在反序列化的时候,允许 JSON 文本包含注释

 

默认情况下,System.Text.JSON 不支持源 JSON 文本包含注释,比如下面的代码,当你不使用 ReadCommentHandling = JsonCommentHandling.Skip 的设置的时候,将抛出异常,因为在字段 Age 的后面有注释 /* age */。

var jsonText = "{"Name":"Ron","Money":4.5,"Age":30/* age */}";
var options = new JsonSerializerOptions
{
    ReadCommentHandling = JsonCommentHandling.Skip,
    AllowTrailingCommas = true,
};
var user = JsonSerializer.Deserialize<UserInfoExtension>(jsonText);

 

允许字段溢出

 

接口数据出现变动时,极有可能出现源 JSON 文本和实体对象属性不匹配的问题,JSON 中可能会多出一些实体对象不存在的属性,这种情况我们称之为“溢出”,在默认情况下,溢出的属性将被忽略,如果希望捕获这些“溢出”的属性,可以在实体对象中声明一个类型为:Dictionary<string, object> 的属性,并对其应用特性标记:JsonExtensionData。

为了演示这种特殊的处理,我们声明了一个实体对象 UserInfo,并构造了一个 JSON 源,该 JSON 源包含了一个 UserInfo 不存在的属性:Money,预期该 Money 属性将被反序列化到属性 ExtensionData 中。

public class UserInfo
{
public string Name { get; set; }
public int Age { get; set; }
    [JsonExtensionData] public Dictionary<string, object> ExtensionData { get; set; }
}
var jsonText = "{"Name":"Ron","Money":4.5,"Age":30}";
var user = JsonSerializer.Deserialize<UserInfo>(jsonText);

 

输出截图

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

有意思的是,被特性 JsonExtensionData 标记的属性,在序列化为 JSON 的时候,他又会将 ExtensionData 的字典都序列化为单个 JSON 的属性,这里不再演示,留给大家去体验。

转换器

 

System.Text.Json 内置了各种丰富的类型转换器,这些默认的转换器在程序初始化 JsonSerializerOptions 的时候就默认加载,在 JsonSerializerOptions 内部,维护着一个私有静态成员 s_defaultSimpleConverters,同时还有一个公有属性 Converters ,Converters 属性在 JsonSerializerOptions 的构造函数中被初始化;从下面的代码中可以看到,默认转换器集合和公有转换器集是相互独立的,System.Text.Json 允许开发人员通过 Converters 添加自定义的转换器。

public sealed partial class JsonSerializerOptions
{
// The global list of built-in simple converters.
private static readonly Dictionary<Type, JsonConverter> s_defaultSimpleConverters = GetDefaultSimpleConverters();
// The global list of built-in converters that override CanConvert().
private static readonly List<JsonConverter> s_defaultFactoryConverters = GetDefaultConverters();
// The cached converters (custom or built-in).
private readonly ConcurrentDictionary<Type, JsonConverter> _converters = new ConcurrentDictionary<Type, JsonConverter>();
private static Dictionary<Type, JsonConverter> GetDefaultSimpleConverters()
{
           ...
    }
private static List<JsonConverter> GetDefaultConverters()
{
           ...
        }
public IList<JsonConverter> Converters { get; }
        ...
}

 

内置转换器

 

在 System.Text.Json 内置的转换器集合中,涵盖了所有的基础数据类型,这些转换器的设计非常精妙,他们通过注册一系列的类型映射,在通过 Utf8JsonWriter/Utf8JsonReader 的内置方法 GetTypeValue/TryGetTypeValue 方法得到值,代码非常精练,复用性非常高,下面是内置类型转换器。

private static IEnumerable<JsonConverter> DefaultSimpleConverters
{
    get
    {
// When adding to this, update NumberOfSimpleConverters above.
yield return new JsonConverterBoolean();
yield return new JsonConverterByte();
yield return new JsonConverterByteArray();
yield return new JsonConverterChar();
yield return new JsonConverterDateTime();
yield return new JsonConverterDateTimeOffset();
yield return new JsonConverterDouble();
yield return new JsonConverterDecimal();
yield return new JsonConverterGuid();
yield return new JsonConverterInt16();
yield return new JsonConverterInt32();
yield return new JsonConverterInt64();
yield return new JsonConverterJsonElement();
yield return new JsonConverterObject();
yield return new JsonConverterSByte();
yield return new JsonConverterSingle();
yield return new JsonConverterString();
yield return new JsonConverterUInt16();
yield return new JsonConverterUInt32();
yield return new JsonConverterUInt64();
yield return new JsonConverterUri();
    }
}

 

自定义类型转换器

 

虽然 System.Text.Json 内置了各种各样丰富的类型转换器,但是在各种业务开发的过程中,总会根据业务需求来决定一些特殊的数据类型的数据,下面,我们就以经典的日期/时间转换作为演示场景。

我们需要将日期类型输出为 Unix 时间戳而不是格式化的日期内容,为此,我们将实现一个自定义的时间格式转换器,该转换器继承自 JsonConverter。

public class JsonConverterUnixDateTime : JsonConverter<DateTime>
{
private static DateTime Greenwich_Mean_Time = TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1), TimeZoneInfo.Local);
private const int Limit = 10000;
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Number)
        {
            var unixTime = reader.GetInt64();
            var dt = new DateTime(Greenwich_Mean_Time.Ticks + unixTime * Limit);
return dt;
        }
els
        {
return reader.GetDateTime();
        }
    }
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
        var unixTime = (value - Greenwich_Mean_Time).Ticks / Limit;
        writer.WriteNumberValue(unixTime);
    }
}

 

应用自定义的时间转换器

 

转换器的应用形式有两种,分别是将转换加入 JsonSerializerOptions.Converters 和给需要转换的属性添加特性标记 JsonConverter

加入 Converters 方式

var options = new JsonSerializerOptions();
options.Converters.Add(new JsonConverterUnixDateTime());
var user = new UserInfo() { Age = 30, Name = "Ron", LoginTime = DateTime.Now };
var json = JsonSerializer.Serialize(user, options);
var deUser = JsonSerializer.Deserialize<UserInfo>(json, options);

// JSON 输出
{"Name":"Ron","Age":30,"LoginTime":1577655080422}

 

应用 JsonConverter 特性方式

public class UserInfo
{
public string Name { get; set; }
public int Age { get; set; }
    [JsonConverter(typeof(JsonConverterUnixDateTime))]
public DateTime LoginTime { get; set; }
}
var user = new UserInfo() { Age = 30, Name = "Ron", LoginTime = DateTime.Now };
var json = JsonSerializer.Serialize(user);
var deUser = JsonSerializer.Deserialize<UserInfo>(json);
// JSON 输出
{"Name":"Ron","Age":30,"LoginTime":1577655080422}

 

注意上面的 UserInfo.LoginTime 的特性标记,当你想小范围的对某些属性单独应用转换器的时候,这种方式费用小巧而有效。

结束语

 

本文全面介绍了 System.Text.Json 在各种场景下的用法并比较和 Newtonsoft.Json 使用上的不同,也通过实例演示具体的使用方法,进一步深入讲解了 System.Text.Json 各种对象的原理,希望对大家在迁移到.NETCore-3.1 的时候有所帮助。 


转自:Ron.Liang
cnblogs.com/viter/p/12116640.html

© 版权声明

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