如何根据另一个JSON属性有条件地反序列化JSON对象?

Mar*_*bel 4 c# json json.net

假设我有以下模型类:

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; }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

我已经创建了自定义ActionResultConverter(类的属性JsonConverter上面的注释),它应该能够基于JSON属性中的字符串创建特定对象.ResultAction result type

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

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

谢谢!

dbc*_*dbc 7

在反序列化子对象时,Json.NET不提供访问JSON层次结构中父对象的属性值的方法.可能这是因为JSON对象被定义为一个组无序名称/值对,根据标准,所以不能保证所希望的父属性的JSON流中的孩子之前发生.

因此,您不需要Type在转换器中处理属性,而是ActionResult需要在转换器中为Action自己执行:

[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;
    }
}
Run Code Online (Sandbox Code Playgroud)

注意JsonSerializer.Populate()里面的使用ReadJson().这会自动填充Action除以外的所有属性Result,从而无需对每个属性进行手动反序列化.