Ошибка десериализации JSON, когда имя свойства отсутствует

Я пытаюсь использовать внешнюю веб-службу и использую .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":""
      }
   ]
]

и у меня есть определение объекта С#, как показано ниже:

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.

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


person leo    schedule 14.10.2019    source источник


Ответы (2)


Проблема здесь в том, что ответ 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

person Brian Rogers    schedule 14.10.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();
    }
}
person Ahmed Msaouri    schedule 14.10.2019
comment
Спасибо за Ваш ответ. На самом деле я пробовал то же самое, используя онлайн-инструмент json to c#. Но у меня другая ошибка. Как показано ниже: Ошибка преобразования значения. Успешный запрос: 96 результатов для типа «DataMigration.EventClass». Путь '[0]', строка 1, позиция 33. - person leo; 14.10.2019
comment
Я думаю, что получил эту ошибку, потому что успешный запрос: 96 результатов не имеет имени свойства, а только значение. Дело в том, что я не могу коснуться результата службы, поскольку он является внешним. - person leo; 14.10.2019