如何在 Blazor WebAssemby 应用程序中实现 System.Text.Json.Serialization.ReferenceHandler.Preserve

问题描述 投票:0回答:3

据我所知,当您在对象模型中定义了循环引用时,您可以使用此设置来解决出现以下错误的问题:

JsonException: A possible object cycle was detected which is not supported. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of 32.

但是,我未能成功实施它以使其发挥作用。如果有人可以提供有关需要做什么的详细说明,我们将不胜感激!

我考虑过将应用程序切换为使用

Newtonsoft.JSON
,但从我读到的内容来看,这在 Blazor WebAssembly 应用程序中不可行?


更新 12/12/2020

我在试图弄清楚如何实现

ReferenceHandler.Preserve
时发现的最接近的文章是:

根据这些文章,我尝试实施以下解决方案,但都不起作用......

第一次尝试,我在服务器项目的 Startup.cs 类中实现了以下代码:

services.AddControllersWithViews().AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.Preserve;
            });

第二次尝试,我在服务器项目的 Startup.cs 类中实现了以下代码:

services.AddControllersWithViews(options =>
            {
                options.OutputFormatters.RemoveType<SystemTextJsonOutputFormatter>();
                options.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonSerializerOptions(JsonSerializerDefaults.Web)
                {
                    ReferenceHandler = ReferenceHandler.Preserve
                }));
            });

更新于 2020 年 12 月 12 日上午 11:12(中部标准时间)

将我的服务器项目更改为目标 .NET 5 并尝试上面的两个代码选项后,我现在应用程序中的每个页面上都会出现以下类型的错误:

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: The JSON value could not be converted to BusinessManager.Shared.Models.EmploymentBenefit[]. Path: $ | LineNumber: 0 | BytePositionInLine: 1.

重现原始问题的步骤

创建新的 Blazor WebAssembly 应用程序

在共享项目中定义一个具有子对象集合的父类,如下所示:

public virtual List<Child> Children{ get; set; } = new List<Child>();

在 Child 类中定义一个引用其父类的属性,如下所示:

public virtual Parent Parent{ get; set; }

然后,我使用实体框架生成数据库对象。创建一个 Web api 函数,返回父对象及其子对象,如下所示:

[HttpGet("{id}")]
        public async Task<IActionResult> Get(Guid id)
        {
            var returnValue = await db.Parents
                .Include(aa => aa.Children)
                .FirstOrDefaultAsync(aa => aa.ParentId== id);
            return Ok(returnValue);
        }

然后尝试通过调用此 Web api 函数在页面上渲染父集合和子集合。

json.net blazor-webassembly system.text.json
3个回答
0
投票

这就是我对我所做的。在 Startup.cs 中添加这个

services.AddMvc().AddJsonOptions(o =>
        {
            o.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
        });

0
投票

将我的服务器项目更改为目标 .NET 5 并尝试这两个代码后 上面的选项现在我在每个页面上都会收到以下类型的错误 在我的应用程序中:

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] 渲染组件未处理的异常:无法获取 JSON 值 转换为 BusinessManager.Shared.Models.EmploymentBenefit[]。小路: $ |行号: 0 |字节位置内联:1.

您是否也在客户端启用

ReferenceHandler.Preserve
?否则客户端中的
JsonSerializer
将无法识别元数据。


0
投票

对于 Blazor WASM,您需要执行以下操作:

在服务器端,将以下代码添加到Program.cs中:

builder.Services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
    options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});

此代码确保服务器将根据您的需要处理引用。

注意:除了

AddControllers
,您还可以使用
AddControllersWithViews

在客户端(Blazor WASM),目前还没有办法以如此简单的方式做到这一点。或者至少我没有发现任何东西。因此,您需要确保每次调用基于 JSON 的 API 时,您都会传递一个 JSON 选项并将其用于反序列化。 您可以使用这样的扩展方法来做到这一点:

public static class HttpClientExtensions
{
    private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions
    {
        ReferenceHandler = ReferenceHandler.Preserve,
        PropertyNameCaseInsensitive = true
    };

    public static Task<TValue> GetFromJsonAsync<TValue>(this HttpClient httpClient, string requestUri)
    {
        return httpClient.GetFromJsonAsync<TValue>(requestUri, JsonSerializerOptions);
    }
}

或者您可以通过自定义 HTTP 客户端类来完成此操作,如下所示:

using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;

public class JsonHttpClient
{
    private readonly HttpClient _httpClient;
    private readonly JsonSerializerOptions _jsonSerializerOptions;

    public JsonHttpClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
        _jsonSerializerOptions = new JsonSerializerOptions
        {
            ReferenceHandler = ReferenceHandler.Preserve,
            PropertyNameCaseInsensitive = true
        };
    }

    public async Task<T> GetFromJsonAsync<T>(string requestUri)
    {
        var response = await _httpClient.GetAsync(requestUri);
        response.EnsureSuccessStatusCode();

        using var contentStream = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync<T>(contentStream, _jsonSerializerOptions);
    }
}

