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

vq8itlhq  于 2023-05-02  发布在  其他
关注(0)|答案(2)|浏览(89)

假设我有以下模型类:

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
我已经创建了自定义ActionResultConverterAction类的Result属性上面的JsonConverter注解),应该能够基于JSON的type属性中的字符串创建特定的result对象
我的问题是,我不知道如何从转换器访问该属性,因为只有整个JSON的result部分被传递给JsonReader
任何想法或帮助将不胜感激。
谢谢!

z5btuh9x

z5btuh9x1#

Json.NET不提供在反序列化子对象时访问JSON层次结构中父对象的属性值的方法。这可能是因为根据standard,JSON对象被定义为一个 * 无序的名称/值对集 *,因此无法保证所需的父属性出现在JSON流中的子属性之前。
因此,与其在ActionResult的转换器中处理Type属性,不如在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()。这会自动填充Action的所有属性,而不是Result,从而避免了手动反序列化每个属性的需要。
Demo fiddle在这里:https://dotnetfiddle.net/2I2oVP

jecbmhm3

jecbmhm32#

关于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();
    }
}

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

相关问题