JSON 缺少 属性 名称时出现反序列化错误
JSON deserialization error when property name is missing
我正在尝试使用外部 Web 服务,并且我正在使用 .NET Core 和 Flurl 框架。我收到如下服务的回复:
[
"Successful Request: 96 Results",
[
{
"eventdate":"2019-10-18",
"name":"",
"url":"",
"info":"",
"showtime":null,
"url_tix":"",
"event_owner":"xxx",
"follow_url":"xxx",
"event_image":"xxx",
"venue":"xxx",
"city":"xxx",
"country":"xxx",
"state":""
}
]
]
我有一个如下所示的 C# 实体定义:
public class ServiceResponce
{
public Event[] Events { get; set; }
}
public class Event
{
[JsonProperty("eventdate")]
public DateTimeOffset Eventdate { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("info")]
public string Info { get; set; }
[JsonProperty("showtime")]
public object Showtime { get; set; }
[JsonProperty("url_tix")]
public object UrlTix { get; set; }
[JsonProperty("event_owner")]
public string EventOwner { get; set; }
[JsonProperty("follow_url")]
public Uri FollowUrl { get; set; }
[JsonProperty("event_image")]
public object EventImage { get; set; }
[JsonProperty("venue")]
public string Venue { get; set; }
[JsonProperty("city")]
public string City { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("state")]
public string State { get; set; }
}
当我尝试调用 Flurl 方法来使用 Web 服务时,如下所示:
var result = await serviceUrl.GetJsonAsync<ServiceResponce>();
我收到下面提到的错误:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type
'xxx.ServiceResponce' because the type requires a JSON object (e.g.
{"name":"value"}) to deserialize correctly. To fix this error either
change the JSON to a JSON object (e.g. {"name":"value"}) or change the
deserialized type to an array or a type that implements a collection
interface (e.g. ICollection, IList) like List that can be
deserialized from a JSON array. JsonArrayAttribute can also be added
to the type to force it to deserialize from a JSON array. Path '',
line 1, position 1.
你有什么解决办法吗?欢迎任何帮助。
我认为问题出在 Json 对象上,我用 'Newtonsoft.Json' 生成了一个 class,如果你可以试试这个代码:
// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
// using MyNameSpace;
//
// var event = Event.FromJson(jsonString);
namespace MyNameSpace
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class EventClass
{
[JsonProperty("eventdate")]
public DateTimeOffset Eventdate { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("info")]
public string Info { get; set; }
[JsonProperty("showtime")]
public object Showtime { get; set; }
[JsonProperty("url_tix")]
public string UrlTix { get; set; }
[JsonProperty("event_owner")]
public string EventOwner { get; set; }
[JsonProperty("follow_url")]
public string FollowUrl { get; set; }
[JsonProperty("event_image")]
public string EventImage { get; set; }
[JsonProperty("venue")]
public string Venue { get; set; }
[JsonProperty("city")]
public string City { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("state")]
public string State { get; set; }
}
public partial struct EventUnion
{
public EventClass[] EventClassArray;
public string String;
public static implicit operator EventUnion(EventClass[] EventClassArray) => new EventUnion { EventClassArray = EventClassArray };
public static implicit operator EventUnion(string String) => new EventUnion { String = String };
}
public class Event
{
public static EventUnion[] FromJson(string json) => JsonConvert.DeserializeObject<EventUnion[]>(json, MyNameSpace.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this EventUnion[] self) => JsonConvert.SerializeObject(self, MyNameSpace.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
EventUnionConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class EventUnionConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(EventUnion) || t == typeof(EventUnion?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.String:
case JsonToken.Date:
var stringValue = serializer.Deserialize<string>(reader);
return new EventUnion { String = stringValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<EventClass[]>(reader);
return new EventUnion { EventClassArray = arrayValue };
}
throw new Exception("Cannot unmarshal type EventUnion");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (EventUnion)untypedValue;
if (value.String != null)
{
serializer.Serialize(writer, value.String);
return;
}
if (value.EventClassArray != null)
{
serializer.Serialize(writer, value.EventClassArray);
return;
}
throw new Exception("Cannot marshal type EventUnion");
}
public static readonly EventUnionConverter Singleton = new EventUnionConverter();
}
}
这里的问题是 JSON 响应实际上是一个混合类型的数组。数组的第一个元素是一个字符串,第二个元素是一个事件对象数组。您将需要自定义 JsonConverter
来反序列化此 JSON。
这是转换器所需的代码:
class ServiceResponceConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(ServiceResponce));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray ja = JArray.Load(reader);
ServiceResponce resp = new ServiceResponce();
resp.Events = ja[1].ToObject<Event[]>(serializer);
return resp;
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
然后,将 [JsonConverter]
属性添加到 ServiceResponce
class 以将其绑定到转换器:
[JsonConverter(typeof(ServiceResponceConverter))]
public class ServiceResponce
{
public Event[] Events { get; set; }
}
现在您可以正常反序列化到 ServiceResponce
class,它会正常工作。
可选:如果您还想从响应中捕获 "Successful Request: 96 Results"
字符串,请添加
public string ResultString { get; set; }
到 ServiceResponce
class 并将以下行添加到转换器的 ReadJson
方法:
resp.ResultString = (string)ja[0];
此处的工作演示:https://dotnetfiddle.net/opPUmX
我正在尝试使用外部 Web 服务,并且我正在使用 .NET Core 和 Flurl 框架。我收到如下服务的回复:
[
"Successful Request: 96 Results",
[
{
"eventdate":"2019-10-18",
"name":"",
"url":"",
"info":"",
"showtime":null,
"url_tix":"",
"event_owner":"xxx",
"follow_url":"xxx",
"event_image":"xxx",
"venue":"xxx",
"city":"xxx",
"country":"xxx",
"state":""
}
]
]
我有一个如下所示的 C# 实体定义:
public class ServiceResponce
{
public Event[] Events { get; set; }
}
public class Event
{
[JsonProperty("eventdate")]
public DateTimeOffset Eventdate { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("info")]
public string Info { get; set; }
[JsonProperty("showtime")]
public object Showtime { get; set; }
[JsonProperty("url_tix")]
public object UrlTix { get; set; }
[JsonProperty("event_owner")]
public string EventOwner { get; set; }
[JsonProperty("follow_url")]
public Uri FollowUrl { get; set; }
[JsonProperty("event_image")]
public object EventImage { get; set; }
[JsonProperty("venue")]
public string Venue { get; set; }
[JsonProperty("city")]
public string City { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("state")]
public string State { get; set; }
}
当我尝试调用 Flurl 方法来使用 Web 服务时,如下所示:
var result = await serviceUrl.GetJsonAsync<ServiceResponce>();
我收到下面提到的错误:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'xxx.ServiceResponce' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly. To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array. Path '', line 1, position 1.
你有什么解决办法吗?欢迎任何帮助。
我认为问题出在 Json 对象上,我用 'Newtonsoft.Json' 生成了一个 class,如果你可以试试这个代码:
// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
// using MyNameSpace;
//
// var event = Event.FromJson(jsonString);
namespace MyNameSpace
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class EventClass
{
[JsonProperty("eventdate")]
public DateTimeOffset Eventdate { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("info")]
public string Info { get; set; }
[JsonProperty("showtime")]
public object Showtime { get; set; }
[JsonProperty("url_tix")]
public string UrlTix { get; set; }
[JsonProperty("event_owner")]
public string EventOwner { get; set; }
[JsonProperty("follow_url")]
public string FollowUrl { get; set; }
[JsonProperty("event_image")]
public string EventImage { get; set; }
[JsonProperty("venue")]
public string Venue { get; set; }
[JsonProperty("city")]
public string City { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("state")]
public string State { get; set; }
}
public partial struct EventUnion
{
public EventClass[] EventClassArray;
public string String;
public static implicit operator EventUnion(EventClass[] EventClassArray) => new EventUnion { EventClassArray = EventClassArray };
public static implicit operator EventUnion(string String) => new EventUnion { String = String };
}
public class Event
{
public static EventUnion[] FromJson(string json) => JsonConvert.DeserializeObject<EventUnion[]>(json, MyNameSpace.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this EventUnion[] self) => JsonConvert.SerializeObject(self, MyNameSpace.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
EventUnionConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class EventUnionConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(EventUnion) || t == typeof(EventUnion?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.String:
case JsonToken.Date:
var stringValue = serializer.Deserialize<string>(reader);
return new EventUnion { String = stringValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<EventClass[]>(reader);
return new EventUnion { EventClassArray = arrayValue };
}
throw new Exception("Cannot unmarshal type EventUnion");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (EventUnion)untypedValue;
if (value.String != null)
{
serializer.Serialize(writer, value.String);
return;
}
if (value.EventClassArray != null)
{
serializer.Serialize(writer, value.EventClassArray);
return;
}
throw new Exception("Cannot marshal type EventUnion");
}
public static readonly EventUnionConverter Singleton = new EventUnionConverter();
}
}
这里的问题是 JSON 响应实际上是一个混合类型的数组。数组的第一个元素是一个字符串,第二个元素是一个事件对象数组。您将需要自定义 JsonConverter
来反序列化此 JSON。
这是转换器所需的代码:
class ServiceResponceConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(ServiceResponce));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray ja = JArray.Load(reader);
ServiceResponce resp = new ServiceResponce();
resp.Events = ja[1].ToObject<Event[]>(serializer);
return resp;
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
然后,将 [JsonConverter]
属性添加到 ServiceResponce
class 以将其绑定到转换器:
[JsonConverter(typeof(ServiceResponceConverter))]
public class ServiceResponce
{
public Event[] Events { get; set; }
}
现在您可以正常反序列化到 ServiceResponce
class,它会正常工作。
可选:如果您还想从响应中捕获 "Successful Request: 96 Results"
字符串,请添加
public string ResultString { get; set; }
到 ServiceResponce
class 并将以下行添加到转换器的 ReadJson
方法:
resp.ResultString = (string)ja[0];
此处的工作演示:https://dotnetfiddle.net/opPUmX