0

I have 3 classes; they contain each other in the following manner:

TestClass1
    - TestClass2
        - TestClass3
        - TestClass3
    - TestClass2
        - TestClass3
        - TestClass3

What I'm trying to do is to serialize and then deserialize this class structure.

Here are the classes:

class TestClass1
{
    public List<object> Data { get; set; }
}

class TestClass2
{
    public string Data1 { get; internal set; }
    public TestClass3 Data2 { get; internal set; }
}

public TestClass3(DateTime dateTime, string v1, string v2, int v3)
{
    this.dateTime = dateTime;
    this.v1 = v1;
    this.v2 = v2;
    this.v3 = v3;
}

The code that I'm running is here:

var data = new List<TestClass2>()
{
    new TestClass2()
    {
        Data1 = "test1",
        Data2 = new TestClass3(new DateTime(2021, 02, 05), "Test", "Test2", 1234)
    },
    new TestClass2()
    {
        Data1 = "test2",
        Data2 = new TestClass3(new DateTime(2021, 02, 06), "Test", "Test3", 1234)
    },
    new TestClass2()
    {
        Data1 = "test1",
        Data2 = new TestClass3(new DateTime(2021, 02, 07), "Test23", "Test2", 5545)
    },
};

var dataStream = new TestClass1()
{
    Data = data.Select(a => (object)a).ToList(),                
};

var streamSerialized = JsonSerializer.Serialize(dataStream);

var objects = JsonSerializer.Deserialize<TestClass1>(streamSerialized);

There are two issues here

Firstly, the objects don't serialize properly streamSerialized doesn't contain any data for TestClass2.Data2. Secondly, when I deserialize, it doesn't correctly deserialize into the class structure - that is, if I do:

var class2 = (TestClass2)objects.Data.First();

I get this error:

System.InvalidCastException: 'Unable to cast object of type 'System.Text.Json.JsonElement' to type 'ConsoleApp1.TestClass2'

dbc
  • 104,963
  • 20
  • 228
  • 340
  • 2
    Serialiser don't know how to deserialise list of `object` types to the list of `TestClass2` types. – Fabio Dec 29 '20 at 11:32
  • 1
    You can't use `object` with the serializer. You need to explicitly use the types you expect, or otherwise manually read and write the properties on a `JsonElement` if the type is "unknown". This is a security feature. – Martin Costello Dec 29 '20 at 11:34
  • `public List Data` Why are you using `object` here? – mjwills Dec 29 '20 at 11:42
  • If I change `public List Data` to `public List Data` I still don't get anything serialized (streamSerialized Data2 is still empty) –  Dec 29 '20 at 11:47
  • I missed something ? it look ok https://dotnetfiddle.net/QPrFCA – Drag and Drop Dec 29 '20 at 12:20
  • @DragandDrop Apparently the fiddle code behaves differently - I don't get any data in Data2 –  Dec 29 '20 at 14:21

1 Answers1

0

Deserialization works based on type provided as generic parameter to Deserialize() and does not know anything about the original types.

If you take the debugger, you can see what happens:

You serialize to json. JSON does not contain any information about the original types:

var streamSerialized = JsonSerializer.Serialize(dataStream);

You deserialize it and tell the JsonSerializer to use information from TestClass1:

var objects = JsonSerializer.Deserialize<TestClass1>(streamSerialized);

TestClass1 tells the JsonSerializer that all list items are objects:

class TestClass1
{
    public List<object> Data { get; set; }
}

Nothing allows the serializer to know that list items should be of type TestClass2 and so it deserializes to list of JsonElements (https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonelement?view=net-5.0)

... and JsonElement cannot be cast to TestClass2.

First fix

public List<TestClass2> Data

Now take a look at your TestClass2:

class TestClass2
{
    public string Data1 { get; internal set; }
    public TestClass3 Data2 { get; internal set; }
}

Setters are internal and so the value will be null. Provide a constructor or make them public.

class TestClass2
{
    public string Data1 { get; set; }
    public TestClass3 Data2 { get; set; }
}

For constructor usage see: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-immutability?pivots=dotnet-5-0

More details on the general behavior can be found in the docs: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-5-0#deserialization-behavior

Christoph Lütjen
  • 5,403
  • 2
  • 24
  • 33