IJSRuntime 忽略服务器端 Blazor 项目中的自定义 json 序列化程序
Posted
技术标签:
【中文标题】IJSRuntime 忽略服务器端 Blazor 项目中的自定义 json 序列化程序【英文标题】:IJSRuntime ignores custom json serializer in server side blazor project 【发布时间】:2019-11-16 19:17:36 【问题描述】:在我的服务器端 blazor 项目(核心 3,preview6)中,我试图用我的类的一个实例调用 javascript。您可以通过注入 IJSRuntime
并在其上调用 InvokeAsync
来做到这一点(请参阅 here)。
因为我需要摆脱所有 null 属性(应该处理我的对象 (chartJs) 的 js 库不能处理 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
s,当我从 preview5 更新到 preview6 时出现运行时错误,说不支持 ExpandoObject
。没什么大不了的,我做了一个函数来将 ExpandoObject
转换为 Dictionary<string, object>
可以毫无问题地序列化。
阅读this question 了解更多关于我是如何做到这一点的并查看一些示例。如果你不太明白我做了什么,这可能是个好主意。它还包含我将在此处添加的 json 以及 c# 模型和其他说明。
这可行,但我想如果我能够回到 json.net,我可以完全删除 ExpandoObject
,因为首先它会使用我为特殊枚举编写的自定义转换器,其次我可以指定 @ 987654341@ 全局(ps。这会有副作用吗?)。
我关注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(类型 propertyType,PropertyInfo propertyInfo,Type classType,JsonSerializerOptions 选项) 在 System.Text.Json.Serialization.JsonClassInfo..ctor(类型类型,JsonSerializerOptions 选项) 在 System.Text.Json.Serialization.JsonSerializerOptions.GetOrAddClass(类型 classType) 在 System.Text.Json.Serialization.JsonSerializer.GetRuntimeClassInfo(对象值,JsonClassInfo& jsonClassInfo,JsonSerializerOptions 选项) 在 System.Text.Json.Serialization.JsonSerializer.HandleEnumerable(JsonClassInfo elementClassInfo,JsonSerializerOptions 选项,Utf8JsonWriter writer,WriteStack& 状态) 在 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](字符串标识符,对象 [] args) 在 ChartJs.Blazor.ChartJS.ChartJsInterop.GetJsonRep(IJSRuntime jSRuntime, Object obj)
然后我还尝试只更改普通序列化程序的选项,看看是否会改变任何东西。我用这个代替AddNewtonsoftJson
:
services.AddRazorPages()
.AddJsonOptions(o => o.JsonSerializerOptions.IgnoreNullValues = true);
有趣的是,这仍然给了我相同的结果。
我将添加生成的 json 以及应该生成的内容。您还可以在我之前提到的 CodeReview 问题中找到这一点。
以下 json 是我得到的:
它保持不变
我没有在ConfigureServices
方法中指定任何内容。
我指定使用Newtonsoft.Json
到AddNewtonsoftJson
。
我从.net 到AddJsonOptions
指定JsonOptions
。
"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?
【问题讨论】:
检查reference source,被调用的方法Microsoft.JSInterop.JSRuntimeBase.InvokeAsync
似乎硬编码为使用System.Text.Json.Serialization.JsonSerializer
。您可能需要创建自己的IJSRuntime
。
该死的 :( 但是我怎么能创建自己的?我认为在 javascript 中注入 jsruntime 有一个相当大的生态系统吗?
既然您要注入自己的IJSRuntime
,也许您可以使用decorator pattern 并为“真实”IJSRuntime
创建一些包装器,将args 序列化和预处理为干净的中性格式? IE。 arg
-> JToken
(通过序列化选项)-> Dictionary<string, object>
如How do I use JSON.NET to deserialize into nested/recursive Dictionary and List? 所示。
我应该这样回答吗?
对不起@dbc。我想我没有完全理解你试图告诉我的内容。您的解决方案可能会奏效,而我的一些主张根本不准确。我没有尝试过,但是在寻找解决这个问题的方法并写a new feature request for this时,我想我终于明白你的意思了。谢谢你的帮助:)
【参考方案1】:
正如@dbc 在 cmets 中正确提到的,IJSRuntime
始终使用 System.Text.Json
。我已经通过询问 asp.net 团队确认了这一点(请参阅my github issue)。
我提交了github feature request,这可能会在未来的 .NET 版本中实现。
【讨论】:
以上是关于IJSRuntime 忽略服务器端 Blazor 项目中的自定义 json 序列化程序的主要内容,如果未能解决你的问题,请参考以下文章