Jer*_*oen 6 c# json abstract-class json.net
我有一些继承,需要一个自定义JsonConverter反序列化.我现在正在使用一种非常简单的方法,根据某些属性的存在来确定类型.
重要说明:在我的实际代码中,我无法触摸DeserializeObject调用,即我无法在那里添加自定义转换器.我知道这在某种程度上是一个XY问题,因此我的答案可能就是我想要的不可能.据我所知,这使我的问题与这个问题略有不同.
这是我的情况的一个副本:
abstract class Mammal { }
class Cat : Mammal { public int Lives { get; set; } }
class Dog : Mammal { public bool Drools { get; set; } }
class Person
{
[JsonConverter(typeof(PetConverter))]
public Mammal FavoritePet { get; set; }
[JsonConverter(typeof(PetConverter))]
public List<Mammal> OtherPets { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这是自定义转换器:
public class PetConverter : JsonConverter
{
public override bool CanConvert(Type objectType) { return objectType == typeof(Mammal); }
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null) return null;
JObject jsonObject = JObject.Load(reader);
if (jsonObject["Lives"] != null) return jsonObject.ToObject<Cat>(serializer);
if (jsonObject["Drools"] != null) return jsonObject.ToObject<Dog>(serializer);
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
这适用于FavoritePet,但不是很多,OtherPets因为它是一个列表.这是一种用NUnit测试重现我的问题的方法:
[TestFixture]
class MyTests
{
[Test]
public void CanSerializeAndDeserializeSingleItem()
{
var person = new Person { FavoritePet = new Cat { Lives = 9 } };
var json = JsonConvert.SerializeObject(person);
var actual = JsonConvert.DeserializeObject<Person>(json);
Assert.That(actual.FavoritePet, Is.InstanceOf<Cat>());
}
[Test]
public void CanSerializeAndDeserializeList()
{
var person = new Person { OtherPets = new List<Mammal> { new Cat { Lives = 9 } } };
var json = JsonConvert.SerializeObject(person);
var actual = JsonConvert.DeserializeObject<Person>(json);
Assert.That(actual.OtherPets.Single(), Is.InstanceOf<Cat>());
}
}
Run Code Online (Sandbox Code Playgroud)
后一种测试是红色的,因为:
Newtonsoft.Json.JsonReaderException:从JsonReader读取JObject时出错.当前的JsonReader项不是对象:StartArray.路径'OtherPets',第1行,第33位.
我也试过没有自定义转换器OtherPets,这导致:
Newtonsoft.Json.JsonSerializationException:无法创建JsonConverterLists.Mammal类型的实例.Type是接口或抽象类,无法实例化.路径'OtherPets [0] .Lives',第1行,第42位.
我明白发生了什么,我甚至知道我可以解决它:
var actual = JsonConvert.DeserializeObject<Person>(json, new PetConverter());
Run Code Online (Sandbox Code Playgroud)
但重复上面的注释:我无法更改DeserializeObject调用,因为它包含在库中的函数中,我目前无法更改.
有没有办法对基于属性的方法做同样的事情,例如,是否有内置的转换器用于列表,其中每个条目都采用自定义转换器?或者我也必须为此推出自己的单独转换器?
脚注,如何重现:
Install-Package Newtonsoft.Json -Version 7.0.1Install-Package nunit -Version 2.6.4你可以在新命名空间中删除上面三个代码块并运行NUnit测试,看到第二个代码块失败.
根据 @AmitKumarGhosh 的建议,如果您想将列表的责任添加到同一个转换器,您可以这样做以使测试通过:
public class PetConverter : JsonConverter
{
public override bool CanConvert(Type objectType) { return objectType == typeof(Mammal); }
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
if (reader.TokenType == JsonToken.StartArray)
{
return JArray.Load(reader)
.Cast<JObject>()
.Select(o => JObjectToMammal(o, serializer))
.ToList();
}
return JObjectToMammal(JObject.Load(reader), serializer);
}
private Mammal JObjectToMammal(JObject jsonObject, JsonSerializer serializer)
{
if (jsonObject["Lives"] != null) return jsonObject.ToObject<Cat>(serializer);
if (jsonObject["Drools"] != null) return jsonObject.ToObject<Dog>(serializer);
return null;
}
}
Run Code Online (Sandbox Code Playgroud)