System.Text.Json.JsonElement ToObject解决方法

Sta*_*fos 5 c# .net-core-3.0 system.text.json

我想知道ToObject<>()Json.NET中System.Text.Json 的等效方法。

使用Json.NET,您可以使用任何JToken一个并将其转换为类。例如:

var str = ""; // some json string
var jObj = JObject.Parse(str);
var myClass = jObj["SomeProperty"].ToObject<SomeClass>();
Run Code Online (Sandbox Code Playgroud)

.NET Core 3的新功能我们将如何做到这一点 System.Text.Json

var str = ""; // some json string
var jDoc = JsonDocument.Parse(str);
var myClass = jDoc.RootElement.GetProperty("SomeProperty"). <-- now what??
Run Code Online (Sandbox Code Playgroud)

最初,我以为只是将JsonElement返回的内容jDoc.RootElement.GetPRoperty("SomeProperty")转换为字符串,然后反序列化该字符串。但是我觉得这可能不是最有效的方法,而且我真的找不到以其他方式进行操作的文档。

Gar*_*and 69

我遇到了同样的问题,所以我写了一些现在工作正常的扩展方法。如果他们将其作为内置提供以避免对字符串的额外分配,那就太好了。

public static T ToObject<T>(this JsonElement element)
{
    var json = element.GetRawText();
    return JsonSerializer.Deserialize<T>(json);
}
public static T ToObject<T>(this JsonDocument document)
{
    var json = document.RootElement.GetRawText();
    return JsonSerializer.Deserialize<T>(json);
}
Run Code Online (Sandbox Code Playgroud)

然后使用如下:

jDoc.RootElement.GetProperty("SomeProperty").ToObject<SomeClass>();
Run Code Online (Sandbox Code Playgroud)

  • 是的,尽管它不仅仅涉及字符串分配。更多的是再次解析整个 JSON 的文本表示。创建 JsonElement/Document 时它已经被解析过一次,所以这也浪费了 CPU。 (10认同)
  • 这也正是我最终所做的。我发布了这个问题,看看是否有一种更有效的方法,无需您提到的字符串分配。也许我们必须等到 system.text.json 更加成熟一点。 (4认同)

dbc*_*dbc 51

有一个关于此的开放增强功能,目前针对Future

在此期间,你可能会通过书面形式更好的性能的中间byte缓冲,而不是为一个字符串,因为两者JsonDocumentUtf8JsonReader直接工作,byte跨度,而不是字符串或char跨度。如文档中所述:

序列化为 UTF-8 比使用基于字符串的方法快 5-10%。不同之处在于字节(如 UTF-8)不需要转换为字符串(UTF-16)。

public static partial class JsonExtensions
{
    public static T ToObject<T>(this JsonElement element, JsonSerializerOptions options = null)
    {
        var bufferWriter = new ArrayBufferWriter<byte>();
        using (var writer = new Utf8JsonWriter(bufferWriter))
            element.WriteTo(writer);
        return JsonSerializer.Deserialize<T>(bufferWriter.WrittenSpan, options);
    }

    public static T ToObject<T>(this JsonDocument document, JsonSerializerOptions options = null)
    {
        if (document == null)
            throw new ArgumentNullException(nameof(document));
        return document.RootElement.ToObject<T>(options);
    }       
}
Run Code Online (Sandbox Code Playgroud)

演示小提琴在这里

  • 大约 18 个月后,仍在 dotnet 5 上使用这个答案。在 ```element.WriteTo(writer);``` 之后需要 ```writer.Flush();``` ,否则有时会得到 ```System.Text.Json.JsonException: '预期深度为零在 JSON 有效负载的末尾。有一个打开的 JSON 对象或数组应该关闭。``` (2认同)
  • 啊,我的错。我正在“使用”C# 8.0 形式的 using ... ```using var writer = new Utf8JsonWriter(bufferWriter);``` 因此 ```Dispose``` 仅在函数末尾被调用,这完全改变了代码的行为,需要“Flush”来修复它。几个括号会有多大的不同! (2认同)

hal*_*ldo 10

.NET 6引入了System.Text.Json.Nodes命名空间,它提供了一种使用与 Json.Net 几乎完全相同的语法来执行此操作的方法:

var str = ""; // some json string
var node = JsonNode.Parse(str);
var myClass = node["SomeProperty"].Deserialize<SomeClass>();
Run Code Online (Sandbox Code Playgroud)

命名空间包括 4 个新类型:JsonNodeJsonArrayJsonObject、 和JsonValue可用于访问或修改 DOM 中的值。JsonNode是其他三种类型的基类。

dbc 的答案Deserialize中列出的扩展方法也已添加到 上进行操作,例如:JsonNode

public static TValue? Deserialize<TValue>(this JsonNode? node, JsonSerializerOptions? options = null);
Run Code Online (Sandbox Code Playgroud)

JsonNode不是一次性的,因此您不需要使用using语法。

使用AsObject()or分别AsArray()解析为JsonObjector JsonArray

// parse array
JsonArray arr = JsonNode.Parse(@"[{""Name"": ""Bob"", ""Age"":30}]").AsArray();
// parse object
JsonObject obj = JsonNode.Parse(@"{""Name"": ""Bob"", ""Age"":30}").AsObject();
// get a value
var date = JsonNode.Parse(@"{""Date"":""2021-12-21T13:24:46+04:00""}")["Date"].GetValue<DateTimeOffset>();
Run Code Online (Sandbox Code Playgroud)

一旦 json 被解析,就可以导航、过滤和转换 DOM 和/或应用于Deserialize<T>()映射到您的具体类型。

要序列化回 json 字符串,您可以使用ToJsonString(),例如:

string innerNodeJson = node["SomeProperty"].ToJsonString();
Run Code Online (Sandbox Code Playgroud)

请参阅Equivalent of JObject in System.Text.Json的答案,了解有关 的更多详细信息JsonObject


Pau*_*aul 9

dbc's answer相同,只包括允许您通过Type returnType.

public static partial class JsonExtensions
{
    public static T ToObject<T>(this JsonElement element, JsonSerializerOptions options = null)
    {
        var bufferWriter = new ArrayBufferWriter<byte>();
        using (var writer = new Utf8JsonWriter(bufferWriter))
        {
            element.WriteTo(writer);
        }

        return JsonSerializer.Deserialize<T>(bufferWriter.WrittenSpan, options);
    }

    public static T ToObject<T>(this JsonDocument document, JsonSerializerOptions options = null)
    {
        if (document == null)
        {
            throw new ArgumentNullException(nameof(document));
        }

        return document.RootElement.ToObject<T>(options);
    }       

    public static object ToObject(this JsonElement element, Type returnType, JsonSerializerOptions options = null)
    {
        var bufferWriter = new ArrayBufferWriter<byte>();
        using (var writer = new Utf8JsonWriter(bufferWriter))
        {
            element.WriteTo(writer);
        }

        return JsonSerializer.Deserialize(bufferWriter.WrittenSpan, returnType, options);
    }

    public static object ToObject(this JsonDocument document, Type returnType, JsonSerializerOptions options = null)
    {
        if (document == null)
        {
            throw new ArgumentNullException(nameof(document));
        }

        return document.RootElement.ToObject(returnType, options);
    }       
}
Run Code Online (Sandbox Code Playgroud)

  • 序列化回文本似乎不是一种有效的方法 (2认同)