简体   繁体   中英

How to de-serialize JSON array containing objects of different class types that have same parent class

I have two classes that have the same parent class as below:

public class Parent
{
    public string Name { get; set; } = string.Empty;
    public string Type { get; set; } = string.Empty;
}

public class ChildInteger : Parent
{
    public int Value { get; set; } = 0;
}

public class ChildDateTime : Parent
{
    public DateTime Value { get; set; } = DateTime.MinValue;
}

Now I have an API that returns a JSON string of type array containing objects of types ChildInteger, ChildDateTime.

JSON string:

[{"Value":1,"Name":"Child Integer 1","Type":"Integer"},{"Value":"2020-08-31T08:29:11.9002559+05:30","Name":"Child DateTime 1","Type":"DateTime"}]

How to de-serialize back to correct types, so that value property is not lost?

  1. The below way will not work because value property is lost.

    List parentList1 = new List(); parentList1 = JsonConvert.DeserializeObject<List>(json);

Note: These classes are from third-party API. So need to achieve it without modifying classes.

Why not try something like this:

public class Parent
{
    public string Name { get; set; } = string.Empty;
    public string Type { get; set; } = string.Empty;
}

public class ChildInteger : Parent
{
    public int Value { get; set; } = 0;
}

public class ChildDateTime : Parent
{
    public DateTime Value { get; set; } = DateTime.MinValue;
}
    
public static void Main()
{
        var json = "[{\"Value\":1,\"Name\":\"Child Integer 1\",\"Type\":\"Integer\"},{\"Value\":\"2020-08-31T08:29:11.9002559+05:30\",\"Name\":\"Child DateTime 1\",\"Type\":\"DateTime\"}]";
        List<Parent> jarray = JArray.Parse(json).AsEnumerable().Select<JToken, Parent>(x => {
        switch(x["Type"].ToObject<string>()) 
        {
            case "Integer":
                return x.ToObject<ChildInteger>();
            case "DateTime":
                return x.ToObject<ChildDateTime>();
        }
            return null;
        }).ToList();
        
   Console.WriteLine(jarray.Count()); // prints 2
   foreach(var p in jarray) {
      Console.WriteLine(p.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public).GetValue(p));
   }
// prints
// 1
// 08/31/2020 02:59:11
}

Note: I had to add inheritance for the Child types that was missing in your listing and change Date property to Value for ChildDateTime .

There are many solutions to this. Assuming you didn't want an explicit converter, and wanted discrete types, don't want to use object or dynamic, then you could use JsonSubTypes . It should be noted, this would be a little more applicable to more complicated schemas... However, Add pepper and salt to taste

Given

[JsonConverter(typeof(JsonSubtypes), "type")]
[JsonSubtypes.KnownSubType(typeof(ChildInteger), "Integer")]
[JsonSubtypes.KnownSubType(typeof(ChildDateTime), "DateTime")]
public abstract class Child
{
   [JsonProperty("type")]
   public virtual string Type { get; }

   public string Name { get; set; }

   public abstract string GetValue();
}

public class ChildInteger : Child
{
   public override string Type => "Integer";
   public int Value { get; set; }

   public override string GetValue()
      => Value.ToString();
}

public class ChildDateTime : Child
{
   public override string Type => "DateTime";
   public DateTime Value { get; set; }

   public override string GetValue()
      => Value.ToString();
}

Usage

var input = "[{\"Value\":1,\"Name\":\"Child Integer 1\",\"Type\":\"Integer\"},{\"Value\":\"2020-08-31T08:29:11.9002559+05:30\",\"Name\":\"Child DateTime 1\",\"Type\":\"DateTime\"}]";
var results = JsonConvert.DeserializeObject<List<Child>>(input);

foreach (var item in results)
   Console.WriteLine($"{item.Name}, {item.GetValue()}");

Output

Child Integer 1, 1 
Child DateTime 1, 08/31/2020 02:59:11

Full Demo here

If your goal is to only deserialized your JSON with different value types, may be you can prefer to use dynamic property. Reference: Using type dynamic .

public class Parent
{
    public string Name { get; set; } = string.Empty;
    public string Type { get; set; } = string.Empty;
    public dynamic Value { get; set; }
}

Deserialize normally var result = JsonConvert.DeserializeObject<List<Parent>>(json); .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM