【问题标题】:Load objects with circular references to Blazor Wasm with System.Text.Json使用 System.Text.Json 加载对 Blazor Wasm 的循环引用的对象
【发布时间】:2021-05-26 14:01:54
【问题描述】:

问题

我正在使用 Blazor WASM,它与使用 ASP.NET Core 构建的 RESTful API 进行通信。 我需要加载带有循环引用的对象。

使用默认序列化设置,这会产生 System.Text.Json.JsonException:

A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger 
than the maximum allowed depth of 32. Consider using ReferenceHandler.Preserve on JsonSerializerOptions 
to support cycles.

我的尝试

我在服务器上的 Startup.cs 中添加了以下几行到 ConfigureServices():

services.AddControllersWithViews().AddJsonOptions(options => {
   options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
});

并修改了客户端上的请求:

data = await Http.GetFromJsonAsync<My.Shared.DataObject[]>(
    "api/GetDataObjects", 
    new System.Text.Json.JsonSerializerOptions() { 
        ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.Preserve
    }
);

当我运行它时,服务器会返回合理的响应:

但是客户端在解析响应时抛出System.Text.Json.JsonException:

Cannot parse a JSON object containing metadata properties like '$id' into an array or immutable 
collection type. Type 'My.Shared.DataObject[]'. 

Path: $.$id | LineNumber: 0 | BytePositionInLine: 7

为什么会这样?如何解决?

【问题讨论】:

  • 对创建循环引用的属性使用[JsonIgnore] 属性。当然,您仍然可以让外键通过,而不是实体。
  • 能否请edit 分享minimal reproducible example 的问题?但是Cannot parse a JSON object contains metadata properties like '$id' into an array or immutable collection type 似乎可以解释。作为猜测,您有一些在客户端声明为数组的集合类型。尝试将其声明为列表。

标签: c# json blazor


【解决方案1】:

错误信息不言自明:

无法将包含元数据属性(如“$id”)的 JSON 对象解析为 数组 或不可变的 集合类型。 输入“My.Shared.DataObject[]”

docs证实了这一点:

对于值类型、不可变类型和数组,没有引用元数据被序列化。在反序列化时,如果找到$ref$id,则会引发异常。

您需要反序列化为List&lt;DataObject&gt;,而不是数组:

data = await Http.GetFromJsonAsync<List<My.Shared.DataObject>>(
    "api/GetDataObjects", 
    new System.Text.Json.JsonSerializerOptions() { 
        ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.Preserve
    }
);

注意事项:

  • 该限制可能已实现,因为数组和只读集合只能在其项目被读入后才能完全构造 - 但ReferenceHandler 旨在支持从子级到父级的反向引用以及来自子级的递归自引用对象自身。并且不清楚如何为只读集合或不可变对象实现此类功能,这些对象只能在其子项构建后才能构建。

    Json.NET 也有类似的限制,详见Cannot preserve reference to array or readonly list, or list created from a non-default constructor

  • 如果您不需要为集合保留引用,您可以将您的集合序列化为一个数组,并且数组本身不会包含$id 信息。 (其项目仍将包含所需的 $id 属性。)

    即在您的服务器端,您必须通过网络发送List&lt;My.Shared.DataObject&gt;。相反,如果您发送 My.Shared.DataObject [],根容器将是一个简单的 JSON 数组,而不是带有参考信息的 JSON 对象和嵌套的 $values 数组。

    相比之下,Json.NET 通过设置 PreserveReferencesHandling.Objects 支持仅保留对象而不是集合的引用,System.Text.Json 似乎没有通过选项提供这种级别的控制。

  • 之后您可以随时使用ToArray()List&lt;DataObject&gt; 转换为数组。

演示小提琴here.

【讨论】:

    猜你喜欢
    • 2021-11-28
    • 2021-10-27
    • 1970-01-01
    • 1970-01-01
    • 2020-10-06
    • 2017-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多