我正在尝试反序列化派生类型,并且我想使用自定义属性Type来区分派生类型。
[
{
"Type": "a",
"Height": 100
},
{
"Type": "b",
"Name": "Joe"
}
]
Run Code Online (Sandbox Code Playgroud)
我找到的解决方案是创建一个自定义JsonConverter. 在ReadJson我读取Type属性并通过ToObject<T>函数实例化该类型时。一切正常,直到我使用JsonConverterAttribute. 该ReadJson方法无限循环,因为该属性也应用于子类型。
如何防止将此属性应用于子类型?
[JsonConverter(typeof(TypeSerializer))]
public abstract class Base
{
private readonly string type;
public Base(string type)
{
this.type = type;
}
public string Type { get { return type; } }
}
public class AType : Base
{
private readonly int height;
public AType(int height)
: base("a")
{
this.height = height;
}
public int Height { get { return height; } }
}
public class BType : Base
{
private readonly string name;
public BType(string name)
: base("b")
{
this.name = name;
}
public string Name { get { return name; } }
}
public class TypeSerializer : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Base);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var j = JObject.Load(reader);
var type = j["Type"].ToObject<string>();
if (type == "a")
// Infinite Loop! StackOverflowException
return j.ToObject<AType>();
if (type == "b")
return j.ToObject<BType>();
throw new NotImplementedException(type);
}
}
[TestFixture]
public class InheritanceSerializeTests
{
[Test]
public void Deserialize()
{
var json = @"{""Type"":""a"", ""Height"":100}";
JObject.Parse(json).ToObject<Base>(); // Crash
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
我目前正在处理的一个项目遇到了一个非常相似的问题:我想进行自定义JsonConverter并通过属性将其映射到我的实体,但随后代码陷入了无限循环。
在我的情况下,使用serializer.Populate而不是什么技巧JObject.ToObject(.ToObject即使我想使用我也无法使用;我使用的是版本 3.5.8,其中不存在此功能)。下面以我的ReadJson方法为例:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JContainer lJContainer = default(JContainer);
if (reader.TokenType == JsonToken.StartObject)
{
lJContainer = JObject.Load(reader);
existingValue = Convert.ChangeType(existingValue, objectType);
existingValue = Activator.CreateInstance(objectType);
serializer.Populate(lJContainer.CreateReader(), existingValue);
}
return existingValue;
}
Run Code Online (Sandbox Code Playgroud)