在 Asp.Net Web API 2 控制器中反序列化嵌套的 ICollection<BaseType>

Deserialize nested ICollection<BaseType> in Asp.Net Web API 2 controller

我有一个像这样的 Web Api 控制器:

public IHttpActionResult Create(PaymentDTO Payment)

我的 DTO 是:

public class PaymentDTO
{
    public int Id { get; set; }

    public string type { get; set; }

    public IEnumerable<TransactionDTO> Transactions { get; set; }

}

public class TransactionDTO
    {
        public int Id { get; set; }
        public string Description { get; set; }
        public string CreateTime { get; set; }
        public string UpdateTime { get; set; }
    }

public class SaleDTO : TransactionDTO
{
        public string Total { get; set; }
        public string Currency{ get; set; }
}

public class OrderDTO : TransactionDTO
{
       public string State {get;set;}
}

我收到以下 JSON 格式的数据:

{
  "Type": "sale",
  "Id": 101,
  "transactions": [
    {
      "Total": "30.50",
      "Currency": "USD",
      "Description": "transaction description"
    }
  ]
}

我希望 JSON.net 根据 Type 属性.

实例化 IEnumerable<SaleDTO>IEnumerable<OrderDTO>

我本可以使用自定义类型转换器,但前提是 Type 属性 在 TransactionDTO 中。但我希望 Type 属性 在父对象中 (PaymentDTO)

预先感谢您的帮助。

您可以在 PaymentDTO class:

上使用 custom JsonConverter
public class PaymentDTOConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(PaymentDTO).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var obj = JObject.Load(reader);
        var payment = (PaymentDTO)existingValue ?? new PaymentDTO();

        // Extract the transactions.
        var transactions = obj.Property("transactions") ?? obj.Property("Transactions");
        if (transactions != null)
            transactions.Remove();

        // Populate the remaining regular properties.
        using (var subReader = obj.CreateReader())
            serializer.Populate(subReader, payment);

        if (transactions != null)
        {
            // Deserialize the transactions list.
            var type = PaymentDTO.GetTransactionDTOType(payment.type) ?? typeof(TransactionDTO);
            using (var subReader = transactions.Value.CreateReader())
                // Here we are taking advantage of array covariance.
                payment.Transactions = (IEnumerable<TransactionDTO>)serializer.Deserialize(subReader, type.MakeArrayType());
        }

        return payment;
    }

    public override bool CanWrite { get { return false; } }

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

然后将其应用于您的 PaymentDTO class,如下所示:

[JsonConverter(typeof(PaymentDTOConverter))]
public class PaymentDTO
{
    static Dictionary<string, Type> namesToTransactions;
    static Dictionary<Type, string> transactionsToNames = new Dictionary<Type, string>
    {
        { typeof(SaleDTO), "sale" },
        { typeof(OrderDTO), "order" },
    };

    static PaymentDTO()
    {
        namesToTransactions = transactionsToNames.ToDictionary(p => p.Value, p => p.Key);
    }

    public static string GetTransactionDTOTypeName<TTransactionDTO>() where TTransactionDTO : TransactionDTO
    {
        string name;
        if (transactionsToNames.TryGetValue(typeof(TTransactionDTO), out name))
            return name;
        return null;
    }

    public static Type GetTransactionDTOType(string name)
    {
        Type type;
        if (namesToTransactions.TryGetValue(name, out type))
            return type;
        return null;
    }

    public int Id { get; set; }

    public string type { get; set; }

    [JsonProperty("transactions")]
    public IEnumerable<TransactionDTO> Transactions { get; set; }
}