如何基于另一个 JSON 属性有条件地反序列化 JSON 对象

本文关键字:JSON 反序列化 对象 有条件 属性 何基于 另一个 | 更新日期: 2023-09-27 17:57:00

假设我有以下模型类:

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 反序列化为该类:

{
    "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

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

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

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

谢谢!

如何基于另一个 JSON 属性有条件地反序列化 JSON 对象

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) },
            // Add additional dictionary entries corresponding to each different subtype of ActionResult
        };
        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];
    }
    // Add enum values for Type corresponding to each different subtype of ActionResult
    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()。 这会自动填充ActionResult之外的所有属性,避免了手动反序列化每个属性的需要。

演示小提琴在这里:https://dotnetfiddle.net/2I2oVP

灵感来自 http://json.codeplex.com/discussions/56031:

public sealed class ActionModelConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(ActionModel).IsAssignableFrom(objectType);
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jObject = JObject.Load(reader);
        ActionModel actionModel = new ActionModel();
        // TODO: Manually populate properties
        actionModel.Id = (string)jObject["id"].ToObject<string>();
        var type = (ActionModel.Type)jObject["type"].ToObject<ActionModel.Type>();
        switch (type)
        {
          case ActionModel.Type.Open:
            var actionResult = jObject["result"].ToObject<ActionOpenResult>(jsonSerializer);
          default:
            throw new JsonSerializationException($"Unsupported action type: '{type}'");
        }
        actionModel.Result = actionResult;
        return actionModel;
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

在编辑器中编码,很抱歉错别字:)