json.net-如何仅在根对象上添加属性$ type [英] json.net - how to add property $type ONLY on root object

查看:83
本文介绍了json.net-如何仅在根对象上添加属性$ type的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想修改我的json.NET序列化器,以将$ type属性仅添加到实现给定接口的对象上,而不添加到任何属性或嵌套对象上.

I want to modify my json.NET serializer to add the $type property only to the objects which implements a given interface but not to any property or nested objects.

使用TypeNameHandling.Auto(默认)

{
  "PropertyA": 123,
  "PropertyB": "foo",
  "PropertyC": [1, 2, 3, 4]
}

具有TypeNameHandling.All

{
  "$type": "JsonNetTypeNameHandling.TestEvent, jsonNetTypeNameHandling",
  "PropertyA": 123,
  "PropertyB": "foo",
  "PropertyC": {
    "$type": "System.Collections.Generic.List`1[[System.Int32, mscorlib]], mscorlib",
    "$values": [1, 2, 3, 4 ]
  }
}

我想要的

{
  "$type": "JsonNetTypeNameHandling.TestEvent, jsonNetTypeNameHandling",
  "PropertyA": 123,
  "PropertyB": "foo",
  "PropertyC": [1, 2, 3, 4]
}

我正在尝试使用自定义的ContractResolver,但无法正常工作:

I am experimenting with a custom ContractResolver but I don't get it to work:

class Program
{
    static void Main(string[] args)
    {
        var serializerSettings = new JsonSerializerSettings()
        {
            TypeNameHandling = TypeNameHandling.Auto,
            TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
            NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
            ContractResolver = new EnableTypeNameHandlingAllOnlyForEvents(),
            Formatting = Formatting.Indented
        };

        var event1 = new TestEvent() { PropertyA = 123, PropertyB = "foo", PropertyC = new List<int> { 1, 2, 3, 4 } };

        string event1Serialized = JsonConvert.SerializeObject(event1, serializerSettings);

        Console.WriteLine(event1Serialized);
        Console.ReadLine();
    }
}

public interface IEvent
{
}

public class TestEvent : IEvent
{
    public int PropertyA { get; set; }
    public string PropertyB { get; set; }
    public List<int> PropertyC { get; set; }
}

public class EnableTypeNameHandlingAllOnlyForEvents : DefaultContractResolver
{
    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        var x = base.CreateObjectContract(objectType);

        if (typeof(IEvent).IsAssignableFrom(x.UnderlyingType))
        {
            // What to do to tell json.NET to add $type to instances of this (IEvent) type???
        }

        return x;
    }
}

推荐答案

如果您需要在根对象上使用"$type"属性,并且可以在嵌套多态对象和数组上显示"$type"属性,请使用以下重载TypeNameHandling.Auto: JsonConvert.SerializeObject(Object, Type, JsonSerializerSettings) .

If you require the "$type" property on your root object and are OK with it appearing on nested polymorphic objects and arrays if required, use the following overload along with TypeNameHandling.Auto: JsonConvert.SerializeObject(Object, Type, JsonSerializerSettings).

文档:

public static string SerializeObject(
    Object value,
    Type type,
    JsonSerializerSettings settings
)

类型 类型:System.Type 要序列化的值的类型.如果值的类型不匹配,则当TypeNameHandling为自动"以写出类型名称时,将使用此参数.指定类型是可选的.

type Type: System.Type The type of the value being serialized. This parameter is used when TypeNameHandling is Auto to write out the type name if the type of the value does not match. Specifing the type is optional.

即,做:

var serializerSettings = new JsonSerializerSettings()
{
    TypeNameHandling = TypeNameHandling.Auto,
    TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
    NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
    Formatting = Formatting.Indented
};

var event1Serialized = JsonConvert.SerializeObject(event1, typeof(IEvent), serializerSettings);

如果您在根对象上需要"$type",并且在嵌套的多态对象和数组上接受它,即使另有要求,您也需要将TypeNameHandling.All自定义合同解析器,它设置了

If you require "$type" on the root object and will not accept it on nested polymorphic objects and arrays even if otherwise required, you will need to use TypeNameHandling.All along with a custom contract resolver that sets JsonContainerContract.ItemTypeNameHandling = TypeNameHandling.None:

public class SuppressItemTypeNameContractResolver : DefaultContractResolver
{
    // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
    // http://www.newtonsoft.com/json/help/html/ContractResolver.htm
    // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
    // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
    static SuppressItemTypeNameContractResolver instance;

    // Using a static constructor enables fairly lazy initialization.  http://csharpindepth.com/Articles/General/Singleton.aspx
    static SuppressItemTypeNameContractResolver() { instance = new SuppressItemTypeNameContractResolver(); }

    public static SuppressItemTypeNameContractResolver Instance { get { return instance; } }

    protected SuppressItemTypeNameContractResolver() : base() { }

    protected override JsonContract CreateContract(Type objectType)
    {
        var contract = base.CreateContract(objectType);
        var containerContract = contract as JsonContainerContract;
        if (containerContract != null)
        {
            if (containerContract.ItemTypeNameHandling == null)
                containerContract.ItemTypeNameHandling = TypeNameHandling.None; 
        }
        return contract;
    }
}

然后像这样使用它:

var serializerSettings = new JsonSerializerSettings()
{
    TypeNameHandling = TypeNameHandling.All,
    ContractResolver = SuppressItemTypeNameContractResolver.Instance,
    TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
    NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
    Formatting = Formatting.Indented
};

var event1Serialized = JsonConvert.SerializeObject(event1, serializerSettings);

最后,请注意 Newtonsoft文档:

当您的应用程序从外部源反序列化JSON时,应谨慎使用

TypeNameHandling.反序列化除None以外的其他值时,应使用自定义SerializationBinder验证传入的类型.

TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.

有关为何可能需要这样做的讨论,请参见 Newtonsoft Json中的TypeNameHandling警告

For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json, How to configure Json.NET to create a vulnerable web API, and Alvaro Muñoz & Oleksandr Mirosh's blackhat paper https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf

这篇关于json.net-如何仅在根对象上添加属性$ type的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