System.Text.Json 中 JObject 的等价物

问题描述

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

public class MyDTO
{
    public JObject ExtractedData {get;set;}
}

我正在将此项目转换为 .NET 5。什么与 .NET 5 中的 JObject 等效?我试图避免 JsonDocument 因为(来自 docs):

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

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

解决方法

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

public class MyDTO
{
    public JsonElement ExtractedData {get;set;}
}

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

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

  1. JsonElement 代表任何 JSON 值,因此最接近于 JToken 而不是 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;
            }
        }
    }
    
  2. 由于 JsonElement 是结构体,因此默认值不是 null。那么,它是什么?事实证明 default(JsonElement)ValueKind = JsonValueKind.Undefined

    没有值(与 Null 不同)。

    如果您尝试使用 JsonElement 序列化此类默认 JsonSerializer,则会引发异常。 IE。如果你只是这样做

    var json = JsonSerializer.Serialize(new MyDTO());
    

    然后抛出一个 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;}
      }
      

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

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

      public class MyDTO
      {
          public JsonElement? ExtractedData {get;set;}
      }
      

      或者您可以像这样将其初始化为 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();
          }
      }
      

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

  3. 另见相关问题:

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...