将两个值反序列化为同一属性

我有一个客户端可以调用两个不同版本的服务。

一项服务只发送一个值:

{ "value" : { ... } } 

第二个服务总是返回多个值:

 { "values" : [ { ... }, { ... } ] } 

理想情况下,我想在客户端类中使用单个对象来表示这一点,因此用户永远不会看到它是单个值还是多个值。

 public class MyValues { public List Values { get; set; } public Thing Other { get; set; } } 

我认为我能够实现这一目标的唯一方法是使用我应用于MyValues的自定义JsonConverter类,但我真的只想在我反序列化属性value时做一些自定义操作。 我似乎无法弄清楚IContractResolver是否是更好的方法(例如以某种方式将幻像属性附加到MyValues,反序列化value并将其放入Values

如果我创建一个自定义转换器,如何告诉它通常反序列化其他所有内容(例如,如果Other具有额外的属性,请确保它们被正确处理,等等)

要创建一个自定义JsonConverterJsonConverter类型的一些属性进行特殊处理,但对剩余部分使用默认处理,您可以将JSON加载到JObject ,分离并处理自定义属性,然后使用JsonSerializer.Populate()填充JObject的余数JsonSerializer.Populate() ,如下:

 class MyValuesConverter : CustomPropertyConverterBase { protected override void ProcessCustomProperties(JObject obj, MyValues value, JsonSerializer serializer) { // Remove the value property for manual deserialization, and deserialize var jValue = obj.GetValue("value", StringComparison.OrdinalIgnoreCase).RemoveFromLowestPossibleParent(); if (jValue != null) { (value.Values = value.Values ?? new List()).Clear(); value.Values.Add(jValue.ToObject(serializer)); } } } public abstract class CustomPropertyConverterBase : JsonConverter where T : class { public override bool CanConvert(Type objectType) { return typeof(T).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var jObj = JObject.Load(reader); var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType); var value = existingValue as T ?? (T)contract.DefaultCreator(); ProcessCustomProperties(jObj, value, serializer); // Populate the remaining properties. using (var subReader = jObj.CreateReader()) { serializer.Populate(subReader, value); } return value; } protected abstract void ProcessCustomProperties(JObject obj, T value, JsonSerializer serializer); public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } public static class JsonExtensions { public static JToken RemoveFromLowestPossibleParent(this JToken node) { if (node == null) return null; var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault(); if (contained != null) contained.Remove(); // Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should if (node.Parent is JProperty) ((JProperty)node.Parent).Value = null; return node; } } 

您可以在JsonConverter创建一个仅设置属性Value ,而不是编写MyValues ,如下所示:

 public class MyValues { [JsonProperty] Stuff Value { set { (Values = Values ?? new List(1)).Clear(); Values.Add(value); } } public List Values { get; set; } public Thing Other { get; set; } } 

如果用[JsonProperty]标记,它可以是公共的或私有的。 在这种情况下,如果在JSON中遇到单例"value"属性,Json.NET将调用Value setter,如果遇到数组"values"属性,则调用Values setter。 由于属性是set-only,因此只会重新序列化数组属性。