如何让 System.Text.Json 将对象反序列化为其原始类型?

Posted

技术标签:

【中文标题】如何让 System.Text.Json 将对象反序列化为其原始类型?【英文标题】:How do I get System.Text.Json to deserialize objects into their original type? 【发布时间】:2021-10-01 19:25:31 【问题描述】:

我正在使用 .NET 5.0 中的 POCO 类,我需要对浏览器进行序列化和反序列化,这是在一个库中,我不想告诉用户他们必须使用哪些序列化程序使用。

所以我试图让它与 Newtonsoft.Json 和 System.Text.Json 一起工作。

我的问题是存储在类型为 object 的字段中的值没有被反序列化为其原始类型,在 System.Text.Json 中。

using Shouldly;
using Xunit;
using SJ = System.Text.Json;
using NJ = Newtonsoft.Json;

public class SerializationTests

    [Fact]
    public void TestNJ()
    
        var nvp = new NVP  Name = "a name", Value = "a value" ;

        var s = NJ.JsonConvert.SerializeObject(nvp);
        var d = NJ.JsonConvert.DeserializeObject<NVP>(s);
        d.ShouldBeEquivalentTo(nvp);
    

    [Fact]
    public void TestSJ()
    
        var nvp = new NVP  Name = "a name", Value = "a value" ;

        var s = SJ.JsonSerializer.Serialize(nvp);
        var d = SJ.JsonSerializer.Deserialize<NVP>(s);
        d.ShouldBeEquivalentTo(nvp);
    

TestNJ 通过。

TestSJ 抛出异常:

预期值为 System.String,但为 System.Text.Json.JsonElement'

在调试器中很清楚发生了什么。

在 TestNJ() 和 TestSJ() 的开头,nvp 的值是:

nvp: NVP
  Name [string]: "a name"
  Value [object]: " a value"

在序列化之后,s 具有我期望的值:

"\"Name\":\"a name\",\"Value\":\"a value\""

但是在TestNJ()中,反序列化后d的值是:

d: NVP
  Name [string]: "a name"
  Value [object]: " a value"

在 TestSJ() 中,d 的值为:

d: NVP
  Name [string]: "a name"
  Value [object]: ValueKind = String: "a value"

这里发生了什么?如何让 System.Text.Json 真正反序列化这种类型?

【问题讨论】:

在我看来这个 Github 问题是一样的。 deserialize object prop adds Valuekind to value #31408 您可以将[JsonConverter(typeof(ObjectAsPrimitiveConverter))] 应用到Value,其中ObjectAsPrimitiveConverter 来自this answer 到C# - Deserializing nested json to nested Dictionary<string, object>。 虽然DateTime 对你没有帮助。 JSON 没有日期和时间的原始类型,因此它们只是被序列化为字符串。 Json.NET 和DataContractSerializer 具有尝试推断字符串是否为日期的逻辑——但最好不要依赖这种逻辑,因为用户输入的字符串可能看起来像DateTime 字符串。所以你的 DTO 解决方案看起来更好。 【参考方案1】:

丹尼斯指向https://github.com/dotnet/runtime/issues/31408 并且作为 https://github.com/dotnet/runtime/issues/29960 的副本被关闭。

所以答案是,你没有。

来自 GitHub 上的讨论:

这是设计使然(不太可能改变)。由于对象类型可以将 JSON 有效负载映射到任何 .NET 类型,因此在反序列化时,JsonSerializer 返回一个装箱的 JsonElement,而不是指向 JSON 令牌本身。在反序列化期间,我们不会尝试从 JSON 有效负载推断或猜测 .NET 类型,因此,调用者(具有足够上下文)需要将返回的 JsonElement 转换为预期的 .NET 类型。

如果您正在尝试我正在尝试做的事情,请在实用程序库中创建一个 POCO 类,该类可以由库的客户端进行序列化和反序列化,而不需要对库的用户进行任何特别的努力,不要使用对象。

在我的例子中,我有一个可能包含字符串、int、double、decimal 或 datetime 的字段。我将不得不将其替换为每个单独的可为空字段。

【讨论】:

我已经尝试了两种方法,使用单个类型的字段和使用每个支持类型的单个可空字段。我发现即使有单独的字段,我仍然需要自定义转换器。使用单独字段的问题在于,使用这些值的代码需要了解所有单独的字段。

以上是关于如何让 System.Text.Json 将对象反序列化为其原始类型?的主要内容,如果未能解决你的问题,请参考以下文章

System.Text.Json - 将嵌套对象反序列化为字符串

如何反序列化作为 System.Text.Json 中的字符串的嵌套 JSON 对象?

System.Text.Json 反序列化来自 API 调用的嵌套对象 - 数据包装在父 JSON 属性中

将 false 反序列化为 null (System.Text.Json)

如何使用 System.Text.Json 将 json 字符串或流反序列化到 Dictionary<string,string>

使用 system.text.json 序列化和反序列化流