如何在不将转换器传递给 DeserializeObject 的情况下反序列化抽象项列表?
How to deserialize a list of abstract items without passing converters to DeserializeObject?
我要进行一些继承,需要自定义 JsonConverter
进行反序列化。我现在使用一种非常直接的方法,我根据某些属性的存在来确定类型。
重要说明: 在我的 实际 代码中我无法触及 DeserializeObject
调用,即我无法在那里添加自定义转换器.我知道这在某种程度上是一个 XY 问题,因此我意识到我的回答可能是我想要的是不可能的。据我所知,这使我的问题与 this question.
略有不同
这是我的情况的重现:
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; }
}
这是自定义转换器:
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;
}
}
这适用于 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>());
}
}
后面的测试是红色的,因为:
Newtonsoft.Json.JsonReaderException : Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path 'OtherPets', line 1, position 33.
我也在 OtherPets
上尝试过不使用自定义转换器,结果是:
Newtonsoft.Json.JsonSerializationException : Could not create an instance of type JsonConverterLists.Mammal. Type is an interface or abstract class and cannot be instantiated. Path 'OtherPets[0].Lives', line 1, position 42.
我明白发生了什么,我什至知道我可以修复它:
var actual = JsonConvert.DeserializeObject<Person>(json, new PetConverter());
但重复上面的说明:我无法更改 DeserializeObject
调用,因为它包含在我目前无法更改的库中的函数中。
有没有办法用基于属性的方法来做同样的事情,例如是否有一个内置的列表转换器,其中每个条目都包含一个自定义转换器?或者我是否也必须为此推出自己的单独转换器?
脚注,如何复制:
- Visual Studio 2013 => 全新的 .NET 4.5.1 Class 库
Install-Package Newtonsoft.Json -Version 7.0.1
Install-Package nunit -Version 2.6.4
您可以将以上三个代码块放入您的新命名空间并 运行 NUnit 测试,看到第二个失败。
稍微调整了转换器class。希望一切顺利 -
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)
{
var li = new List<Mammal>();
var arr = JArray.Load(reader);
foreach (JObject obj in arr)
{
if (obj["Drools"] != null)
{
var k = obj.ToObject<Dog>(serializer);
li.Add(k);
}
}
return li;
}
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;
}
}
根据@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;
}
}
我要进行一些继承,需要自定义 JsonConverter
进行反序列化。我现在使用一种非常直接的方法,我根据某些属性的存在来确定类型。
重要说明: 在我的 实际 代码中我无法触及 DeserializeObject
调用,即我无法在那里添加自定义转换器.我知道这在某种程度上是一个 XY 问题,因此我意识到我的回答可能是我想要的是不可能的。据我所知,这使我的问题与 this question.
这是我的情况的重现:
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; }
}
这是自定义转换器:
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;
}
}
这适用于 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>());
}
}
后面的测试是红色的,因为:
Newtonsoft.Json.JsonReaderException : Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path 'OtherPets', line 1, position 33.
我也在 OtherPets
上尝试过不使用自定义转换器,结果是:
Newtonsoft.Json.JsonSerializationException : Could not create an instance of type JsonConverterLists.Mammal. Type is an interface or abstract class and cannot be instantiated. Path 'OtherPets[0].Lives', line 1, position 42.
我明白发生了什么,我什至知道我可以修复它:
var actual = JsonConvert.DeserializeObject<Person>(json, new PetConverter());
但重复上面的说明:我无法更改 DeserializeObject
调用,因为它包含在我目前无法更改的库中的函数中。
有没有办法用基于属性的方法来做同样的事情,例如是否有一个内置的列表转换器,其中每个条目都包含一个自定义转换器?或者我是否也必须为此推出自己的单独转换器?
脚注,如何复制:
- Visual Studio 2013 => 全新的 .NET 4.5.1 Class 库
Install-Package Newtonsoft.Json -Version 7.0.1
Install-Package nunit -Version 2.6.4
您可以将以上三个代码块放入您的新命名空间并 运行 NUnit 测试,看到第二个失败。
稍微调整了转换器class。希望一切顺利 -
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)
{
var li = new List<Mammal>();
var arr = JArray.Load(reader);
foreach (JObject obj in arr)
{
if (obj["Drools"] != null)
{
var k = obj.ToObject<Dog>(serializer);
li.Add(k);
}
}
return li;
}
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;
}
}
根据@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;
}
}