我在C#控制台应用程序中使用了一些JSON,对于某些数据,有一系列选项。示例JSON:
{
"FIELD_NAME": "Survey",
"VALUE": "",
"FIELD_ID": 1234,
"OPTIONS":[
{ "VALUE": "GENERAL", "DISPLAY_ORDER": 1, "DISPLAY": "GENERAL" },
{ "VALUE": "HPEFS", "DISPLAY_ORDER": 3, "DISPLAY": "HPEFS" },
{ "VALUE": "NONE", "DISPLAY_ORDER": 3, "DISPLAY": "NONE" }]
}
但有时对于JSON中的记录,OPTIONS是空的:
{"FIELD_NAME":"Product_Node3","VALUE":"","FIELD_ID":1740,"OPTIONS":{}}
正如您所看到的,选项设置为{},但我理解{}是一个空对象,而不是一个空数组。
当我尝试反序列化为POCO时,我得到一个例外,抱怨它需要OPTIONS属性中的JSON数组。
我的田野课程:
public class Field
{
public string FIELD_NAME { get; set; }
public string VALUE { get; set; }
public int FIELD_ID { get; set; }
public List<Option> OPTIONS { get; set;
}
}
选项类:
public class Option
{
public string VALUE { get; set; }
public int DISPLAY_ORDER { get; set; }
public string DISPLAY { get; set; }
}
导致此异常的代码是:
var stringTest = File.ReadAllText("json.txt");
var data = JsonConvert.DeserializeObject<List<Field>>(stringTest);
例外是:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[testproj.Option]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
答案 0 :(得分:1)
当预期的JSON值类型(数组,集合或基元)与观察值类型不匹配时,Json.NET将抛出异常。因为,对于List<Option> OPTIONS
,您希望跳过意外的值类型,因此您需要创建custom JsonConverter
,如下所示:
public class TolerantCollectionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
if (objectType.IsPrimitive || objectType == typeof(string) || objectType.IsArray)
return false;
return objectType.GetCollectionItemTypes().Count() == 1;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
else if (reader.TokenType == JsonToken.StartArray)
{
existingValue = existingValue ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
serializer.Populate(reader, existingValue);
return existingValue;
}
else
{
reader.Skip();
return existingValue;
}
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public static class TypeExtensions
{
/// <summary>
/// Return all interfaces implemented by the incoming type as well as the type itself if it is an interface.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<Type> GetInterfacesAndSelf(this Type type)
{
if (type == null)
throw new ArgumentNullException();
if (type.IsInterface)
return new[] { type }.Concat(type.GetInterfaces());
else
return type.GetInterfaces();
}
public static IEnumerable<Type> GetCollectionItemTypes(this Type type)
{
foreach (Type intType in type.GetInterfacesAndSelf())
{
if (intType.IsGenericType
&& intType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
yield return intType.GetGenericArguments()[0];
}
}
}
}
然后将其应用于Field
,如下所示:
public class Field
{
public string FIELD_NAME { get; set; }
public string VALUE { get; set; }
public int FIELD_ID { get; set; }
[JsonConverter(typeof(TolerantCollectionConverter))]
public List<Option> OPTIONS { get; set; }
}
或通过JsonSerializerSettings
将其用于所有馆藏:
var settings = new JsonSerializerSettings
{
Converters = { new TolerantCollectionConverter() },
};
var obj = JsonConvert.DeserializeObject<Field>(stringTest, settings);
注意:
转换器仅适用于可写的集合,因为它首先分配集合然后填充它。对于只读集合或数组,您需要首先填充List<T>
,然后从中分配只读集合或数组。
我的假设是,当需要数组值时,您希望忽略一个空对象。如果您希望将对象反序列化为集合项,然后将其添加到返回的集合,则可以使用How to handle both a single item and an array for the same property using JSON.net中的SingleOrArrayConverter<T>
。
您问题中显示的根JSON容器是一个对象 - 一组无序的名称/值对,以{
开头,以}
结尾 - 而不是数组。因此,您需要将其反序列化为Field
而不是List<Field>
。
示例fiddle。