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