如何在反序列化JSON时将ObjectCreationHandling.Replace应用于所选属性?

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

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

当我使用Json.NET从JSON反序列化此类的实例时,JSON中的值将添加到列表中而不是替换列表中的项,从而导致列表具有太多值。 在反序列化列表期间 , Json.Net调用属性getter中给出了此问题的解决方案,从而导致重复项 。

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

这会导致Json.NET为正在反序列化的所有内容分配新的实例。

但是,这引入了另一个问题:我的类存在于更大的对象图中,并且图中的某些类型没有默认构造函数。 它们由包含类中的构造函数构造。 如果我使用ObjectCreationHandling = ObjectCreationHandling.Replace ,则Json.NET无法尝试构造这些类型的实例,但会出现以下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应用于对象图中的某些属性,而不是其他属性?

您可以使用一些替代方法来强制替换列表而不是重复使用:

  1. 您可以向list属性添加一个属性 ,指示应该替换它而不是重用:

     public class Configuration { [JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)] public List> MyThreeTuple { get; set; } } 
  2. 您可以使用数组而不是列表,因为数组总是被替换。 如果您的列表应始终包含三个项目并且从未resize,则这可能有意义:

     public class Configuration { public Tuple[] MyThreeTuple { get; set; } public Configuration() { MyThreeTuple = new[] { new Tuple(-100, 20, 501), new Tuple(100, 20, 864), new Tuple(500, 20, 1286), }; } } 
  3. 如果您不希望类定义依赖于Json.NET,则可以创建一个自定义JsonConverter ,在反序列化时清除列表:

     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() } }; 
  4. 如果您想要替换所有列表属性而不是重用,您可以创建一个自定义ContractResolver来执行此操作:

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

    然后使用以下设置:

     var settings = new JsonSerializerSettings { ContractResolver = ListReplacementContractResolver.Instance }; 
  5. 如果集合是get-only(在这种情况下不是),请在填充现有对象时添加项目之前查看Clear集合 。