填充对象重用对象和替换数组的对象?

使用Json.NET,我想将JObject映射到.NET对象,具有以下行为:

  1. 对于JObject (非null)数组属性,替换目标对象上的整个集合。

  2. 对于JObject (非null)对象属性,如果它不为null,则重用目标对象的属性,并仅将提供的属性映射到其上。

JsonSerializer.Populate似乎就是我想要的,如本答案所述 。 至于我正在寻找的行为,似乎我可以通过JsonSerializerSettings.ObjectCreationHandling实现其中一个,但不是两个。 ObjectCreationHandling.Replace根据需求#1执行我想要的操作,而ObjectCreationHandling.Auto根据需求#2执行我想要的操作,但它将数组项附加到现有集合上。

在这里达到这两个要求的推荐方法是什么?

Json.NET将自动替换任何数组或只读集合。 要在反序列化时清除读写集合,可以创建一个自定义契约解析器 ,将OnDeserializingCallback添加到每个可修改的集合中,以便在反序列化开始时清除集合。 清除集合而不是直接替换集合处理集合是get-only的情况,例如:

 public class RootObject { readonly HashSet hashSet = new HashSet(); public HashSet HashSetValues { get { return this.hashSet; } } } 

合同解析器如下:

 public class CollectionClearingContractResolver : DefaultContractResolver { static void ClearGenericCollectionCallback(object o, StreamingContext c) { var collection = o as ICollection; if (collection == null || collection is Array || collection.IsReadOnly) return; collection.Clear(); } static SerializationCallback ClearListCallback = (o, c) => { var collection = o as IList; if (collection == null || collection is Array || collection.IsReadOnly) return; collection.Clear(); }; protected override JsonArrayContract CreateArrayContract(Type objectType) { var contract = base.CreateArrayContract(objectType); if (!objectType.IsArray) { if (typeof(IList).IsAssignableFrom(objectType)) { contract.OnDeserializingCallbacks.Add(ClearListCallback); } else if (objectType.GetCollectItemTypes().Count() == 1) { MethodInfo method = typeof(CollectionClearingContractResolver).GetMethod("ClearGenericCollectionCallback", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); MethodInfo generic = method.MakeGenericMethod(contract.CollectionItemType); contract.OnDeserializingCallbacks.Add((SerializationCallback)Delegate.CreateDelegate(typeof(SerializationCallback), generic)); } } return contract; } } public static class TypeExtensions { public static IEnumerable GetInterfacesAndSelf(this Type type) { if (type == null) throw new ArgumentNullException(); if (type.IsInterface) return new[] { type }.Concat(type.GetInterfaces()); else return type.GetInterfaces(); } public static IEnumerable GetCollectItemTypes(this Type type) { foreach (Type intType in type.GetInterfacesAndSelf()) { if (intType.IsGenericType && intType.GetGenericTypeDefinition() == typeof(ICollection<>)) { yield return intType.GetGenericArguments()[0]; } } } } public static class JsonExtensions { public static void Populate(this JToken value, T target) where T : class { value.Populate(target, null); } public static void Populate(this JToken value, T target, JsonSerializerSettings settings) where T : class { using (var sr = value.CreateReader()) { JsonSerializer.CreateDefault(settings).Populate(sr, target); } } } 

然后使用它,做:

 var settings = new JsonSerializerSettings { ContractResolver = new CollectionClearingContractResolver(), }; jObject.Populate(rootObject, settings); 

样品小提琴 。

在反序列化在其默认构造函数中填充集合的对象时,这样的合同解析器也很有用,因为在反序列化中会导致列表条目的副本 。

一种解决方法是使用自定义JsonConverter ,通过在检测到集合类型时忽略现有值来有效地替换集合。

 public class ReplaceArrayConverter : JsonConverter { public override bool CanConvert(Type objectType) { // check for Array, IList, etc. return objectType.IsCollection(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // ignore existingValue and just create a new collection return JsonSerializer.CreateDefault().Deserialize(reader, objectType); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { JsonSerializer.CreateDefault().Serialize(writer, value); } } 

像这样使用:

 var ser = JsonSerializer.CreateDefault(new JsonSerializerSettings { Converters = new[] { new ReplaceArrayConverter() } }); using (var reader = jObj.CreateReader()) { ser.Populate(reader, model); }