问题
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