派生对象列表的序列化



我想将派生对象的列表序列化为json并进行反序列化。序列化工作正常,反序列化无法将对象转换为派生类对象,所有对象都被反序列化为基类对象,并丢失属于派生类的成员。我有基类AutoEvent,以及派生类MouseClickEvent和ClickImageEvent,所以i反序列化为List所有对象都属于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 
}
}
}

来自关于javascriptserializer 的文档

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

也就是说,您可能应该使用另一个json库。

如果您更喜欢Json.Net,请参阅Json.Net序列化/反序列化派生类型?。如果您更喜欢System.Text.Json,请参阅如何使用System.Text.Json 序列化派生类的属性

运行时

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";
.....
}

应该在创建对象时分配,然后再序列化。