使用第二个选项,您还需要将以下代码添加到 Program.cs

builder.Services.AddScoped(sp => new JsonHttpClient(sp.GetRequiredService<HttpClient>()));

以及您想要使用它的以下代码(剃刀页面):

@inject JsonHttpClient JsonHttpClient

var resultData = await JsonHttpClient.GetFromJsonAsync<MyDataType>("api/someendpoint");

注意:只有新 HTTP 客户端的注入和

Http
JsonHttpClient
的交换。

这也是两个类的代码以及其他有用的方法:

public static class HttpClientExtensions
{
    private static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions
    {
        ReferenceHandler = ReferenceHandler.Preserve,
        PropertyNameCaseInsensitive = true
    };

    public static Task<TValue> GetFromJsonAsync<TValue>(this HttpClient httpClient, string requestUri)
    {
        return httpClient.GetFromJsonAsync<TValue>(requestUri, JsonSerializerOptions);
    }

    public static Task<HttpResponseMessage> PostAsJsonAsync<TValue>(this HttpClient httpClient, string requestUri, TValue value)
    {
        return httpClient.PostAsJsonAsync(requestUri, value, JsonSerializerOptions);
    }

    public static Task<HttpResponseMessage> PutAsJsonAsync<TValue>(this HttpClient httpClient, string requestUri, TValue value)
    {
        return httpClient.PutAsJsonAsync(requestUri, value, JsonSerializerOptions);
    }

    public static async Task<TResponse> PostAsJsonAsync<TValue, TResponse>(this HttpClient httpClient, string requestUri, TValue value)
    {
        var response = await httpClient.PostAsJsonAsync(requestUri, value, JsonSerializerOptions);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<TResponse>(JsonSerializerOptions);
    }

    public static async Task<TResponse> PutAsJsonAsync<TValue, TResponse>(this HttpClient httpClient, string requestUri, TValue value)
    {
        var response = await httpClient.PutAsJsonAsync(requestUri, value, JsonSerializerOptions);
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadFromJsonAsync<TResponse>(JsonSerializerOptions);
    }
}

using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

public class JsonHttpClient
{
    private readonly HttpClient _httpClient;
    private readonly JsonSerializerOptions _jsonSerializerOptions;

    public JsonHttpClient(HttpClient httpClient)
    {
        _httpClient = httpClient;
        _jsonSerializerOptions = new JsonSerializerOptions
        {
            ReferenceHandler = ReferenceHandler.Preserve,
            PropertyNameCaseInsensitive = true
        };
    }

    public async Task<T> GetFromJsonAsync<T>(string requestUri)
    {
        var response = await _httpClient.GetAsync(requestUri);
        response.EnsureSuccessStatusCode();

        using var contentStream = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync<T>(contentStream, _jsonSerializerOptions);
    }

    public async Task<TResponse> PostAsJsonAsync<TRequest, TResponse>(string requestUri, TRequest content)
    {
        using var contentStream = new MemoryStream();
        await JsonSerializer.SerializeAsync(contentStream, content, _jsonSerializerOptions);
        contentStream.Position = 0;

        using var response = await _httpClient.PostAsync(requestUri, new StreamContent(contentStream));
        response.EnsureSuccessStatusCode();

        using var responseStream = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync<TResponse>(responseStream, _jsonSerializerOptions);
    }

    public async Task PostAsJsonAsync<TRequest>(string requestUri, TRequest content)
    {
        using var contentStream = new MemoryStream();
        await JsonSerializer.SerializeAsync(contentStream, content, _jsonSerializerOptions);
        contentStream.Position = 0;

        using var response = await _httpClient.PostAsync(requestUri, new StreamContent(contentStream));
        response.EnsureSuccessStatusCode();
    }

    public async Task<TResponse> PutAsJsonAsync<TRequest, TResponse>(string requestUri, TRequest content)
    {
        using var contentStream = new MemoryStream();
        await JsonSerializer.SerializeAsync(contentStream, content, _jsonSerializerOptions);
        contentStream.Position = 0;

        using var response = await _httpClient.PutAsync(requestUri, new StreamContent(contentStream));
        response.EnsureSuccessStatusCode();

        using var responseStream = await response.Content.ReadAsStreamAsync();
        return await JsonSerializer.DeserializeAsync<TResponse>(responseStream, _jsonSerializerOptions);
    }

    public async Task PutAsJsonAsync<TRequest>(string requestUri, TRequest content)
    {
        using var contentStream = new MemoryStream();
        await JsonSerializer.SerializeAsync(contentStream, content, _jsonSerializerOptions);
        contentStream.Position = 0;

        using var response = await _httpClient.PutAsync(requestUri, new StreamContent(contentStream));
        response.EnsureSuccessStatusCode();
    }

    public async Task DeleteAsync(string requestUri)
    {
        var response = await _httpClient.DeleteAsync(requestUri);
        response.EnsureSuccessStatusCode();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.