2016-02-10 13 views
6

Tôi đã có một số thừa kế, yêu cầu một tuỳ chỉnh JsonConverter để deserialization. Tôi đang sử dụng một cách tiếp cận rất đơn giản cho bây giờ, nơi tôi xác định loại dựa trên sự tồn tại của các thuộc tính nhất định.Làm thế nào để deserialize một danh sách các mặt hàng trừu tượng mà không cần chuyển đổi để DeserializeObject?

Lưu ý quan trọng: trong thực tế mã của tôi Tôi không thể chạm vào DeserializeObject cuộc gọi, ví dụ: tôi không thể thêm convertors tùy chỉnh đó. Tôi biết điều này là do một số mức độ một vấn đề XY, và nhận ra như vậy câu trả lời của tôi có thể là những gì tôi muốn là không thể. Theo như tôi có thể nói điều này làm cho câu hỏi của tôi hơi khác với this question.

Dưới đây là một repro về tình hình của tôi:

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; } 
} 

Và đây là bộ chuyển đổi tùy chỉnh:

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; 
    } 
} 

này hoạt động tốt cho FavoritePet, nhưng không quá nhiều cho OtherPets vì đó là một danh sách . Đây là một cách để tái tạo vấn đề của tôi với các bài kiểm tra 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>()); 
    } 
} 

Các thử nghiệm thứ hai là màu đỏ bởi vì:

Newtonsoft.Json.JsonReaderException: Lỗi đọc JObject từ JsonReader. Mục JsonReader hiện tại không phải là một đối tượng: StartArray. Path 'OtherPets', dòng 1, vị trí 33.

Tôi cũng đã cố gắng mà không cần bộ chuyển đổi tùy chỉnh trên OtherPets, mà kết quả trong:

Newtonsoft.Json.JsonSerializationException: Không thể tạo một thể hiện của loại JsonConverterLists.Mammal. Kiểu là một giao diện hoặc lớp trừu tượng và không thể được khởi tạo. Path 'OtherPets [0] .Lives', dòng 1, vị trí 42.

tôi hiểu những gì đang xảy ra, tôi thậm chí không biết rằng tôi có thể sửa chữa nó với:

var actual = JsonConvert.DeserializeObject<Person>(json, new PetConverter()); 

Nhưng lặp lại lưu ý từ trên: Tôi không thể thay đổi cuộc gọi DeserializeObject vì nó được bao bọc bên trong một hàm trong thư viện mà hiện tại tôi không thể thay đổi.

Có cách nào để thực hiện tương tự với phương pháp dựa trên thuộc tính hay không, ví dụ: có bộ chuyển đổi tích hợp cho danh sách trong đó mỗi mục nhập trong bộ chuyển đổi tùy chỉnh không? Hay tôi phải cuộn công cụ chuyển đổi riêng của mình cho điều này?


Footnote, làm thế nào để tái tạo:

  • Visual Studio 2013 => tươi mới NET 4.5.1 Class Library
  • Install-Package Newtonsoft.Json -Version 7.0.1
  • Install-Package nunit -Version 2.6.4

Bạn chỉ có thể thả ba khối mã trên trong namespace tươi của bạn và chạy các bài kiểm tra NUnit, nhìn thấy cái thứ hai thất bại.

Trả lời

0

Dựa off @ đề nghị AmitKumarGhosh, nếu bạn muốn thêm trách nhiệm cho danh sách này để chuyển đổi tương tự, bạn có thể làm điều này để làm các bài kiểm tra pass:

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; 
    } 
} 
+0

Mã của bạn trông đầy hứa hẹn nhưng đang ném Ngoại lệ Stackoverflow ... Có vẻ như jsonObject.ToObject đang gọi ReadJson – Bidou

+0

@Bidou Hmm, lạ. Theo như tôi có thể nhớ tôi đã sử dụng mã từ câu hỏi * như là * với giải pháp này, và các bài kiểm tra đơn vị làm việc tốt. Là repro của bạn cũng * chính xác * giống nhau, hoặc tinh tế khác nhau? Bạn sử dụng phiên bản NewtonSoft nào? – Jeroen

+0

Mmhmhm Tôi quản lý để làm cho nó hoạt động bằng cách tạo ra một lớp mới có nguồn gốc từ 'DefaultContractResolver'. Điều này ngăn chặn hành vi kỳ quặc này ... – Bidou

1

đã điều chỉnh lớp chuyển đổi một chút. hy vọng nó tốt -

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; 
    } 
} 
+0

upvoted như thế này dẫn đến [câu trả lời công việc] (http://stackoverflow.com/a/35310669/419956). Không thể chấp nhận nó như là mặc dù, vì nó sẽ không vượt qua bài kiểm tra/nó vẫn có một hoặc hai lỗi. – Jeroen

Các vấn đề liên quan