【问题标题】:serialization of List of derived objects派生对象列表的序列化
【发布时间】:2022-01-07 06:11:46
【问题描述】:

我想将派生对象列表序列化为 json 并反序列化。序列化工作正常,反序列化无法将对象转换为派生类对象,所有对象都被反序列化为基类对象并丢失属于派生类的成员。 我有基类 AutoEvent,派生类 MouseClickEvent 和 ClickImageEvent,所以一次 我反序列化以列出所有对象都是 AutoEvent 类型。如何反序列化,以便将每个对象转换为派生类对象?

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Reflection;
using System.Web.Script.Serialization;

namespace AutoEvent
{            
    public class AutoEvent
    {
        public string Name { get; set; }
        public int DelayMs { get; set; }
        public AutoEvent(string name, int delayMs)
        {
            Name = name;
            DelayMs = delayMs;
        }
        public AutoEvent() { }
    }

    public class MouseClickEvent : AutoEvent
    {
        public Rectangle RectArea { get; set; }
        public bool IsDoubleClick { get; set; }
        public MouseClickEvent(string name, int delayMs, Rectangle rectArea, bool isDoubleClick) : base(name, delayMs)
        {
            RectArea = rectArea;
            IsDoubleClick = isDoubleClick;
        }
        public MouseClickEvent() { }

    }

    public class ClickImageEvent : AutoEvent
    {
        public List<string> ImgFiles { get; set; }
        public Rectangle SearchArea { get; set; }
        public double ImgTolerance { get; set; }
        public double ImgError { get; set; }
        public bool IsDoubleClick { get; set; }
        public ClickImageEvent(string name, int delayMs, Rectangle searchArea, bool isDoubleClick, double imgTol, double imgErr) : base(name, delayMs)
        {
            SearchArea = searchArea;
            IsDoubleClick = isDoubleClick;
            ImgTolerance = imgTol;
            ImgError = imgErr;
        }
        public ClickImageEvent() { }

    }

    class Program
    {
        static void Main(string[] args)
        {
            string path = @"c:/nenad/testSer.txt";
            List<AutoEvent> events = new List<AutoEvent>();
            MouseClickEvent clickEvent1 = new MouseClickEvent("mouse click1", 100, new Rectangle(20, 30, 15, 10), true);
            MouseClickEvent clickEvent2 = new MouseClickEvent("mouse click2", 15, new Rectangle(20, 45, 15, 10), true);
            ClickImageEvent imgclick1 = new ClickImageEvent("image click1", 15, new Rectangle(20, 45, 555, 150), false, 0.1, 0.05);
            ClickImageEvent imgclick2 = new ClickImageEvent("image click2", 125, new Rectangle(2220, 45, 5525, 150), false, 0.15, 0.25);
            events.Add(clickEvent1);
            events.Add(clickEvent2);
            events.Add(imgclick1);
            events.Add(imgclick2);

            JavaScriptSerializer ser = new JavaScriptSerializer();
            string json = ser.Serialize(events);
            if (!File.Exists(path))
            {
                using (var h = File.Create(path)) ;
            }

            File.WriteAllText(path, json);

            json = File.ReadAllText(path);
            events = ser.Deserialize<List<AutoEvent>>(json);
            MouseClickEvent event1 = (MouseClickEvent)events[0]; // fails at runtime to convert 
            ClickImageEvent event2 = (ClickImageEvent)events[2]; // fails at runtime to convert 
        }
    }
}

【问题讨论】:

  • 这不起作用,因为 events[0] 是 AutoEvent 类型,所以它不包含像 RectArea 和 IsDoubleClick 这样的成员,而只有 Name 和 DelayMs。

标签: c# inheritance serialization


【解决方案1】:

来自关于javascriptserializer的文档

对于 .NET Framework 4.7.2 及更高版本,使用 System.Text.Json 命名空间中的 API 进行序列化和反序列化。对于早期版本的 .NET Framework,请使用 Newtonsoft.Json。此类型旨在为支持 AJAX 的应用程序提供序列化和反序列化功能

即您可能应该使用另一个 json 库。

