加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

json.net – 如何在根对象上添加属性$type ONLY

发布时间:2020-12-16 19:47:36 所属栏目:百科 来源:网络整理
导读:我想修改我的json.NET序列化程序,只将$type属性添加到实现给定接口但不对任何属性或嵌套对象的对象. 使用TypeNameHandling.Auto(默认) { "PropertyA": 123,"PropertyB": "foo","PropertyC": [1,2,3,4]} 使用TypeNameHandling.All { "$type": "JsonNetTypeNam
我想修改我的json.NET序列化程序,只将$type属性添加到实现给定接口但不对任何属性或嵌套对象的对象.

使用TypeNameHandling.Auto(默认)

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

使用TypeNameHandling.All

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

我想要的是

{
  "$type": "JsonNetTypeNameHandling.TestEvent,4]
}

我正在尝试使用自定义的ContractResolver,但我没有让它工作:

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,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”属性,并且可以在嵌套的多态对象和数组中显示,如果需要,请使用以下重载以及TypeNameHandling.Auto: JsonConvert.SerializeObject(Object,Type,JsonSerializerSettings).

从docs开始:

06000

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,Formatting = Formatting.Indented
};

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

如果在根对象上需要“$type”并且在嵌套的多态对象和数组上不接受它,即使否则需要,您将需要使用TypeNameHandling.All以及设置JsonContainerContract.ItemTypeNameHandling = TypeNameHandling.None的custom contract resolver:

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,serializerSettings);

最后,请注意Newtonsoft docs中的这一注意事项:

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.

有关为何需要这样做的讨论,请参阅TypeNameHandling caution in Newtonsoft Json,How to configure Json.NET to create a vulnerable web API和AlvaroMu?oz& Oleksandr Mirosh的黑帽纸https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读