将对象转换为 System.Text.Json.JsonElement
Convert object to System.Text.Json.JsonElement
假设我有一个类型的对象:
public class MyClass
{
public string Data { get; set; }
}
我需要将其转换为 System.Text.Json.JsonElement。我找到的唯一方法是:
var json = JsonSerializer.Serialize(new MyClass { Data = "value" });
using var document = JsonDocument.Parse(json);
var jsonElement = document.RootElement;
似乎很奇怪,我必须先序列化它然后再解析它。有更好的方法吗?
之前我使用的是 Newtonsoft.Json 中的 JObject
,我可以这样做:
var jobject = JObject.FromObject(new MyClass { Data = "value" });
在 .NET 6 中 方法被添加到 JsonSerializer
以将对象直接序列化为 JsonElement
or JsonDocument
:
public static partial class JsonSerializer
{
public static JsonDocument SerializeToDocument<TValue>(TValue value, JsonSerializerOptions? options = null);
public static JsonDocument SerializeToDocument(object? value, Type inputType, JsonSerializerOptions? options = null);
public static JsonDocument SerializeToDocument<TValue>(TValue value, JsonTypeInfo<TValue> jsonTypeInfo);
public static JsonDocument SerializeToDocument(object? value, Type inputType, JsonSerializerContext context);
public static JsonElement SerializeToElement<TValue>(TValue value, JsonSerializerOptions? options = null);
public static JsonElement SerializeToElement(object? value, Type inputType, JsonSerializerOptions? options = null);
public static JsonElement SerializeToElement<TValue>(TValue value, JsonTypeInfo<TValue> jsonTypeInfo);
public static JsonElement SerializeToElement(object? value, Type inputType, JsonSerializerContext context);
}
因此在 .NET 6 中你将能够做到:
using var jsonDocument = JsonSerializer.SerializeToDocument(new MyClass { Data = "value" });
或
var jsonElement = JsonSerializer.SerializeToElement(new MyClass { Data = "value" });
备注:
JsonSerializerContext
and JsonTypeInfo<T>
are newly exposed in .NET 6 and provide metadata about a set of types, or a single type T
, that is relevant to JSON serialization. They are used when serializing using metadata and code generated at compile time. See Try the new System.Text.Json source generator 了解详情。
JsonDocument
是IDisposable
,实际上必须要处理掉,因为根据docs:
JsonDocument
builds an in-memory view of the data into a pooled buffer. Therefore, unlike JObject
or JArray
from Newtonsoft.Json, the JsonDocument
type implements IDisposable
and needs to be used inside a using
block.
在您的示例代码中,您没有处理 JsonDocument.Parse()
返回的文档,但您应该处理。
新方法应该出现在 .NET 6 RC1。
在 .NET 5 和更早版本中 等同于 JObject.FromObject()
的方法目前在 System.Text.Json
中不可用。关于此有一个开放的增强功能,目前针对 Future:
在此期间,您可以通过序列化为中间 byte
数组而不是字符串来获得更好的性能,因为 JsonDocument
和 Utf8JsonReader
都直接与 [=33= 一起工作] 跨度而不是字符串或 char
跨度,如下所示:
public static partial class JsonExtensions
{
public static JsonDocument JsonDocumentFromObject<TValue>(TValue value, JsonSerializerOptions options = default)
=> JsonDocumentFromObject(value, typeof(TValue), options);
public static JsonDocument JsonDocumentFromObject(object value, Type type, JsonSerializerOptions options = default)
{
var bytes = JsonSerializer.SerializeToUtf8Bytes(value, type, options);
return JsonDocument.Parse(bytes);
}
public static JsonElement JsonElementFromObject<TValue>(TValue value, JsonSerializerOptions options = default)
=> JsonElementFromObject(value, typeof(TValue), options);
public static JsonElement JsonElementFromObject(object value, Type type, JsonSerializerOptions options = default)
{
using var doc = JsonDocumentFromObject(value, type, options);
return doc.RootElement.Clone();
}
}
然后这样称呼它:
using var doc = JsonExtensions.JsonDocumentFromObject(new MyClass { Data = "value" });
或者,如果您需要在 using
语句的范围之外使用根元素:
var element = JsonExtensions.JsonElementFromObject(new MyClass { Data = "value" });
备注:
如上所述,JsonDocument
创建后需要释放。
上述 JsonExtensions.JsonElementFromObject()
扩展方法正确处理了它们的内部文档和 returns 根元素的克隆,如 documentation.
中所推荐
序列化为中间 Utf8 字节序列可能比序列化为 string
更高效,因为根据 docs:
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).
反演方法见System.Text.Json.JsonElement ToObject workaround.
演示 fiddle here.
dbc 的回答是一个好的开始,但如果对象值已经是 json 字符串,则还不够!
而且,他的代码中并没有使用该类型。
因此我提出以下改进版本:
public static JsonDocument JsonDocumentFromObject(object value, JsonSerializerOptions options = null)
{
if (value is string valueStr)
{
try { return JsonDocument.Parse(valueStr); }
catch {}
}
byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(value, options);
return JsonDocument.Parse(bytes);
}
public static JsonElement JsonElementFromObject(object value, JsonSerializerOptions options = null)
{
JsonElement result;
using (JsonDocument doc = JsonDocumentFromObject(value, options))
{
result = doc.RootElement.Clone();
}
return result;
}
使用以下单元测试 (xUnit):
[Fact()]
public void JsonElementFromObjectTest()
{
object o = new
{
id = "myId",
timestamp = DateTime.UtcNow,
valid = true,
seq = 1
};
JsonElement element1 = JsonSerializerExtension.JsonElementFromObject(o);
Assert.Equal(JsonValueKind.Object, element1.ValueKind);
string oStr1 = element1.GetRawText();
Assert.NotNull(oStr1);
JsonElement element2 = JsonSerializerExtension.JsonElementFromObject(oStr1);
Assert.Equal(JsonValueKind.Object, element2.ValueKind);
string oStr2 = element2.GetRawText();
Assert.NotNull(oStr2);
Assert.Equal(oStr1, oStr2);
}
如果没有直接尝试解析,element2 是一个 JsonValueKind.String 并且 oStr2 包含未转义的 unicode 字符,因此是一个无效的 Json 字符串。
.NET 5 中的巧妙方法是:
private JsonElement JsonElementFromObject(object value)
{
var jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(value, new JsonSerializerOptions());
using var doc = JsonDocument.Parse(jsonUtf8Bytes);
return doc.RootElement.Clone();
}
步骤:
- 将值转换为 JSON 字符串,编码为 UTF-8 字节 (SerializeToUtf8Bytes)。
- 解析 JSON 字符串 (JsonDocument.Parse)。
- Return 根元素。
假设我有一个类型的对象:
public class MyClass
{
public string Data { get; set; }
}
我需要将其转换为 System.Text.Json.JsonElement。我找到的唯一方法是:
var json = JsonSerializer.Serialize(new MyClass { Data = "value" });
using var document = JsonDocument.Parse(json);
var jsonElement = document.RootElement;
似乎很奇怪,我必须先序列化它然后再解析它。有更好的方法吗?
之前我使用的是 Newtonsoft.Json 中的 JObject
,我可以这样做:
var jobject = JObject.FromObject(new MyClass { Data = "value" });
在 .NET 6 中 方法被添加到 JsonSerializer
以将对象直接序列化为 JsonElement
or JsonDocument
:
public static partial class JsonSerializer
{
public static JsonDocument SerializeToDocument<TValue>(TValue value, JsonSerializerOptions? options = null);
public static JsonDocument SerializeToDocument(object? value, Type inputType, JsonSerializerOptions? options = null);
public static JsonDocument SerializeToDocument<TValue>(TValue value, JsonTypeInfo<TValue> jsonTypeInfo);
public static JsonDocument SerializeToDocument(object? value, Type inputType, JsonSerializerContext context);
public static JsonElement SerializeToElement<TValue>(TValue value, JsonSerializerOptions? options = null);
public static JsonElement SerializeToElement(object? value, Type inputType, JsonSerializerOptions? options = null);
public static JsonElement SerializeToElement<TValue>(TValue value, JsonTypeInfo<TValue> jsonTypeInfo);
public static JsonElement SerializeToElement(object? value, Type inputType, JsonSerializerContext context);
}
因此在 .NET 6 中你将能够做到:
using var jsonDocument = JsonSerializer.SerializeToDocument(new MyClass { Data = "value" });
或
var jsonElement = JsonSerializer.SerializeToElement(new MyClass { Data = "value" });
备注:
JsonSerializerContext
andJsonTypeInfo<T>
are newly exposed in .NET 6 and provide metadata about a set of types, or a single typeT
, that is relevant to JSON serialization. They are used when serializing using metadata and code generated at compile time. See Try the new System.Text.Json source generator 了解详情。JsonDocument
是IDisposable
,实际上必须要处理掉,因为根据docs:JsonDocument
builds an in-memory view of the data into a pooled buffer. Therefore, unlikeJObject
orJArray
from Newtonsoft.Json, theJsonDocument
type implementsIDisposable
and needs to be used inside ausing
block.在您的示例代码中,您没有处理
JsonDocument.Parse()
返回的文档,但您应该处理。新方法应该出现在 .NET 6 RC1。
在 .NET 5 和更早版本中 等同于 JObject.FromObject()
的方法目前在 System.Text.Json
中不可用。关于此有一个开放的增强功能,目前针对 Future:
在此期间,您可以通过序列化为中间 byte
数组而不是字符串来获得更好的性能,因为 JsonDocument
和 Utf8JsonReader
都直接与 [=33= 一起工作] 跨度而不是字符串或 char
跨度,如下所示:
public static partial class JsonExtensions
{
public static JsonDocument JsonDocumentFromObject<TValue>(TValue value, JsonSerializerOptions options = default)
=> JsonDocumentFromObject(value, typeof(TValue), options);
public static JsonDocument JsonDocumentFromObject(object value, Type type, JsonSerializerOptions options = default)
{
var bytes = JsonSerializer.SerializeToUtf8Bytes(value, type, options);
return JsonDocument.Parse(bytes);
}
public static JsonElement JsonElementFromObject<TValue>(TValue value, JsonSerializerOptions options = default)
=> JsonElementFromObject(value, typeof(TValue), options);
public static JsonElement JsonElementFromObject(object value, Type type, JsonSerializerOptions options = default)
{
using var doc = JsonDocumentFromObject(value, type, options);
return doc.RootElement.Clone();
}
}
然后这样称呼它:
using var doc = JsonExtensions.JsonDocumentFromObject(new MyClass { Data = "value" });
或者,如果您需要在 using
语句的范围之外使用根元素:
var element = JsonExtensions.JsonElementFromObject(new MyClass { Data = "value" });
备注:
如上所述,
中所推荐JsonDocument
创建后需要释放。 上述JsonExtensions.JsonElementFromObject()
扩展方法正确处理了它们的内部文档和 returns 根元素的克隆,如 documentation.序列化为中间 Utf8 字节序列可能比序列化为
string
更高效,因为根据 docs: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).
反演方法见System.Text.Json.JsonElement ToObject workaround.
演示 fiddle here.
dbc 的回答是一个好的开始,但如果对象值已经是 json 字符串,则还不够! 而且,他的代码中并没有使用该类型。
因此我提出以下改进版本:
public static JsonDocument JsonDocumentFromObject(object value, JsonSerializerOptions options = null)
{
if (value is string valueStr)
{
try { return JsonDocument.Parse(valueStr); }
catch {}
}
byte[] bytes = JsonSerializer.SerializeToUtf8Bytes(value, options);
return JsonDocument.Parse(bytes);
}
public static JsonElement JsonElementFromObject(object value, JsonSerializerOptions options = null)
{
JsonElement result;
using (JsonDocument doc = JsonDocumentFromObject(value, options))
{
result = doc.RootElement.Clone();
}
return result;
}
使用以下单元测试 (xUnit):
[Fact()]
public void JsonElementFromObjectTest()
{
object o = new
{
id = "myId",
timestamp = DateTime.UtcNow,
valid = true,
seq = 1
};
JsonElement element1 = JsonSerializerExtension.JsonElementFromObject(o);
Assert.Equal(JsonValueKind.Object, element1.ValueKind);
string oStr1 = element1.GetRawText();
Assert.NotNull(oStr1);
JsonElement element2 = JsonSerializerExtension.JsonElementFromObject(oStr1);
Assert.Equal(JsonValueKind.Object, element2.ValueKind);
string oStr2 = element2.GetRawText();
Assert.NotNull(oStr2);
Assert.Equal(oStr1, oStr2);
}
如果没有直接尝试解析,element2 是一个 JsonValueKind.String 并且 oStr2 包含未转义的 unicode 字符,因此是一个无效的 Json 字符串。
.NET 5 中的巧妙方法是:
private JsonElement JsonElementFromObject(object value)
{
var jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(value, new JsonSerializerOptions());
using var doc = JsonDocument.Parse(jsonUtf8Bytes);
return doc.RootElement.Clone();
}
步骤:
- 将值转换为 JSON 字符串,编码为 UTF-8 字节 (SerializeToUtf8Bytes)。
- 解析 JSON 字符串 (JsonDocument.Parse)。
- Return 根元素。