我有一个包含List<Tuple<int, int, int>>
属性的类,其默认构造函数分配列表并使用一些默认值填充它,例如:
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 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为正在反序列化的所有内容分配新的实例。
然而,这引入了另一个问题:我的类存在于更大的对象图中,并且图中的某些类型没有默认构造函数。它们由包含类中的构造函数构造。如果我使用ObjectCreationHandling = ObjectCreationHandling.Replace
,Json.NET无法尝试构造这些类型的实例,但有以下异常:
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
应用于对象图中的某些属性,而不是其他属性?
答案 0 :(得分:13)
您可以使用一些替代方法来强制更换列表而不是重复使用:
您可以在list属性中添加attribute,表示应该替换它而不是重复使用:
public class Configuration
{
[JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)]
public List<Tuple<int, int, int>> MyThreeTuple { get; set; }
}
您可以使用数组而不是列表,因为数组总是被替换。如果您的列表始终包含三个项目且从未调整大小,则这可能有意义:
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
,在反序列化时清除列表:
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() } };
如果您希望替换所有列表属性而不是重复使用,则可以创建一个自定义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 };
如果集合是get-only(在这种情况下不是),请参阅Clear collections before adding items when populating existing objects。