反序列化JSON时如何将ObjectCreationHandling.Replace应用于选定的属性? [英] How to apply ObjectCreationHandling.Replace to selected properties when deserializing JSON?

查看:88
本文介绍了反序列化JSON时如何将ObjectCreationHandling.Replace应用于选定的属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含List<Tuple<int, int, int>>属性的类,该属性的默认构造函数分配列表,并用一些默认值填充列表,例如:

I have a class that contains a List<Tuple<int, int, int>> property whose default constructor allocates the list and fills it with some default values, for instance:

public class Configuration
{
    public List<Tuple<int, int, int>> MyThreeTuple { get; set; }

    public Configuration()
    {
        MyThreeTuple = new List<Tuple<int, int, int>>();
        MyThreeTuple.Add(new Tuple<int, int, int>(-100, 20, 501));
        MyThreeTuple.Add(new Tuple<int, int, int>(100, 20, 864));
        MyThreeTuple.Add(new Tuple<int, int, int>(500, 20, 1286));
    }
}

当我使用Json.NET从JSON反序列化此类的实例时,JSON的值将添加添加到列表中,而不是替换列表中的项目,导致列表中的值过多. Json.Net在反序列化列表期间调用属性getter,从而导致重复的项目. >

When I deserialize an instance of this class from JSON using Json.NET, the values from JSON get added to the list rather than replacing the items in the list, causing the list to have too many values. A solution to this problem is given in Json.Net calls property getter during deserialization of list, resulting in duplicate items.

var settings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
var config = JsonConvert.DeserializeObject<Configuration>(jsonString, settings);    

这将导致Json.NET分配要反序列化的所有新实例.

This causes Json.NET to allocate fresh instances everything being deserialized.

但是,这带来了另一个问题:我的类存在于较大的对象图中,并且图中的某些类型没有默认的构造函数.相反,它们是由包含类中的构造函数构造的.如果使用ObjectCreationHandling = ObjectCreationHandling.Replace,则Json.NET尝试构造这些类型的实例失败,但有以下例外:

However, this introduces an additional problem: my class exists in a larger object graph, and some of the types in the graph do not have default constructors. They are instead constructed by a constructor in the containing class. If I use ObjectCreationHandling = ObjectCreationHandling.Replace, Json.NET fails trying to construct instances of these types with the following exception:

Unable to find a constructor to use for the type MySpecialType. A class 
should either have a default constructor, one constructor with arguments
or a constructor marked with the JsonConstructor attribute. 

如何将ObjectCreationHandling.Replace选择性地应用于对象图中的某些属性,而不应用于其他属性?

How can I apply ObjectCreationHandling.Replace selectively to certain properties in my object graph, and not others?

推荐答案

您有几种选择来强制替换列表而不是重复使用列表:

You have a few alternatives to force your list to be replaced rather than reused:

  1. 您可以在列表属性中添加属性指示应将其替换而不可重复使用:

  1. You can add an attribute to the list property indicating that it should be replaced not reused:

public class Configuration
{
    [JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)]
    public List<Tuple<int, int, int>> MyThreeTuple { get; set; }
}

  • 您可以使用数组而不是列表,因为总是会替换数组.如果您的列表总是 包含三项并且永远不会调整大小,则这可能是有道理的:

  • You can use an array instead of a list, as arrays are always replaced. This might make sense if your list should always contain three items and is never resized:

    public class Configuration
    {
        public Tuple<int, int, int>[] MyThreeTuple { get; set; }
    
        public Configuration()
        {
            MyThreeTuple = new[]
            {
                new Tuple<int, int, int>(-100, 20, 501),
                new Tuple<int, int, int>(100, 20, 864),
                new Tuple<int, int, int>(500, 20, 1286),
            };
        }
    }
    

  • 如果您不希望类定义依赖于Json.NET,则可以创建自定义

  • If you don't want your class definitions to have a dependency on Json.NET, you can make a custom JsonConverter that clears the list when deserializing:

    public class ConfigurationConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(Configuration).IsAssignableFrom(objectType);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var config = (existingValue as Configuration ?? (Configuration)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator());
            if (config.MyThreeTuple != null)
                config.MyThreeTuple.Clear();
            serializer.Populate(reader, config);
            return config;
        }
    
        public override bool CanWrite { get { return false; } }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    然后将其与以下 JsonSerializerSettings 一起使用:

    var settings = new JsonSerializerSettings { Converters = new JsonConverter[] { new ConfigurationConverter() } };
    

  • 如果要替换而不是重复使用 all 列表属性,则可以自定义

  • If you want all list properties to be replaced rather than reused, you can make a custom ContractResolver that does this:

    public class ListReplacementContractResolver : 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 readonly ListReplacementContractResolver instance;
    
        // Using a static constructor enables fairly lazy initialization.  http://csharpindepth.com/Articles/General/Singleton.aspx
        static ListReplacementContractResolver() { instance = new ListReplacementContractResolver(); }
    
        public static ListReplacementContractResolver Instance { get { return instance; } }
    
        protected ListReplacementContractResolver() : base() { }
    
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            var jsonProperty = base.CreateProperty(member, memberSerialization);
            if (jsonProperty.ObjectCreationHandling == null && jsonProperty.PropertyType.GetListType() != null)
                jsonProperty.ObjectCreationHandling = ObjectCreationHandling.Replace;
            return jsonProperty;
        }
    }
    
    public static class TypeExtensions
    {
        public static Type GetListType(this Type type)
        {
            while (type != null)
            {
                if (type.IsGenericType)
                {
                    var genType = type.GetGenericTypeDefinition();
                    if (genType == typeof(List<>))
                        return type.GetGenericArguments()[0];
                }
                type = type.BaseType;
            }
            return null;
        }
    }
    

    然后将其用于以下设置:

    Then use it with the following settings:

    var settings = new JsonSerializerSettings { ContractResolver = ListReplacementContractResolver.Instance };
    

  • 如果集合是仅获取的(在这种情况下不是),请参见 查看全文

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