如何反序列化不同类型的集合?

Posted

技术标签:

【中文标题】如何反序列化不同类型的集合?【英文标题】:How to deserialize collection with different types? 【发布时间】:2014-08-05 10:54:16 【问题描述】:

我有一个如下所示的 JSON 提要(我删除了本示例不需要的一些字段):


  "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": "user2@example.com"
        ,
        "recipient": 
          "id": "5011f33df8182b142400000a",
          "name": "User One",
          "email": "user1@example.com"
        
      
    ,
    
      "transaction": 
        "id": "5018f833f8182b129c00002e",
        "created_at": "2012-08-01T02:36:43-07:00",
        "hsh": "9d6a7d1112c3db9de5315b421a5153d71413f5f752aff75bf504b77df4e646a3",
        "sender": 
          "id": "5011f33df8182b142400000e",
          "name": "User Two",
          "email": "user2@example.com"
        ,
        "recipient_address": "37muSN5ZrukVTvyVh3mT5Zc5ew9L9CBare"
      
    
 ]

此提要中有两种类型的交易:具有recipient 的内部交易,以及具有hshrecipient_address 的外部交易。

我创建了以下类来适应这种结构:

因此,我们为所有分页结果 (PagedResult) 提供了一个基类,并为事务 (TransactionPagedResult) 提供了特定实现。这个结果有一个包含 0..* 事务的集合(抽象类Transaction)。它们不是Transaction 类型,而是InternalTransactionExternalTransaction 类型,它们是Transaction 的实现。

我的问题是如何让 JSON.NET 处理这个问题。我希望 JSON.NET 查看它正在解析的当前事务是 InternalTransaction 还是 ExternalTransaction,并将相应的类型添加到 TransactionPagedResult 中的 IEnumerable<Transaction> 集合中。

我创建了自己的 JsonConverter,并使用 [JsonConverter(typeof(TransactionCreationConverter))] 属性将其作为属性添加到 IEnumerable<Transaction>,但这不起作用,我收到以下错误:

附加信息:从 JsonReader 读取 JObject 时出错。当前的 JsonReader 项不是对象:StartArray。路径“交易”, 第 1 行,位置 218。

我理解这是因为 JSON.NET 试图反序列化整个集合,但我希望它一个一个地反序列化集合中的每个对象。

有人吗?

【问题讨论】:

到目前为止的帖子不错,但是您能否在帖子的最后添加您所说的...“但这不起作用”的确切含义? 是的,我没有添加,因为我不希望帖子变得太长。我收到一个错误,指出 JsonReader 项不是对象而是数组(这是正确的,因为我将属性放在 IEnumerable 集合上)。我希望 JsonConverter 查看 IEnumerable 中的每个项目,它们都是对象。 @Jack 我认为你链接到了错误的线程? 这有帮助吗? ***.com/questions/7699972/… 编辑:固定链接 Deserializing polymorphic json classes without type information using json.net的可能重复 【参考方案1】:

你的问题本质上是this one的重复,解决方法是一样的。您需要一个 JsonConverter 来实例化正确的对象。但是,我看到了一些不同之处。

如果您查看 other answer 中的转换器实现,您可以看到它在 JSON 中查找布尔标志来确定要实例化的类型。在您的情况下,没有这样的标志,因此您需要使用字段的存在或不存在来做出此决定。此外,JSON 中的交易列表实际上是 包含 交易的对象列表,因此转换器也需要考虑这一点。

通过这些更改,您的转换器应如下所示:

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();
    

假设你的类是这样定义的:

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; 

另外,为了回答你问题的最后一部分,如果你用[JsonConverter] 属性装饰你的列表,转换器应该会处理整个列表。要处理单个项目,您需要使用列表中的[JsonProperty(ItemConverterType=typeof(TransactionConverter))]。我已经编辑了上面的类定义以明确这一点。

【讨论】:

太棒了!这可以解决问题:) 非常感谢您的示例和解释! :) 没问题;很高兴我能帮上忙。

以上是关于如何反序列化不同类型的集合?的主要内容,如果未能解决你的问题,请参考以下文章

无法反序列化泛型类型的集合

如何反序列化具有相同名称但不同类型的 API 响应

如何反序列化不同类型的 JSON 数组?

如何配置Jackson使其能够反序列化特殊集合?

guid的值为null如何在C#反序列化

不同程序集版本的二进制反序列化