【问题标题】:Deserializing a collection of interfaces反序列化接口集合
【发布时间】:2017-01-19 03:40:06
【问题描述】:

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

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 用于inside Form.Controls 集合中的项目?

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

    • 就是这样!谢谢。
    【解决方案2】:

    我不确定您的 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);
    }
    

    【讨论】:

    • 我不想/在我的 json 中有默认类型信息 ($type prop)。
    • 您将需要它,否则它如何知道对象是什么类型?
    • MyJsonConverter 通过检查自定义属性了解它
    • 那个属性是什么?如果您对哪些属性或某物如何确定类型有更多了解,我们可以进一步提供帮助。
    • 没那么重要。足以说MyJsonConverter 反序列化IControlALL 实现。我使用这种方法:skrift.io/articles/archive/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-24
    • 1970-01-01
    相关资源
    最近更新 更多