解决方案

我想知道 Json.NET系统,短信,杰森ToObject<>()方法的等价物。

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

var str = ""; // Some JSON string
var jObj = JObject.Parse(str);
var myClass = jObj["SomeProperty"].ToObject<SomeClass>();

我们如何能够做到这一点与.NET 核心3的新 System.Text.Json

var str = ""; // Some JSON string
var jDoc = JsonDocument.Parse(str);
var myClass = jDoc.RootElement.GetProperty("SomeProperty"). <-- now what??

最初,我想我只需要将 jDoc.RootElement.GetPRoperty("SomeProperty")中返回的 JsonElement转换成一个字符串,然后反序列化该字符串。但是我觉得这可能不是最有效的方法,而且我实在找不到关于另一种方法的文档。

75645 次浏览

I came across the same issue, so I wrote some extension methods which work fine for now. It would be nice if they provided this as built in to avoid the additional allocation to a string.

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);
}

Then use as follows:

jDoc.RootElement.GetProperty("SomeProperty").ToObject<SomeClass>();

In .NET 6, extension methods are being added to JsonSerializer to deserialize an object directly from a JsonElement or JsonDocument:

public static partial class JsonSerializer
{
public static TValue? Deserialize<TValue>(this JsonDocument document, JsonSerializerOptions? options = null);
public static object? Deserialize(this JsonDocument document, Type returnType, JsonSerializerOptions? options = null);
public static TValue? Deserialize<TValue>(this JsonDocument document, JsonTypeInfo<TValue> jsonTypeInfo);
public static object? Deserialize(this JsonDocument document, Type returnType, JsonSerializerContext context);


public static TValue? Deserialize<TValue>(this JsonElement element, JsonSerializerOptions? options = null);
public static object? Deserialize(this JsonElement element, Type returnType, JsonSerializerOptions? options = null);
public static TValue? Deserialize<TValue>(this JsonElement element, JsonTypeInfo<TValue> jsonTypeInfo);
public static object? Deserialize(this JsonElement element, Type returnType, JsonSerializerContext context);
}

Now you will be able to do:

using var jDoc = JsonDocument.Parse(str);
var myClass = jDoc.RootElement.GetProperty("SomeProperty").Deserialize<SomeClass>();

Notes:

  • JsonDocument is disposable. According to the documentation, This class utilizes resources from pooled memory... failure to properly dispose this object will result in the memory not being returned to the pool, which will increase GC impact across various parts of the framework.

    So, be sure to declare your jDoc with a using statement.

  • The new methods should be present in .NET 6.0 Preview RC1.

    They were added in response to the enhancement request We should be able serialize and serialize from DOM #31274, which has been closed.

  • Similar extension methods were added for the new JsonNode mutable JSON document node as well

    public static TValue? Deserialize<TValue>(this JsonNode? node, JsonSerializerOptions? options = null)
    public static object? Deserialize(this JsonNode? node, Type returnType, JsonSerializerOptions? options = null)
    public static TValue? Deserialize<TValue>(this JsonNode? node, JsonTypeInfo<TValue> jsonTypeInfo)
    public static object? Deserialize(this JsonNode? node, Type returnType, JsonSerializerContext context)
    

In .NET 5 and earlier these methods do not exist. As a workaround, you may get better performance by writing to an intermediate byte buffer rather than to a string, since both JsonDocument and Utf8JsonReader work directly with byte spans rather than strings or char spans. As stated in the documentation:

Serializing to UTF-8 is about 5-10% faster than using the string-based methods. The difference is because the bytes (as UTF-8) don't need to be converted to strings (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);
}
}

A demo fiddle is here.

Same as dbc's answer, just including the methods which allow you to specify a return type via 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);
}
}

.NET 6 introduced the System.Text.Json.Nodes namespace, which provides a way to do this using almost exactly the same syntax as Json.Net:

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

The namespace includes 4 new types: JsonNode, JsonArray, JsonObject, and JsonValue which can be used to access or modify values within the DOM. JsonNode is the base class for the other three types.

The Deserialize extension methods listed in dbc's answer have also been added to operate on JsonNode, eg:

public static TValue? Deserialize<TValue>(this JsonNode? node, JsonSerializerOptions? options = null);

JsonNode is not disposable so you do not need to use the using syntax.

Use AsObject() or AsArray() to parse to a JsonObject or JsonArray, respectively:

// 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>();

Once the json has been parsed it's possible to navigate, filter and transform the DOM and/or apply Deserialize<T>() to map to your concrete type.

To serialize back to a json string you can use ToJsonString(), eg:

string innerNodeJson = node["SomeProperty"].ToJsonString();

Please see this answer to Equivalent of JObject in System.Text.Json for more details information about JsonObject.