我有一个JSON响应包含在外部数组中,如下所示:
[
{
"type": "a"
},
{
"type": "b",
"id": 1
},
{
"type": "c",
"name": "somename"
}
]
我试图将此转换为这样的对象:
class LoginOptions
{
public IList<ILoginOption> Options { get; set; }
}
interface ILoginOption
{
[JsonProperty("type")]
LoginType LoginType { get; }
}
class LoginOptionA : ILoginOption{
public LoginType LoginType
{
get { return LoginType.A; }
}
}
class LoginOptionB : ILoginOption{
public LoginType LoginType
{
get { return LoginType.B; }
}
[JsonProperty("id")]
public int Id { get; set; }
}
class LoginOptionC : ILoginOption{
public LoginType LoginType
{
get { return LoginType.C; }
}
[JsonProperty("name")]
public string Name { get; set; }
}
导致此异常:
无法将当前JSON数组(例如[1,2,3])反序列化为类型&#39; Library.Models.Domain.LoginOptions&#39;因为类型需要JSON对象(例如{&#34; name&#34;:&#34; value&#34;})才能正确反序列化。 要修复此错误,请将JSON更改为JSON对象(例如{&#34; name&#34;:&#34; value&#34;})或将反序列化类型更改为实现集合接口的数组或类型(例如ICollection,IList)就像可以从JSON数组反序列化的List。 JsonArrayAttribute也可以添加到类型中,以强制它从JSON数组反序列化。
我宁愿不在我的LoginOptions
类中实现一个集合,因为当我能够将它存储在字段中时,这将是一个太多的开销。使用[JsonArray]
属性将返回无法创建和填充列表类型Library.Models.Domain.LoginOptions 。
我发现的大多数资源都是顶级数组的{"name":"value"}
对,而不是匿名数组。我该如何反序化呢?
我已相应更改了我的代码:
public sealed class LoginOptions
{
[JsonConverter(typeof(LoginOptionConverter))]
public IList<ILoginOption> Options { get; set; }
}
我的呼叫调度员解析JSON的地方:
private List<ILoginOption> DeserializeObject<List<ILoginOption>>(Stream stream)
{
using (StreamReader sr = new StreamReader(stream))
using (JsonReader reader = new JsonTextReader(sr))
{
return new JsonSerializer().Deserialize<List<ILoginOption>>(reader);
}
}
这样的自定义转换器:
internal class LoginOptionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ILoginOption);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var item = JObject.Load(reader);
var data = item["type"].Value<string>();
if (data == "UsernamePassword")
{
return item.ToObject<LoginOptionA>();
}
if (data == "Saml")
{
return item.ToObject<LoginOptionB>();
}
if (data == "Crm")
{
return item.ToObject<LoginOptionC>();
}
throw new ArgumentException("Invalid JSON response");
}
}
这会引发错误
无法创建Library.Models.Domain.ILoginOption类型的实例。 Type是接口或抽象类,无法实例化。
使用
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
};
建议here 没有任何区别。
请注意,在自定义转换器中创建此错误之前会抛出此错误:创建JsonSerializer
时会抛出此错误。
实例化发生在这里:
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
};
using (StreamReader sr = new StreamReader(stream))
using (JsonReader reader = new JsonTextReader(sr))
{
return JsonSerializer.Create(settings).Deserialize<List<ILoginOption>>(reader);
}
答案 0 :(得分:7)
由于JSON中的顶级是一个数组,因此您应该直接反序列化为一个列表。不需要包装类来保存列表。
List<ILoginOption> loginOptions =
JsonConvert.DeserializeObject<List<ILoginOption>>(json);
现在,因为您的列表将包含几种不同类型的ILoginObjects
,所以Json.Net将无法知道要对列表进行反序列化时要创建哪些类型。为此,您需要JsonConverter
。 This answer显示了如何创建转换器来处理此问题。