繁体   English   中英

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

[英]Deserialize two values into the same property

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

一种服务仅发送一个值:

{
  "value" : { ... }
}

第二个服务始终返回多个值:

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

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

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

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

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

除了编写JsonConverter ,您还可以在MyValues上设置一个仅设置属性的Value ,如下所示:

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

    public List<Stuff> Values { get; set; }
    public Thing Other { get; set; }
}

如果标记为[JsonProperty] ,则可以是公共的或私有的。 在这种情况下,如果在JSON中遇到单例"value"属性,则Json.NET将调用Value设置器,如果在数组中遇到"values"属性,则将调用Values设置器。 由于该属性仅设置,因此仅array属性将被重新序列化。

为了使定制的JsonConverter对某种类型的一些属性进行特殊处理,但对其余类型使用默认处理,可以将JSON加载到JObject ,分离并处理定制属性,然后使用JsonSerializer.Populate()JObject填充其余的JsonSerializer.Populate() ,如下所示:

class MyValuesConverter : CustomPropertyConverterBase<MyValues>
{
    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<Stuff>()).Clear();
            value.Values.Add(jValue.ToObject<Stuff>(serializer));
        }
    }
}

public abstract class CustomPropertyConverterBase<T> : 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;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM