Ошибка десериализации JSON при отсутствии имени свойства - PullRequest
0 голосов
/ 14 октября 2019

Я пытаюсь использовать внешний веб-сервис и использую .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 для использования Интернетасервис, как показано ниже:

var result = await serviceUrl.GetJsonAsync<ServiceResponce>();

Я получил ошибку, упомянутую ниже:

Невозможно десериализовать текущий массив JSON (например, [1,2,3]) в тип 'xxx. ServiceResponce ', потому что для корректной десериализации тип требует JSON-объект (например, {"name": "value"}). Чтобы исправить эту ошибку, измените JSON на объект JSON (например, {"name": "value"}) или измените десериализованный тип на массив или тип, который реализует интерфейс коллекции (например, ICollection, IList), например List, который можетбыть десериализованным из массива JSON. JsonArrayAttribute также может быть добавлен к типу, чтобы принудительно десериализовать его из массива JSON. Путь '', строка 1, позиция 1.

У вас есть какое-то решение для этого? Любая помощь всегда приветствуется.

Ответы [ 2 ]

1 голос
/ 14 октября 2019

Проблема в том, что ответ 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, чтобы связать его с конвертером:

[JsonConverter(typeof(ServiceResponceConverter))]
public class ServiceResponce
{
    public Event[] Events { get; set; }
}

Теперь вы можете десериализовать класс ServiceResponce как обычно, и он будет работать правильно.

Необязательно: Если вы также хотите захватить строку "Successful Request: 96 Results" из ответа, добавьте

public string ResultString { get; set; }

к классу ServiceResponce и добавьте следующую строку в метод ReadJson преобразователя:

resp.ResultString = (string)ja[0];

Рабочая демонстрация здесь: https://dotnetfiddle.net/opPUmX

0 голосов
/ 14 октября 2019

Я думаю, что проблема в объекте Json, я сгенерировал класс с 'Newtonsoft.Json', если вы можете попробовать этот код:

// <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();
    }
}
...