如果您更喜欢 Json.Net,请参阅 Json.net serialize/deserialize derived types?。如果您更喜欢 System.Text.Json,请参阅How to serialize properties of derived classes with System.Text.Json

【讨论】:

  • 我使用的序列化方法有什么区别,问题仍然存在,反序列化后有关派生对象成员的信息丢失?
  • @Nenad Birešev 在序列化库时需要包含派生类型的所有数据,以及对象的实际类型。在反序列化期间需要使用此类型信息来提供正确的对象类型。因此,绝对需要使用合适的库。
  • 你能提供简单的例子吗?
  • @Nenad Birešev 如果您点击链接,或者在您的库和多态性上进行一些谷歌搜索,应该有示例。
  • 它通过使用设置与 Newtonsoft.Json 一起工作:JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
【解决方案2】:

当你跑步时

  events = ser.Deserialize<List<AutoEvent>>(json);

你只能得到这个

[
  {
    "Name": "mouse click1",
    "DelayMs": 100
  },
  {
    "Name": "mouse click2",
    "DelayMs": 15
  },
  {
    "Name": "image click1",
    "DelayMs": 15
  },
  {
    "Name": "image click2",
    "DelayMs": 125
  }
]

json 中的内容无关紧要,因为您使用它进行反序列化,它将自动剪切为 AutoEvent 类中的数据。

为了获取所有信息,你必须使用最高的继承级别来反序列化,而不是最低的。这段代码在VS中测试过,运行正常。

List<ClickImageEvent> events1 = ser.Deserialize<List<ClickImageEvent>>(json);
 MouseClickEvent event1 = (MouseClickEvent)events[0]; 
 ClickImageEvent event2 = (ClickImageEvent)events[2];

所有类都应该相互继承

public class ClickImageEvent : MouseClickEvent
{
    public List<string> ImgFiles { get; set; }
    public Rectangle SearchArea { get; set; }
    public double ImgTolerance { get; set; }
    public double ImgError { get; set; }
    public ClickImageEvent(string name, int delayMs, Rectangle searchArea, bool isDoubleClick, double imgTol=0, double imgErr=0) : base(name, delayMs,searchArea, isDoubleClick)
    {
        SearchArea = searchArea;
        IsDoubleClick = isDoubleClick;
        ImgTolerance = imgTol;
        ImgError = imgErr;
    }
    public ClickImageEvent() { }

}

或者如果您只想从 AutoEvent 派生,您可以使用映射器或手动转换或使用 linq Select

 var ev =events[0]; 

MouseClickEvent event1 = new MouseClickEvent( ev.Name, ev.DelayMs, ev.SeachArea,...);

例如,如果您需要使用 linq 进行转换,则必须添加类的类型

public class AutoEvent
 {
  public string TypeName { get; set; } = "Auto";
.....
}

应该在创建对象时分配,在序列化之前。

【讨论】:

  • 我可以将所有成员放在 AutoEvent 中并且根本不使用派生类,这与您的建议相似,但我问如何使用我拥有基类和的派生对象列表来做到这一点正如我在问题中所解释的那样推导出来。
  • 实际上序列化 List 派生类的成员也会被序列化,因为在运行时这些对象是 ClickImageEvent 和 MouseClickEvent。序列化没问题,派生类的所有成员都被序列化了,问题是反序列化,它只反序列化 AutoEvent 基类的成员,并不能断定 json 文件中的对象是 ClickImageEvent 和 MouseClickEvent 类型。
  • @NenadBirešev 请参阅我回答的第二部分。我告诉过你,你需要手动转换或使用 linq 或 mapper
  • "或者如果您只想从 AutoEvent 派生,您可以使用映射器或手动转换或使用 linq Select" 映射器或 linq 没有帮助,因为有关派生对象成员的信息丢失了反序列化后。
  • 是的,你是对的。您必须为类型添加字符串属性
猜你喜欢
  • 2012-03-31
  • 2013-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-09
  • 1970-01-01
  • 2011-12-29
  • 1970-01-01
相关资源
最近更新 更多