2014-06-15 14 views
8

Tôi có một thức ăn JSON trông như thế này (tôi loại bỏ một số lĩnh vực mà không phải là cần thiết ví dụ này):Làm thế nào để deserialize bộ sưu tập với các loại khác nhau?

{ 
    "total_count": 2, 
    "num_pages": 1, 
    "current_page": 1, 
    "balance": { 
    "amount": "0.00001199", 
    "currency": "BTC" 
    }, 
    "transactions": [ 
    { 
     "transaction": { 
     "id": "5018f833f8182b129c00002f", 
     "created_at": "2012-08-01T02:34:43-07:00", 
     "sender": { 
      "id": "5011f33df8182b142400000e", 
      "name": "User Two", 
      "email": "[email protected]" 
     }, 
     "recipient": { 
      "id": "5011f33df8182b142400000a", 
      "name": "User One", 
      "email": "[email protected]" 
     } 
     } 
    }, 
    { 
     "transaction": { 
     "id": "5018f833f8182b129c00002e", 
     "created_at": "2012-08-01T02:36:43-07:00", 
     "hsh": "9d6a7d1112c3db9de5315b421a5153d71413f5f752aff75bf504b77df4e646a3", 
     "sender": { 
      "id": "5011f33df8182b142400000e", 
      "name": "User Two", 
      "email": "[email protected]" 
     }, 
     "recipient_address": "37muSN5ZrukVTvyVh3mT5Zc5ew9L9CBare" 
     } 
    } 
] 
} 

Có hai loại giao dịch trong thức ăn này: giao dịch nội bộ mà có một recipient, và các giao dịch bên ngoài có hshrecipient_address.

Tôi tạo ra các lớp sau để chứa cấu trúc này:

UML

Vì vậy, chúng ta có một lớp cơ sở cho tất cả các kết quả paged (PagedResult) với một thực hiện cụ thể cho các giao dịch (TransactionPagedResult). Kết quả này có một bộ sưu tập chứa 0 .. * giao dịch (lớp trừu tượng Transaction). Tuy nhiên, chúng không thuộc loại Transaction nhưng thuộc loại InternalTransaction hoặc ExternalTransaction đang triển khai Transaction.

Câu hỏi của tôi là cách tôi có thể cho phép JSON.NET xử lý việc này. Tôi muốn JSON.NET để xem liệu giao dịch hiện tại của nó là phân tích cú pháp là InternalTransaction hoặc ExternalTransaction và thêm loại theo dõi vào bộ sưu tập IEnumerable<Transaction> trong TransactionPagedResult.

Tôi tạo ra JsonConverter của riêng tôi mà tôi thêm vào như là một tài sản cho IEnumerable<Transaction> với thuộc tính [JsonConverter(typeof(TransactionCreationConverter))], nhưng điều này không làm việc, tôi nhận được lỗi sau:

Additional information: Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path 'transactions', line 1, position 218.

Tôi hiểu điều này là bởi vì JSON. NET cố gắng deserialize toàn bộ bộ sưu tập, nhưng tôi muốn nó deserialize từng đối tượng bên trong bộ sưu tập từng người một.

Bất kỳ ai?

+0

bài đăng tốt cho đến giờ, nhưng bạn có thể vui lòng thêm thông tin về ý chính xác của bạn ..."nhưng điều này không hoạt động" ở cuối bài đăng của bạn? –

+0

Có, tôi không thêm điều đó vì tôi không muốn bài đăng trở nên quá dài. Tôi nhận được một lỗi rằng các JsonReader mục không phải là một đối tượng nhưng một mảng (đó là chính xác, kể từ khi tôi đặt thuộc tính trên bộ sưu tập IEnumerable). Tôi muốn JsonConverter để xem xét tất cả các mục duy nhất trong IEnumerable mặc dù, đó là tất cả các đối tượng. –

+0

@Jack Tôi nghĩ bạn đã liên kết với chuỗi không đúng? –

Trả lời

6

Câu hỏi của bạn về cơ bản là bản sao của this one và giải pháp là như nhau. Bạn cần một JsonConverter để khởi tạo đối tượng chính xác. Tuy nhiên, có một vài sự khác biệt mà tôi thấy.

Nếu bạn nhìn vào việc thực hiện chuyển đổi từ other answer, bạn có thể thấy rằng nó tìm kiếm cờ boolean trong JSON để xác định loại để khởi tạo. Trong trường hợp của bạn, không có lá cờ như vậy, vì vậy bạn cần sử dụng sự tồn tại hoặc vắng mặt của một trường để đưa ra quyết định này. Ngoài ra, danh sách các giao dịch của bạn trong JSON thực sự là danh sách các đối tượng mà chứa các giao dịch, vì vậy trình chuyển đổi cũng cần tính đến điều đó.

Với những thay đổi, chuyển đổi của bạn sẽ giống như thế này:

public class TransactionConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(Transaction).IsAssignableFrom(objectType); 
    } 

    public override object ReadJson(JsonReader reader, 
     Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JToken transaction = JToken.Load(reader)["transaction"]; 
     if (transaction["recipient"] != null) 
     { 
      return transaction.ToObject<InternalTransaction>(); 
     } 
     else 
     { 
      return transaction.ToObject<ExternalTransaction>(); 
     } 
    } 

    public override void WriteJson(JsonWriter writer, 
     object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

giả định rằng lớp học của bạn được định nghĩa như thế này:

class TransactionPagedResult 
{ 
    [JsonProperty(ItemConverterType=typeof(TransactionConverter))] 
    public IEnumerable<Transaction> Transactions { get; set; } 
} 

class Transaction 
{ 
    public string Id { get; set; } 
    [JsonProperty("created_at")] 
    public DateTime CreatedAt { get; set; } 
} 

class InternalTransaction : Transaction 
{ 
    public User Recipient { get; set; } 
} 

class ExternalTransaction : Transaction 
{ 
    public string Hsh { get; set; } 
    [JsonProperty("recipient_address")] 
    public string RecipientAddress { get; set; } 
} 

class User 
{ 
    public string Id { get; set; } 
    public string Name { get; set; } 
    public string Email { get; set; } 
} 

Ngoài ra, để trả lời phần cuối của câu hỏi của bạn, nếu bạn trang trí danh sách của mình với thuộc tính [JsonConverter], trình chuyển đổi dự kiến ​​sẽ xử lý toàn bộ danh sách. Để xử lý các mục riêng lẻ, bạn cần sử dụng [JsonProperty(ItemConverterType=typeof(TransactionConverter))] trên danh sách thay thế. Tôi đã chỉnh sửa các định nghĩa lớp ở trên để làm rõ điều này.

+0

Tuyệt vời! Điều này làm các trick :) Cảm ơn rất nhiều cho các ví dụ và giải thích! :) –

+0

Không sao cả; mừng vì tôi có thể giúp. –

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