IJSRuntime会忽略服务器端blazor项目中的自定义json序列化程序

时间:2019-07-06 14:54:45

标签: c# json asp.net-core serialization json.net

在我的服务器端blazor项目(核心3,preview6)中,我试图用我的类的实例调用javascript。您可以通过注入IJSRuntime并在其上调用InvokeAsync来做到这一点(请参阅here)。

因为我需要摆脱所有null属性(应该处理我的对象的js库(chartJs)不能处理null值),并且由于我有一些自定义序列化(对于可以具有不同数据类型的枚举),所以我砍掉了它可以与ExpandoObject一起使用(这不是我的想法,但是可以)。

我首先使用json.net序列化了对象,因此可以定义NullValueHandling = NullValueHandling.Ignore,因此所有自定义转换器都被使用了。

然后,我再次使用json.net将其解析为ExpandoObject。我之所以这样做,是因为否则,不在json中的值在c#实例中将为null,但这样就根本不存在它们。

ExpandoObject不具有空值,并且具有正确的枚举值,然后用于调用javascript。这样,当到达javascript引擎时就不会有null值。

Since in preview6 json.net isn't used internally anymore和新的json序列化程序(来自System.Text.Json)无法处理ExpandoObject,当我从Preview5更新为Preview6时遇到运行时错误,说ExpandoObject不支持。没什么大不了的,我做了一个函数,将ExpandoObject转换为Dictionary<string, object>,可以毫无问题地进行序列化。

阅读this question,以了解有关我如何执行此操作的更多信息,并查看一些示例。如果您不太了解我的所作所为,那么这可能是个好主意。它还包含我将在此处添加的json以及c#模型和其他说明。

这有效,但是我认为也许如果我能够回到json.net,我可以完全删除ExpandoObject,因为首先它将使用我为特殊枚举编写的自定义转换器,其次我可以全局指定NullValueHandling = NullValueHandling.Ignore(请问这会有副作用吗?)。

我遵循the ms docs再次使用json.net,并将其添加到ConfigureServies方法中(services.AddRazorPages()已经存在):

services.AddRazorPages()
    .AddNewtonsoftJson(o =>
    {
        o.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
        o.SerializerSettings.ContractResolver = new DefaultContractResolver
        {
            NamingStrategy = new CamelCaseNamingStrategy(true, false)
        };
    });

可悲的是,它不起作用,直接用对象调用js导致了与我添加此对象之前完全相同的json(请参见下文)。

然后我尝试直接使用ExpandoObject,因为我知道.net的新json序列化器无法处理该问题。正如预期的那样,它引发了异常,并且堆栈跟踪没有显示json.net的迹象。这证实了它实际上并没有像我所说的那样使用json.net。

  在System.Text.Json.Serialization.JsonClassInfo.GetElementType中的

(Type propertyType,Type parentType,MemberInfo memberInfo)      在System.Text.Json.Serialization.JsonClassInfo.CreateProperty中(类型为声明的PropertyType,类型为runtimePropertyType,PropertyInfo propertyInfo,类型为ParentClassType,JsonSerializerOptions选项)      在System.Text.Json.Serialization.JsonClassInfo.AddProperty(Type propertyType,PropertyInfo propertyInfo,Type classType,JsonSerializerOptions选项)      在System.Text.Json.Serialization.JsonClassInfo..ctor中(类型类型,JsonSerializerOptions选项)      在System.Text.Json.Serialization.JsonSerializerOptions.GetOrAddClass(Type classType)      在System.Text.Json.Serialization.JsonSerializer.GetRuntimeClassInfo(对象值,JsonClassInfo&jsonClassInfo,JsonSerializerOptions选项)      在System.Text.Json.Serialization.JsonSerializer.HandleEnumerable(JsonClassInfo elementClassInfo,JsonSerializerOptions选项,Utf8JsonWriter writer,WriteStack&state)中      在System.Text.Json.Serialization.JsonSerializer.Write(Utf8JsonWriter writer,Int32 flushThreshold,JsonSerializerOptions选项,WriteStack和状态)      在System.Text.Json.Serialization.JsonSerializer.WriteCore(PooledByteBufferWriter输出,对象值,类型类型,JsonSerializerOptions选项)      在System.Text.Json.Serialization.JsonSerializer.WriteCoreString(对象值,类型类型,JsonSerializerOptions选项)      在System.Text.Json.Serialization.JsonSerializer.ToString [TValue](TValue值,JsonSerializerOptions选项)处      在Microsoft.JSInterop.JSRuntimeBase.InvokeAsync [T](字符串标识符,Object []参数)      在ChartJs.Blazor.ChartJS.ChartJsInterop.GetJsonRep(IJSRuntime jSRuntime,对象obj)

然后,我还尝试仅更改普通序列化器中的选项,以查看是否会更改任何内容。我用它代替AddNewtonsoftJson

services.AddRazorPages()
    .AddJsonOptions(o => o.JsonSerializerOptions.IgnoreNullValues = true);

有趣的是,这仍然给了我相同的结果。

我将添加生成的json和应该生成的内容。您也可以在我前面提到的CodeReview问题中找到它。

以下是我得到的json:

在以下时间保持不变

  • 我没有在ConfigureServices方法中指定任何内容。
  • 我指定使用Newtonsoft.JsonAddNewtonsoftJson
  • 我通过JsonOptions从.net指定AddJsonOptions
{
    "options": {
        "someInt": 2,
        "someString": null,
        "axes": [
            {
                "someString": null
            },
            {
                "someString": "axisString"
            }
        ]
    },
    "data": {
        "data": [
            1,
            2,
            3,
            4,
            5
        ],
        "someString": "asdf",
        "someStringEnum": {}   <-- this is one of those special enums with a custom converter
    }
}

这就是我应该得到的。请注意,所有空值都已删除,并使用了自定义转换器。我只能通过使用ExpandoObject的hacky解决方案来做到这一点。

{
    "options": {
        "someInt": 2,
        "axes": [
            {},
            {
                "someString": "axisString"
            }
        ]
    },
    "data": {
        "data": [
            1,
            2,
            3,
            4,
            5
        ],
        "someString": "asdf",
        "someStringEnum": "someTestThing"   <-- this is one of those special enums with a custom converter
    }
}

那么,即使我指示这样做,为什么仍然使用System.Text.Json而不是Newtonsoft.Json?我还需要添加什么才能使其使用json.net?

1 个答案:

答案 0 :(得分:1)

正如@dbc的注释中正确提到的,IJSRuntime始终使用System.Text.Json。我已通过询问asp.net团队对此进行了确认(请参见my github issue)。

这些当然是个坏消息,但实际上没有办法做我想做的事,并且可以确认,asp.net团队不打算添加此功能(再次参见my github feature request)。