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

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

问题描述

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

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,你可以自定义 JsonConverter 反序列化时清除列表:

  • 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:

    Then use it with the following JsonSerializerSettings:

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

  • 如果您希望所有列表属性被替换而不是重复使用,您可以自定义ContractResolver 这样做:

  • 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 };
    

  • 如果集合是仅获取的(在这种情况下不是),请参阅 填充现有对象时在添加项之前清除集合.

    这篇关于反序列化 JSON 时如何将 ObjectCreationHandling.Replace 应用于选定的属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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