反序列化接口集合

时间:2017-01-19 03:40:07

标签: c# .net json serialization json.net

我有以下类,它是完全可序列化的:

public class Form {
    public IList<IControl> Controls { get; set; }
}

public class ControlA : IControl {}
public class ControlB : IControl {}

它序列化没有 $type信息,但我有一个自定义JsonConverter,可以反序列化 IControl的所有实现:

internal class MyJsonConverter : CustomCreationConverter<IControl> {}

它适用于这样的场景:

[JsonConverter(typeof(MyJsonConverter ))]
public IControl MyControl {get;set;}

但是,我无法将相同的JsonConverterAttribute应用于我的Form.Controls媒体资源:

  

“从JsonReader读取JObject时出错。当前的JsonReader项目不是   一个对象:StartArray。路径'控制',第5行,第15位。“

如何指示反序列化程序在 MyJsonConverter集合中使用项目<{1}}?

2 个答案:

答案 0 :(得分:1)

我不确定您MyJsonConverter实际上做了什么,但作为一个基本示例,您只需要提供JsonSerializerSettings并将TypeNameHandling属性设置为TypeNameHandling.All

这个简单的例子有效。

public interface IControl { }

public class Form
{
    public IList<IControl> Controls { get; set; }
}

public class ControlA : IControl { }
public class ControlB : IControl { }

static void Main(string[] args)
{
    var form = new Form();
    form.Controls = new List<IControl>();

    form.Controls.Add(new ControlA());
    form.Controls.Add(new ControlB());
    var json = JsonConvert.SerializeObject(form, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
    var obj = JsonConvert.DeserializeObject<Form>(json, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
}

修改

在澄清没有使用类型处理因此没有$type属性后,我们必须更有创意并阅读原始jSON。并以一种特殊的方式构建对象。以下是客户序列化程序的示例。

internal class MyJsonConverter : CustomCreationConverter<IControl>
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jObject = JObject.Load(reader);
        var controlType = jObject["CustomProperty"]?.Value<string>();

        IControl control = null;

        if (!string.IsNullOrWhiteSpace(controlType))
        {
            switch (controlType.ToLowerInvariant())
            {
                case "controla":
                    control = Activator.CreateInstance(typeof(ControlA)) as IControl;
                    break;
                case "controlb":
                    control = Activator.CreateInstance(typeof(ControlB)) as IControl;
                    break;
            }
        }
        if (controlType == null)
            throw new SerializationException($"Unable to deserialize property. {controlType}");

        serializer.Populate(jObject.CreateReader(), control);
        return control;
    }

    public override IControl Create(Type objectType)
    {
        return null;
    }
}

基本上,当我们在IControl接口中的某个属性(在问题中已经省略)时,我们将手动解析json并获取对属性的引用CustomProperty

如果此属性存在有效字符串值(或者您可以使用任何其他值),我们将手动创建IControl

最后,处理反序列化的部分是最后一行serializer.Populate()

完整测试案例:

internal class MyJsonConverter : CustomCreationConverter<IControl>
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jObject = JObject.Load(reader);
        var controlType = jObject["CustomProperty"]?.Value<string>();

        IControl control = null;

        if (!string.IsNullOrWhiteSpace(controlType))
        {
            switch (controlType.ToLowerInvariant())
            {
                case "controla":
                    control = Activator.CreateInstance(typeof(ControlA)) as IControl;
                    break;
                case "controlb":
                    control = Activator.CreateInstance(typeof(ControlB)) as IControl;
                    break;
            }
        }
        if (controlType == null)
            throw new SerializationException($"Unable to deserialize property. {controlType}");

        serializer.Populate(jObject.CreateReader(), control);
        return control;
    }

    public override IControl Create(Type objectType)
    {
        return null;
    }
}


[JsonConverter(typeof(MyJsonConverter))]
public interface IControl
{
    string CustomProperty { get; set; }
}

public class Form
{
    public IList<IControl> Controls { get; set; }
}

public class ControlA : IControl
{
    public string CustomProperty { get; set; } = "ControlA";
}
public class ControlB : IControl
{
    public string CustomProperty { get; set; } = "ControlB";
}

static void Main(string[] args)
{
    var form = new Form();
    form.Controls = new List<IControl>();
    form.Controls.Add(new ControlA());
    form.Controls.Add(new ControlB());
    var json = JsonConvert.SerializeObject(form);
    var obj = JsonConvert.DeserializeObject<Form>(json);
}

答案 1 :(得分:1)

您可以使用[JsonProperty(ItemConverterType = typeof(MyJsonConverter))]将转换器应用于集合中的项目:

public class Form 
{
    [JsonProperty(ItemConverterType = typeof(MyJsonConverter))]
    public IList<IControl> Controls { get; set; }
}