System.Text.Json 中的 JsonExtensionData

System.Text.Json 中的 JsonExtensionData

Intro

最近两天在排查我们 API 的一个问题,查看源码过程中发现 System.Text.Json 里有一个有意思的 JsonExtensionData

在反序列化的时候,如果反序列化的 Model 中没有对应的属性信息,这些信息就会丢失,只会保留 Model 里有的数据,而 JsonExtensionData 则可以将这些没有对应属性的信息也保留下来,在序列化的时候也会保留下来。

Sample

直接来看示例吧:

定义的 Model 如下,这里使用了 C# 9 引入的 record 来简化代码

public record Person(string Name, int Age);

如果我们的 JSON 字符串正好只有这两个属性的话

JsonSerializer.Serialize(new
        {
            Name = "Ming",
            Age = 10,
        });

如果是这样的,那么也不会有什么问题

如果 JSON 字符串会有更多的信息,比如:

JsonSerializer.Serialize(new
        {
            Name = "Ming",
            Age = 10,
            Title = "SDE",
            City = "Shanghai"
        });

可以看到,这个 JSON 会有更多的信息,会包含 Model 里没有定义的 CityTitle

此时在使用上面的 Model 就会出现信息丢失,TitleCity 的信息就会丢掉了,System.Text.Json 提供了一种方式 JsonExtensionData  来保存这些在 Model 里没有定义的属性/字段信息

使用 JsonExtensionData 的属性/字段有类型要求,需要是以下三种类型之一:

  • IDictionary<string, object>

  • IDictionary<string, JsonElement>

  • JsonObject(.NET 6 新增支持)

于是我们就有了下面的测试 Model

public record Person(string Name, int Age);

public record Person1(string Name, int Age) : Person(Name, Age)
{
    [JsonExtensionData]
    public Dictionary<string, object?> Extensions { get; set; } = new();
}

public record Person2(string Name, int Age) : Person(Name, Age)
{
    [JsonExtensionData]
    public Dictionary<string, JsonElement> Extensions { get; set; } = new(StringComparer.OrdinalIgnoreCase);
}

public record Person3(string Name, int Age) : Person(Name, Age)
{
    [JsonExtensionData]
    public JsonObject? Extensions { get; set; }
}

测试代码如下:

var p1 = JsonSerializer.Deserialize<Person1>(jsonString);
ArgumentNullException.ThrowIfNull(p1, nameof(p1));
WriteLine(JsonSerializer.Serialize(p1.Extensions));

var p2 = JsonSerializer.Deserialize<Person2>(jsonString);
ArgumentNullException.ThrowIfNull(p2, nameof(p2));
WriteLine(JsonSerializer.Serialize(p2.Extensions));

var p3 = JsonSerializer.Deserialize<Person3>(jsonString);
ArgumentNullException.ThrowIfNull(p3, nameof(p3));
WriteLine(JsonSerializer.Serialize(p3.Extensions));

输出结果如下:

b917871bd5abf39d12d1705078a3d894.png

可以看到使用了 JsonExtensionData 之后,多余的信息也会保存下来,把 Extensions 打印一下都是一样的结果

Extensions 中保存了我们没有匹配到的信息,这样我们就可以获取到那些可能会丢失掉的数据了

我们可以把整个对象直接打印出来

using static System.Console;

var p = JsonSerializer.Deserialize<Person>(jsonString);
ArgumentNullException.ThrowIfNull(p, nameof(p));
WriteLine(JsonSerializer.Serialize(p));

var p1 = JsonSerializer.Deserialize<Person1>(jsonString);
ArgumentNullException.ThrowIfNull(p1, nameof(p1));
WriteLine(JsonSerializer.Serialize(p1));

var p2 = JsonSerializer.Deserialize<Person2>(jsonString);
ArgumentNullException.ThrowIfNull(p2, nameof(p2));
WriteLine(JsonSerializer.Serialize(p2));

var p3 = JsonSerializer.Deserialize<Person3>(jsonString);
ArgumentNullException.ThrowIfNull(p3, nameof(p3));
WriteLine(JsonSerializer.Serialize(p3));
WriteLine(new string('-', 20));

输出结果如下:

3fc5d074ff9fdaf11da710559f300242.png

output

More

借助 JsonExtensionData 我们可以实现一些比较灵活的扩展,没有用过的童鞋不妨试一下

细心的童鞋可能会发现最后一个输出的结果会有一些不同,这是一个 BUG 可以参考 issue: https://github.com/dotnet/runtime/issues/60806

上述示例可以在 Github 获取 https://github.com/WeihanLi/SamplesInPractice/blob/master/JsonSample/SystemTextJsonSample/JsonExtensionDataSample.cs

References

  • https://github.com/WeihanLi/SamplesInPractice/blob/master/JsonSample/SystemTextJsonSample/JsonExtensionDataSample.cs

  • https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-handle-overflow?WT.mc_id=DT-MVP-5004222

  • https://docs.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonextensiondataattribute?WT.mc_id=DT-MVP-5004222

  • https://github.com/dotnet/runtime/issues/61080

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值