How can I deserialize a child instance as a parent object without losing its specific property?

ぃ、小莉子 提交于 2019-12-22 09:57:32

问题


Below are several classes. How can I serialize a Json string of an instance of A in which PropertyB contains either SpecPropB1 or SpecPropB2 so that those properties remains in the C# object?

public class A
{
  public B PropertyB {get;set;}
}

public class B
{
  public string GenProp {get;set;}
}

public class B1:B
{
  public string SpecPropB1 {get;set;}
}

public class B2:B
{
  public string SpecPropB2 {get;set;}
}

I see multiple solutions to this problem but they lack elegance:

  • Put the type of propertyB as object: I lose the strong typing for this property.
  • Redeserialize the problematic part as a child object: this will quickly become ugly
  • Create a specific class ASerialisable (which have B1 and B2 property), serialise the string to this, then use a constructor to create an A object from this class: same as above, I am afraid that it will create a lot of classes if there is a lot of similar problem. *Add the type of the object before serializing and use the property TypeNameHandling. I don't see a way of doing that that doesn't involve a partial deserialisation.

Similar problem, but I am nor the one which serialize the string, so i can't use this solution: Deserialize into correct child objects

EDIT:

I make a new class inheriting JSonConverter which automatically do the job proposed by Falanwe. Here is the final code for those needing it.

private class BConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(A).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader,
        Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject item = JObject.Load(reader);
        if(objectType.IsSubclassOf(typeof(B)) || objectType == typeof(B))
        {
            if (item["SpecPropB1"] != null)
            {
                return new B1() 
                { 
                    GenProp = (string)item["GenProp"], 
                    SpecPropB1 = (string)item["SpecPropB1"] 
                };
            }
            else if (item["SpecPropB2"] != null)
            {
                return new B2()
                {
                    GenProp = (string)item["GenProp"],
                    SpecPropB2 = (string)item["SpecPropB2"]
                };
            }
            else
            {
                return new B()
                {
                    GenProp = (string)item["GenProp"]
                };
            }
        }
        else
        {
            return item.ToObject(objectType);
        }
    }

    public override void WriteJson(JsonWriter writer,
        object value, JsonSerializer serializer)
    {
        //not implemented here but needed if you want to deserialized data,
        // this can help you: http://blog.maskalik.com/asp-net/json-net-implement-custom-serialization/
        throw new NotImplementedException();
    }
}

private class A
{

    public B PropertyB { get; set; }
}

[JsonConverter(typeof(BConverter))]
private class B
{
    public string GenProp { get; set; }
}

private class B1 : B
{
    public string SpecPropB1 { get; set; }
}

private class B2 : B
{
    public string SpecPropB2 { get; set; }
} 

回答1:


If the situation arises in only a few cases, do serialize into a DTO (Data Transfer Object) class that has both properties. If you don't want to bother creating a new named class, you can also use an anonymous class that will do the job for you

var myDto = JsonConvert.DeserializeAnonymousType(jsonString,
    new {
        PropertyB = new {
            GenProp ="",
            SpecPropB1 ="",
            SpecPropB2 = ""}
        });

If the case arrises too frequently, that is if you are unsure of the schema of the json you're deserializing, you can deserialize into a JObject, and then query if fields are present or not.

B b;
JObject o = JObject.Parse(jsonString);

// I need this variable to compile, but I won't use it.
JToken _;
if(o.TryGetValue("SpecPropB1", out _)
{
    b = o.ToObject<B1>();
}
else
{
//...
}


来源:https://stackoverflow.com/questions/28026386/how-can-i-deserialize-a-child-instance-as-a-parent-object-without-losing-its-spe

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!