如何基于另一个JSON属性有条件地反序列化JSON对象? [英] How to conditionally deserialize JSON object based on another JSON property?

查看:50
本文介绍了如何基于另一个JSON属性有条件地反序列化JSON对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有以下模型类:

public class Action
{
    public enum Type
    {
        Open,
        Close,
        Remove,
        Delete,
        Reverse,
        Alert,
        ScaleInOut,
        Nothing
    }

    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("active")]
    [JsonConverter(typeof(IntToBoolConverter))]
    public bool Active { get; set; }

    [JsonProperty("type")]
    [JsonConverter(typeof(ActionTypeConverter))]
    public Type ActionType { get; set; }

    [JsonProperty("result")]
    [JsonConverter(typeof(ActionResultConverter))]
    public ActionResult Result { get; set; }
}

,我想将以下JSON反序列化到该类中:

and I want to deserialize following JSON into that class:

{
    "name":"test1",
    "id":"aa0832f0508bb580ce7f0506132c1c13",
    "active":"1",
    "type":"open",
    "result":{
        "property1":"buy",
        "property2":"123.123",
        "property3":"2016-07-16T23:00:00",
        "property4":"768",
        "property5":true
     }
}

结果对象每次都可以不同(6个模型之一),其类型取决于JSON属性type.

Result object can be different each time (one of 6 models) and its type depends on JSON property type.

我已经创建了自定义ActionResultConverter(在Action类的Result属性上方的JsonConverter批注),该自定义ActionResultConverter应该可以创建特定的 result 对象基于JSON的type属性中的字符串.

I have created custom ActionResultConverter (JsonConverter annotation above Result property of Action class) that should be able to create specific result object based on string in type property of JSON.

我的问题是我不知道如何从转换器访问该属性,因为只有整个JSON的result部分才传递给JsonReader.

My problem is that I don't know how to access that property from converter because only the result part of whole JSON is passed to JsonReader.

任何想法或帮助将不胜感激.

Any ideas or help will be appreciated.

谢谢!

推荐答案

Json.NET不提供在反序列化子对象时访问JSON层次结构中父对象的属性值的方法.根据

Json.NET does not provide a method to access the value of a property of a parent object in the JSON hierarchy while deserializing a child object. Likely this is because a JSON object is defined to be an unordered set of name/value pairs, according to the standard, so there can be no guarantee the desired parent property occurs before the child in the JSON stream.

因此,您需要在Action本身的转换器中处理它,而不是在ActionResult的转换器中处理Type属性:

Thus, rather than handling the Type property in a converter for ActionResult, you'll need to do it in a converter for Action itself:

[JsonConverter(typeof(ActionConverter))]
public class Action
{
    readonly static Dictionary<Type, System.Type> typeToSystemType;
    readonly static Dictionary<System.Type, Type> systemTypeToType;

    static Action()
    {
        typeToSystemType = new Dictionary<Type, System.Type>
        {
            { Type.Open, typeof(OpenActionResult) },
        };
        systemTypeToType = typeToSystemType.ToDictionary(p => p.Value, p => p.Key);
    }

    public static Type SystemTypeToType(System.Type systemType)
    {
        return systemTypeToType[systemType];
    }

    public static System.Type TypeToSystemType(Type type)
    {
        return typeToSystemType[type];
    }

    public enum Type
    {
        Open,
        Close,
        Remove,
        Delete,
        Reverse,
        Alert,
        ScaleInOut,
        Nothing
    }

    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("active")]
    [JsonConverter(typeof(IntToBoolConverter))]
    public bool Active { get; set; }

    [JsonProperty("type")]
    [JsonConverter(typeof(ActionTypeConverter))]
    public Type ActionType { get; set; }

    [JsonProperty("result")]
    public ActionResult Result { get; set; }
}

class ActionConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var obj = JObject.Load(reader);
        var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
        var action = existingValue as Action ?? (Action)contract.DefaultCreator();

        // Remove the Result property for manual deserialization
        var result = obj.GetValue("Result", StringComparison.OrdinalIgnoreCase).RemoveFromLowestPossibleParent();

        // Populate the remaining properties.
        using (var subReader = obj.CreateReader())
        {
            serializer.Populate(subReader, action);
        }

        // Process the Result property
        if (result != null)
            action.Result = (ActionResult)result.ToObject(Action.TypeToSystemType(action.ActionType));

        return action;
    }

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

请注意在ReadJson()中使用 JsonSerializer.Populate() .这样会自动填充除Result之外的Action的所有属性,从而避免了手动对每个属性进行反序列化的情况.

Notice the use of JsonSerializer.Populate() inside ReadJson(). This automatically fills in all properties of Action other than Result, avoiding the need for manual deserialization of each.

这篇关于如何基于另一个JSON属性有条件地反序列化JSON对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