System.Text.Json 中 JObject 的等效项

LP1*_*P13 11 c# .net-5 system.text.json

我有一个属性类型为 的 DTO 类JObject。此 DTO 类在多个服务之间通过 HTTP 发送/接收。使用 JObject 是因为ExtractedData它没有预定义的属性

public class MyDTO
{
    public JObject ExtractedData {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

我正在将此项目转换为 .NET 5。什么相当于 .NET 5 中的 JObject?我试图避免 JsonDocument 因为(来自文档):

JsonDocument 将数据的内存视图构建到池化缓冲区中。因此,与 Newtonsoft.Json 中的 JObject 或 JArray 不同,JsonDocument 类型实现 IDisposable 并且需要在 using 块中使用。

我打算使用JsonElement. 这是最合适的选择还是有任何其他类型可用于将 JSON 作为对象保存?

hal*_*ldo 102

自 2021 年 11 月起,.NET 6引入了System.Text.Json.Nodes命名空间,其中:

提供用于处理内存中可写文档对象模型 (DOM) 的类型,以便随机访问数据结构化视图中的 JSON 元素

这四种新类型是JsonArrayJsonObjectJsonNodeJsonValue。最接近的类型JObjectJsonObject提供类似功能的类型。

请参阅下面的一些示例:

// create object manually using initializer syntax
JsonObject obj = new JsonObject
{
    ["Id"] = 3,
    ["Name"] = "Bob",
    ["DOB"] = new DateTime(2001, 02, 03),
    ["Friends"] = new JsonArray
    {
        new JsonObject 
        {
            ["Id"] = 2,
            ["Name"] = "Smith"
        },
        new JsonObject
        {
            ["Id"] = 4,
            ["Name"] = "Jones"
        } 
    }
};

// random access to values
int id = (int)obj["Id"];
DateTime dob = (DateTime)obj["DOB"];
string firstFriendName = (string)obj["Friends"][0]["Name"];
Run Code Online (Sandbox Code Playgroud)

下面列出了一些其他很酷的东西,现在使使用System.Text.Json变得更加容易.NET6

解析、创建和 DOM 操作

// parse
var jsonObj = JsonNode.Parse(jsonString).AsObject();
Run Code Online (Sandbox Code Playgroud)

如果你有一个JsonElement(可能在反序列化为dynamicobject或 后JsonElement)你可以调用Create,现在你就有了一个可导航且可写的 DOM 对象:

// create
JsonObject obj = JsonObject.Create(jsonElement);
Run Code Online (Sandbox Code Playgroud)

您可以添加/删除属性:

obj.Add("FullName", "Bob Smith");
bool successfullyRemoved = obj.Remove("Name");
Run Code Online (Sandbox Code Playgroud)

ContainsKey使用and TryGetPropertyValue(返回 a )安全地询问对象的特定键JsonNode

if (obj.ContainsKey("Hobbies"))
    // do stuff

if (obj.TryGetPropertyValue("Hobbies", out JsonNode? node))
    // do stuff with node
Run Code Online (Sandbox Code Playgroud)

项目和过滤数据

可以使用 Linq 来投影和过滤JsonObject

// select Keys
List<string> keys = obj.Select(node => node.Key).ToList();
// filter friends
var friends = obj["Friends"].AsArray()
             .Where(n => (int)n.AsObject()["Id"] > 2);
Run Code Online (Sandbox Code Playgroud)

反序列化Json

现在可以轻松反序列化 Json 或反序列化 Json 的一部分。当我们只想从主对象反序列化部分 Json 时,这很有用。对于上面的示例,我们可以轻松地将好友列表反序列化为泛型List<Friend>

List<Friend> friends = obj["Friends"].AsArray().Deserialize<List<Friend>>();
Run Code Online (Sandbox Code Playgroud)

其中Deserilize<T>()是 上的扩展方法JsonNode

连载

JsonObject使用以下命令可以轻松序列化ToJsonString()

string s = obj.ToJsonString();

// write pretty json with WriteIndented
string s = obj.ToJsonString(new JsonSerializerOptions { WriteIndented = true }));
Run Code Online (Sandbox Code Playgroud)

JsonObject在您的特定情况下,您可以在 DTO 中定义:

public class MyDTO
{
    public JsonObject ExtractedData {get;set;}
}
Run Code Online (Sandbox Code Playgroud)


dbc*_*dbc 6

最接近的等价物JObject确实是JsonElement这样,您可以按如下方式修改您的 DTO:

public class MyDTO
{
    public JsonElement ExtractedData {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

无需担心处理任何文档,因为在内部,JsonElementConverterused byJsonSerializer返回非池化元素(通过在 .NET 5 中克隆该元素)。

但是,对应关系并不准确,因此请记住以下几点:

  1. JsonElement表示任何 JSON 值,因此最接近于JTokennot JObject。由于JsonElementstruct不存在对应于JSON对象没有子类。如果要限制ExtractedData为 JSON 对象,则需要在 setter 中进行检查:

    public class MyDTO
    {
        JsonElement extractedData;
    
        public JsonElement ExtractedData
        {
            get => extractedData;
            set
            {
                if (value.ValueKind != JsonValueKind.Object
                    // && value.ValueKind != JsonValueKind.Null Uncomment if you want to allow null
                    )
                    throw new ArgumentException(string.Format("{0} is not a JSON object type", value.ValueKind));
                extractedData = value;
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 由于JsonElement是结构,因此默认值不是null。那么,它是什么?事实证明,default(JsonElement)ValueKind = JsonValueKind.Undefined

    没有值(与 Null 不同)。

    如果你试图序列这样的默认值JsonElementJsonSerializer一个异常将被抛出。也就是说,如果你只是这样做

    var json = JsonSerializer.Serialize(new MyDTO());
    
    Run Code Online (Sandbox Code Playgroud)

    然后System.InvalidOperationException: Operation is not valid due to the current state of the object.抛出异常。

    您有几个选项可以避免此问题:

    • 在 .NET 5 中,您可以这样申请[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]

      public class MyDTO
      {
          [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
          public JsonElement ExtractedData {get;set;}
      }
      
      Run Code Online (Sandbox Code Playgroud)

      这会导致ExtractedData在序列化期间跳过未初始化的值。

    • 在 .NET Core 3.xJsonIgnoreCondition中不存在,因此您可以改为定义ExtractedData为可空:

      public class MyDTO
      {
          public JsonElement? ExtractedData {get;set;}
      }
      
      Run Code Online (Sandbox Code Playgroud)

      或者您可以将其初始化为 null,JsonElement如下所示:

      public class MyDTO
      {
          public JsonElement ExtractedData {get;set;} = JsonExtensions.Null;
      }
      
      public static class JsonExtensions
      {
          static readonly JsonElement nullElement = CreateNull();
      
          public static JsonElement Null => nullElement;
      
          static JsonElement CreateNull()
          {
              using var doc = JsonDocument.Parse("null");
              return doc.RootElement.Clone();
          }
      }
      
      Run Code Online (Sandbox Code Playgroud)

      这两个选项都会导致 的未初始化值ExtractedData序列化为null

  3. 另请参阅相关问题: