I would like to serialize the list of derived objects to json and to deserialize. Serialization works fine, the deserialize fails to convert the objects to derived class objects, all objects are deserialized to base class objects and lose members that belong to the derived class. I have base class AutoEvent, and derived classes MouseClickEvent and ClickImageEvent, so once i deserialize to List all objects are of type AutoEvent. How can i deserialize so that i can convert each object to the derived class object?
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
}
}
}
CodePudding user response:
From the documentation about javascriptserializer
For .NET Framework 4.7.2 and later versions, use the APIs in the System.Text.Json namespace for serialization and deserialization. For earlier versions of .NET Framework, use Newtonsoft.Json. This type was intended to provide serialization and deserialization functionality for AJAX-enabled applications
I.e. you should probably be using another json library.
If you prefer Json.Net, See Json.net serialize/deserialize derived types?. If you prefer System.Text.Json, see How to serialize properties of derived classes with System.Text.Json
CodePudding user response:
when you run
events = ser.Deserialize<List<AutoEvent>>(json);
you can get only this
[
{
"Name": "mouse click1",
"DelayMs": 100
},
{
"Name": "mouse click2",
"DelayMs": 15
},
{
"Name": "image click1",
"DelayMs": 15
},
{
"Name": "image click2",
"DelayMs": 125
}
]
it doesn't matter what is in the json, it will be automatically cut to data that is in AutoEvent class since you are using it for deserialization.
In order to get all information, you have to use the highest inheritance level to deserialize, not the lowest one. This code was tested in VS and working properly.
List<ClickImageEvent> events1 = ser.Deserialize<List<ClickImageEvent>>(json);
MouseClickEvent event1 = (MouseClickEvent)events[0];
ClickImageEvent event2 = (ClickImageEvent)events[2];
and all classes should inherit from each other
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() { }
}
or if you want to derive from the AutoEvent only, you can user mapper or convert manually or using linq Select
var ev =events[0];
MouseClickEvent event1 = new MouseClickEvent( ev.Name, ev.DelayMs, ev.SeachArea,...);
if you need to convert using linq for example, you will have to add type of the class
public class AutoEvent
{
public string TypeName { get; set; } = "Auto";
.....
}
that should be assign when you create the object, before serialiazation.
CodePudding user response:
One way is to use DeserializeObject method which takes json text as input and returns object. Object is in this case list of 4 object elements (because i serialized list of 4 elements) and each element of the list is an object, but it is in fact tuple of property name and value of the property. So this method is not directly converting to List, i have to iterate over the resulting objects and assign the values of resulting objects to the MouseClickEvent and ClickImageEvent objects.
object result = ser.DeserializeObject(json);