解析数组和单个对象
Parsing both Array and single object
API 重新调整 json 如下 2 种形式的对象。
表格 1
{
"Pricing": [
{
"total": 27,
"currency": "USD",
"charges": [ //Chargers Array
{
"code": "C1",
"currency": "USD",
"rate": 15
},
{
"code": "C45",
"currency": "USD",
"rate": 12
}
]
}
]
}
表格 2
{
"Pricing": [
{
"total": 12,
"currency": "USD",
"charges": { //Chargers single object
"code": "C1",
"currency": "USD",
"rate": 12
}
}
]
}
如您所见,有时充电器对象 return 带有数组,有时则没有。我的问题是如何将其解析为 C# class 对象?如果我像下面那样添加了 C# class,它就无法正确解析 Form 2。(Form 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; }
}
使用 Newtonsoft 反序列化进行解析时出错。
MainObj obj = JsonConvert.DeserializeObject<MainObj>(json);
错误
Cannot deserialize the current JSON object (e.g. {"name":"value"})
into type 'System.Collections.Generic.List`1[Charge]' because the type
requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix
this error either change the JSON to a JSON array (e.g. [1,2,3]) or
change the deserialized type so that it is a normal .NET type (e.g.
not a primitive type like integer, not a collection type like an array
or List) that can be deserialized from a JSON object.
JsonObjectAttribute can also be added to the type to force it to
deserialize from a JSON object. Path 'Pricing[0].charges.code', line
1, position 69.
在使用 C# 接收不同类型的对象类型时,是否有任何常用的解析方法?
(我也研究了 ,但它是针对 java 的。大多数此类问题都是针对 java 而不是 C# 提出的。)
您可以尝试将 Form1 的对象解析为 json,如果失败,它将使用 Form2 的对象解析为 json。
示例如下:https://dotnetfiddle.net/F1Yh25
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
string form1 = "{\"Pricing\":[{\"total\":27,\"currency\":\"USD\",\"charges\":[{\"code\":\"C1\",\"currency\":\"USD\",\"rate\":15},{\"code\":\"C45\",\"currency\":\"USD\",\"rate\":12}]}]}";
string form2 = "{\"Pricing\":[{\"total\":12,\"currency\":\"USD\",\"charges\":{\"code\":\"C1\",\"currency\":\"USD\",\"rate\":12}}]}";
string json = form1;//Change form1 to form2 and it will also work
try
{
Form1.Root Object = JsonConvert.DeserializeObject<Form1.Root>(json);
Console.WriteLine("Item 1's Pricing: " + Object.Pricing[0].total);
}
catch//If Form1's json is Form2 it will catch here
{
Form2.Root Object = JsonConvert.DeserializeObject<Form2.Root>(json);
Console.WriteLine("Item 1's Pricing: " + Object.Pricing[0].total);
}
}
public class Form1
{
public class Charge
{
public string code { get; set; }
public string currency { get; set; }
public int rate { get; set; }
}
public class Pricing
{
public int total { get; set; }
public string currency { get; set; }
public List<Charge> charges { get; set; }
}
public class Root
{
public List<Pricing> Pricing { get; set; }
}
}
public class Form2
{
public class Charges
{
public string code { get; set; }
public string currency { get; set; }
public int rate { get; set; }
}
public class Pricing
{
public int total { get; set; }
public string currency { get; set; }
public Charges charges { get; set; }
}
public class Root
{
public List<Pricing> Pricing { get; set; }
}
}
}
好的,这里有另一种方法可以做到这一点,而不必使用两个 classes 并且没有 try catch。基本上只需将 Pricing class 更新为 following,它适用于这两种情况。可能是更好的方法,但这更好(至少在我看来)并且有两个 classes 并让 try catch 做你的分支。如果您有 10 个属性存在这个问题,那么您会创建 10 个吗? class是否可以处理每个组合?没办法哈哈!
public class Pricing {
public int total { get; set; }
public string currency { get; set; }
private List<Charge> _charges;
public object charges {
get {
return _charges;
}
set {
if(value.GetType().Name == "JArray") {
_charges = JsonConvert.DeserializeObject<List<Charge>>(value.ToString());
}
else {
_charges = new List<Charge>();
var charge = JsonConvert.DeserializeObject<Charge>(value.ToString());
_charges.Add(charge);
}
}
}
}
另一种处理此问题的方法是定义一个可以处理这两种情况的自定义 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();
}
- 里面的
ReadJson
首先我们得到一个JToken
来判断读取值的Type
(种类)
- 基于此我们可以调用
ToObject<List<T>>
或 ToObject<T>
- 在
CanConvert
中,我们检查要填充的 属性 类型是 List<T>
- 尽管有一个通用的
JsonConverter<T>
,您不必在其中定义 CanConvert
,但它的 ReadJson
可以以更复杂的方式实现
- 因为问题是关于反序列化我还没有实现
WriteJson
方法
- 您也可以考虑将基数 class 的
CanWrite
属性 覆盖为始终 return false
有了这个 class 在我们手中,您可以用 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; }
...
}
API 重新调整 json 如下 2 种形式的对象。
表格 1
{
"Pricing": [
{
"total": 27,
"currency": "USD",
"charges": [ //Chargers Array
{
"code": "C1",
"currency": "USD",
"rate": 15
},
{
"code": "C45",
"currency": "USD",
"rate": 12
}
]
}
]
}
表格 2
{
"Pricing": [
{
"total": 12,
"currency": "USD",
"charges": { //Chargers single object
"code": "C1",
"currency": "USD",
"rate": 12
}
}
]
}
如您所见,有时充电器对象 return 带有数组,有时则没有。我的问题是如何将其解析为 C# class 对象?如果我像下面那样添加了 C# class,它就无法正确解析 Form 2。(Form 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; }
}
使用 Newtonsoft 反序列化进行解析时出错。
MainObj obj = JsonConvert.DeserializeObject<MainObj>(json);
错误
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Charge]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'Pricing[0].charges.code', line 1, position 69.
在使用 C# 接收不同类型的对象类型时,是否有任何常用的解析方法?
(我也研究了
您可以尝试将 Form1 的对象解析为 json,如果失败,它将使用 Form2 的对象解析为 json。
示例如下:https://dotnetfiddle.net/F1Yh25
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
string form1 = "{\"Pricing\":[{\"total\":27,\"currency\":\"USD\",\"charges\":[{\"code\":\"C1\",\"currency\":\"USD\",\"rate\":15},{\"code\":\"C45\",\"currency\":\"USD\",\"rate\":12}]}]}";
string form2 = "{\"Pricing\":[{\"total\":12,\"currency\":\"USD\",\"charges\":{\"code\":\"C1\",\"currency\":\"USD\",\"rate\":12}}]}";
string json = form1;//Change form1 to form2 and it will also work
try
{
Form1.Root Object = JsonConvert.DeserializeObject<Form1.Root>(json);
Console.WriteLine("Item 1's Pricing: " + Object.Pricing[0].total);
}
catch//If Form1's json is Form2 it will catch here
{
Form2.Root Object = JsonConvert.DeserializeObject<Form2.Root>(json);
Console.WriteLine("Item 1's Pricing: " + Object.Pricing[0].total);
}
}
public class Form1
{
public class Charge
{
public string code { get; set; }
public string currency { get; set; }
public int rate { get; set; }
}
public class Pricing
{
public int total { get; set; }
public string currency { get; set; }
public List<Charge> charges { get; set; }
}
public class Root
{
public List<Pricing> Pricing { get; set; }
}
}
public class Form2
{
public class Charges
{
public string code { get; set; }
public string currency { get; set; }
public int rate { get; set; }
}
public class Pricing
{
public int total { get; set; }
public string currency { get; set; }
public Charges charges { get; set; }
}
public class Root
{
public List<Pricing> Pricing { get; set; }
}
}
}
好的,这里有另一种方法可以做到这一点,而不必使用两个 classes 并且没有 try catch。基本上只需将 Pricing class 更新为 following,它适用于这两种情况。可能是更好的方法,但这更好(至少在我看来)并且有两个 classes 并让 try catch 做你的分支。如果您有 10 个属性存在这个问题,那么您会创建 10 个吗? class是否可以处理每个组合?没办法哈哈!
public class Pricing {
public int total { get; set; }
public string currency { get; set; }
private List<Charge> _charges;
public object charges {
get {
return _charges;
}
set {
if(value.GetType().Name == "JArray") {
_charges = JsonConvert.DeserializeObject<List<Charge>>(value.ToString());
}
else {
_charges = new List<Charge>();
var charge = JsonConvert.DeserializeObject<Charge>(value.ToString());
_charges.Add(charge);
}
}
}
}
另一种处理此问题的方法是定义一个可以处理这两种情况的自定义 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();
}
- 里面的
ReadJson
首先我们得到一个JToken
来判断读取值的Type
(种类)- 基于此我们可以调用
ToObject<List<T>>
或ToObject<T>
- 基于此我们可以调用
- 在
CanConvert
中,我们检查要填充的 属性 类型是List<T>
- 尽管有一个通用的
JsonConverter<T>
,您不必在其中定义CanConvert
,但它的ReadJson
可以以更复杂的方式实现
- 尽管有一个通用的
- 因为问题是关于反序列化我还没有实现
WriteJson
方法- 您也可以考虑将基数 class 的
CanWrite
属性 覆盖为始终 returnfalse
- 您也可以考虑将基数 class 的
有了这个 class 在我们手中,您可以用 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; }
...
}