首页 > 解决方案 > 自定义 JSON 转换器,以帮助反序列化回确切原始类型的对象

问题描述

我正在做的事情已经Json.net通过设置TypeNameHandling来实现TypeNameHandling.Objects。这样对象类型也将被序列化,并且反序列化的对象将具有确切的原始类型。

但是 usingTypeNameHandling暴露了一些安全问题,并要求我们使用自定义SerializationBinder来限制将支持哪些类型以避免可能的代码注入。这不是我试图寻找另一种解决方案的主要原因。实际上我发现通过使用TypeNameHandling.Objects,一个对象将被序列化为一个复杂的 JSON,不仅包括对象数据本身和对象类型,还包括其他一些对我来说看起来多余的属性。

我认为我们只需要一个包含有关对象类型信息的属性(例如对象类型的程序集限定名称),所以我想创建一个自定义JsonConverter,它将任何对象序列化为一些 JSON,如下所示:

{
   "Item" : "normal JSON string of object",
   "ItemType" : "assembly qualified name of object type"
}

这还不够吗?正如我之前所说,除了 2 个类似于那些(具有不同名称)的属性外,Json.netlib 还包括一些其他属性(签名...),这对我来说真的是多余的。

我不是在问如何实现JsonConverter我上面提到的自定义。我只是想知道该转换器(具有简化的 JSON 结构)是否可以,或者我应该使用Json.netwith提供的标准解决方案TypeNameHandling(涉及更复杂的 JSON 结构)?我主要担心的是由于要转换/序列化/传输更多数据而导致TypeNameHandling设置为可能的性能问题。Objects

标准解决方案的另一个问题是性能问题,实际上我只需要将自定义转换逻辑应用于确切类型的所有对象object,而不是所有其他强类型对象(可能仍然不必要地应用TypeNameHandling?)

标签: c#json.net

解决方案


我对您提出的多态自定义设计有一些反应JsonConverter(我们称其为基本类型):PolymorphicConverter<T>T

  1. 关于安全,你写道,

    ... usingTypeNameHandling暴露了一些安全问题,并要求我们使用自定义SerializationBinder来限制将支持哪些类型以避免可能的代码注入。

    可能出现的相同安全风险也TypeNameHandling将出现在PolymorphicConverter<T>.

    这里的风险是攻击者欺骗一些多态反序列化代码来实例化攻击小工具由于 Json.Net TypeNameHandling auto,请参阅Newtonsoft Json和External json 中的 TypeNameHandling 警告?示例和讨论。如果攻击者使用转换器支持的属性中指定的攻击小工具类型制作 JSON ,那么它最终可能会实例化攻击小工具并实施攻击。 "ItemType"

    PolymorphicConverter<T>您可以通过仅对那些在实践中实际上是多态的属性应用(或[JsonProperty(TypeNameHandling = TypeNameHandling.All)]就此而言)启用对已知多态属性或数组项的多态反序列化的支持来减少攻击面——但如果这些属性的多态基类型只是碰巧与攻击小工具兼容,您将容易受到攻击。

    因此,无论使用什么机制,您仍然需要像自定义这样的东西SerializationBinder来过滤掉顽皮的类型,无论您如何在 JSON 中编码类型信息的细节。

  2. JSON 文件大小。Json.NET 通过在对象的开头添加一个属性来编码类型信息:

    "$type" : "assembly qualified name of object type"
    

    您的计划是改为添加:

    "ItemType" : "assembly qualified name of object type"
    

    目前尚不清楚为什么会有优势,除非您的类型名称更紧凑。

  3. 表现。你写了,

    我主要担心的是由于要转换/序列化/传输更多数据而导致TypeNameHandling设置为可能的性能问题。Objects

    首先,为什么不只是测量和找出?见https://ericlippert.com/2012/12/17/performance-rant/

    其次,Newtonsoft 有一个设置MetadataPropertyHandling,当设置为 时Default,假定多态属性"$type"在每个对象中排在首位,因此能够将它们流式传输,而无需将整个 JSON 预加载到JToken层次结构中。

    如果您的转换器无条件地预加载到JToken层次结构中以获取"ItemType"属性值,则它的性能可能会更差。

  4. 关于将多态反序列化限制为仅需要的属性,您写道:

    标准解决方案的另一个问题是性能问题,实际上我只需要将自定义转换逻辑应用于确切类型的所有对象object,而不是所有其他强类型对象

    无论哪种方式,这都可以通过自定义ContractResolver. 根据您选择的解决方案,覆盖DefaultContractResolver.CreateProperty和,何时JsonProperty.PropertyType == typeof(object),设置TypeNameHandling或根据需要。Converter


推荐阅读