Search code examples
c#jsonserializationjson.netdeserialization

C# Newtonsoft Json deserialize array of different inheirted objects from json file


I have an array of objects which I want to write to a json file and recover it later Point - Visual Basic class:

<Serializable> Public Class Point
    Implements IComparable
    Public Property X As Integer
    Public Property Y As Integer
    Public Sub New()
        X = 0
        Y = 0
    End Sub
    Public Sub New(ByVal x As Integer, ByVal y As Integer)
        Me.X = x
        Me.Y = y
    End Sub
End Class

Point3D - C# class

    [Serializable]
    public class Point3D : Point
    {
        public int Z { get; set; }
        public Point3D() : base()
        {
            var rnd = new Random();
            Z = rnd.Next(10);
        }
        public Point3D(int x, int y, int z) : base(x, y) 
        { 

            this.Z = z; 
        }
    }

Serialization works fine but when I try to deserialize json from file, all objects presented as Point3D (even the ones with only X and Y variables). Before deserialization: enter image description here After: enter image description here

Serialization code block:

            using (var fs = new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write))
            {
                switch (Path.GetExtension(dlg.FileName))
                {
                    ...
                    case ".json":
                        var jf = new JsonSerializer();
                        jf.TypeNameHandling= TypeNameHandling.Auto;
                        using (var w = new StreamWriter(fs))
                            jf.Serialize(w, points);
                        break;
                }
            }

Deserialization code block:

            using (var fs = new FileStream(dlg.FileName, FileMode.Open, FileAccess.Read))
            {
                switch (Path.GetExtension(dlg.FileName))
                {
                    ...
                    case ".json":
                        var jf = new JsonSerializer();
                        using (var r = new StreamReader(fs))
                            points = (Point[])jf.Deserialize(r, typeof(Point3D[]));
                        break;
                }
            }

Code I use to initialize array of objects (Point and Point3D):

        private void buttonCreate_Click(object sender, EventArgs e)
        {
            points = new Point[5];
            var rnd = new Random();
            for (int i = 0; i < points.Length; i++)
            {
                points[i] = rnd.Next(3) % 2 == 0 ? new Point() : new Point3D();
            }
            listBox.DataSource = points;
        }

And json string

{"$type":"PointLib.Point[], PointLib","$values":[{"$type":"LabOneFormsApp.Point3D, LabOneFormsApp","Z":6,"X":0,"Y":0},{"$type":"LabOneFormsApp.Point3D, LabOneFormsApp","Z":1,"X":0,"Y":0},{"$type":"PointLib.Point, PointLib","X":0,"Y":0},{"$type":"PointLib.Point, PointLib","X":0,"Y":0},{"$type":"PointLib.Point, PointLib","X":0,"Y":0}]}

I tried adding

jf.TypeNameHandling= TypeNameHandling.Auto;

to my code but it does not seem to work for me. Any ideas?


Solution

  • I've decided to do it simple way

    case ".json":
                        var reader = new StreamReader(fs).ReadToEnd();
                        var jsonFile = JsonConvert.DeserializeObject<List<Dictionary<string, int>>>(reader);
    
                        if (jsonFile != null)
                        {
                            points = new Point[jsonFile.Count];
    
                            for (int i = 0; i < jsonFile.Count; i++)
                            {
                                var pointDict = jsonFile[i];
    
                                switch (pointDict.Count)
                                {
                                    case 2:
                                        points[i] = new Point(pointDict["X"], pointDict["Y"]);
                                        break;
    
                                    case 3:
                                        points[i] = new Point3D(pointDict["X"], pointDict["Y"], pointDict["Z"]);
                                        break;
                                }
                            }
                        }
                        else
                        {
                            points = null;
                        }
    

    Thanks everyone for the answers!