解析数组和单个对象

wee*_*raa 2 c# parsing json json.net

API 返回 json 对象,如下所示 2 种形式。

表1

{
"Pricing": [
    {
        "total": 27,
        "currency": "USD",
        "charges": [ //Chargers Array
            {
                "code": "C1",
                "currency": "USD",
                "rate": 15
            },
            {
                "code": "C45",
                "currency": "USD",
                "rate": 12
            }
        ]
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

表2

{
"Pricing": [
    {
        "total": 12,
        "currency": "USD",
        "charges": { //Chargers single object
            "code": "C1",
            "currency": "USD",
            "rate": 12
        }
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,充电器对象有时会返回数组,有时则不会。我的问题是如何将其解析为 C# 类对象?如果我添加如下所示的 C# 类,则无法正确解析表单 2。(正确解析表单 1)

public class Charge
{
    public string code { get; set; }
    public string currency { get; set; }
    public decimal rate { get; set; }
}

public class Pricing
{
    public decimal total { get; set; }
    public string currency { get; set; }
    public List<Charge> charges { get; set; } //In Form 2, this should be single object
}

public class MainObj
{
    public List<Pricing> Pricing { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

使用Newtonsoft反序列化解析时发生错误。

MainObj obj = JsonConvert.DeserializeObject<MainObj>(json);
Run Code Online (Sandbox Code Playgroud)

错误

无法将当前 JSON 对象(例如 {"name":"value"})反序列化为类型 'System.Collections.Generic.List`1[Charge]',因为该类型需要 JSON 数组(例如 [1,2,3] )以正确反序列化。要修复此错误,请将 JSON 更改为 JSON 数组(例如 [1,2,3])或更改反序列化类型,使其成为普通的 .NET 类型(例如,不是像整数这样的原始类型,不是像这样的集合类型)数组或列表),可以从 JSON 对象反序列化。还可以将 JsonObjectAttribute 添加到类型中以强制其从 JSON 对象反序列化。路径“Pricing[0].charges.code”,第 1 行,位置 69。

使用 C# 接收不同类型的对象类型时,有什么通用的解析方法吗?

(我也研究了这个,但它是针对 java 的。大多数此类问题是针对 java 而不是 C# 提出的。)

Pet*_*ala 6

处理这个问题的另一种方法是定义一个JsonConverter可以处理这两种情况的自定义。

class ArrayOrObjectConverter<T> : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        return token.Type == JTokenType.Array
                ? token.ToObject<List<T>>()
                : new List<T> { token.ToObject<T>() };
    }

    public override bool CanConvert(Type objectType)
        => objectType == typeof(List<T>);

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        =>throw new NotImplementedException(); 
}
Run Code Online (Sandbox Code Playgroud)
  • 在第一个内部ReadJson我们得到一个JToken能够确定读取值的Type(种类)
    • 基于此,我们可以致电ToObject<List<T>>ToObject<T>
  • 在里面CanConvert我们检查要填充的属性的类型是List<T>
    • 即使有一个JsonConverter<T>您不必定义的泛型CanConvert,它也ReadJson可以以更复杂的方式实现
  • 由于问题都是关于反序列化的,所以我没有实现该WriteJson方法
    • 您还可以考虑重写CanWrite基类的属性以始终返回false

有了这个类,您可以用 a 来装饰您的属性,JsonConverterAttribute告诉 Json.NET 如何处理这些属性

public class Pricing
{
    public decimal total { get; set; }
    public string currency { get; set; }

    [JsonConverter(typeof(ArrayOrObjectConverter<Charge>))]
    public List<Charge> charges { get; set; } 
    
    ...
}
Run Code Online (Sandbox Code Playgroud)