Как десериализовать ответ JSON на список объектов - PullRequest
1 голос
/ 08 июля 2019

Я написал веб-API для доступа к некоторым данным JSON в файловой системе. API настроен на возврат данных в виде json клиенту. Однако, когда я пытаюсь десериализовать ответ JSON в список моих объектов, он терпит неудачу.

Я попытался очистить ответ, так как в нем, кажется, есть дополнительные символы, но, похоже, это не сработало, так как очистка имела тенденцию приводить к появлению вонючего не-JSON. Я чувствую, что делаю это сложнее, чем нужно.

Мой класс модели данных:

public abstract class CatalogueBase
{
    string name;
    string description;
    int id;

    public string Name { get => name; set => name = value; }
    public string Description { get => description; set => description = value; }
    public int Id { get => id; set => id = value; }
}

public class Media : CatalogueBase
{
    MediaType type;
    MediaRating rating;
    string genre;

    public MediaType Type { get => type; set => type = value; }
    public MediaRating Rating { get => rating; set => rating = value; }
    public string Genre { get => genre; set => genre = value; }

    public Media()
    {

    }
}

Конечная точка веб-API, которая захватывает и отправляет данные:

[HttpGet]
[Route("GetMedia/")]
public ActionResult<string> GetAll()    
{
    string[] files = Directory.GetFiles(this.config.Value.JSONFileDirectory);
    if (files.Length > 0)
    {
        try
        {
            List<string> jsonFiles = new List<string>();
            string json;

            foreach (var file in files)
            {
                using (StreamReader reader = new StreamReader(file))
                {
                    json = reader.ReadToEnd();
                    jsonFiles.Add(Regex.Unescape(json));
                }  
            }

            return JsonConvert.SerializeObject(jsonFiles, Formatting.Indented);
        }
        catch (Exception e)
        {
            throw new Exception("Could not parse JSON file.", e);
        }
    }
    else
    {
        return NotFound("No files were found in the catalog.");
    }
}

Обратите внимание, что каждый файл содержит один Media объект, ранее сериализованный как JSON.

Код на стороне клиента, который вызывает вышеуказанную конечную точку:

public async Task<List<Media>> GetAllMedia()
{
    client = GetHttpClient(@"GetMedia/");
    HttpResponseMessage response = await client.GetAsync("");
    string content = await response.Content.ReadAsStringAsync();
    var media = JsonConvert.DeserializeObject<List<Media>>(content);
    return media;
}

И, наконец, образец JSONresponse, который я получаю в своем клиенте:

"\"[\\r\\n  \\\"{\\\\\\\"Type\\\\\\\":0,\\\\\\\"Rating\\\\\\\":5,\\\\\\\"Genre\\\\\\\":\\\\\\\"Drama\\\\\\\",\\\\\\\"Name\\\\\\\":\\\\\\\"Memes\\\\\\\",\\\\\\\"Description\\\\\\\":\\\\\\\"A movie set during the American Civil War\\\\\\\",\\\\\\\"Id\\\\\\\":1}\\\",\\r\\n  \\\"{\\\\r\\\\n\\\\\\\"Id\\\\\\\": 2,\\\\r\\\\n\\\\\\\"Name\\\\\\\": \\\\\\\"Gods and Generals\\\\\\\",\\\\r\\\\n\\\\\\\"Description\\\\\\\": \\\\\\\"A movie set during the American Civil War\\\\\\\",\\\\r\\\\n\\\\\\\"Type\\\\\\\": \\\\\\\"Movie\\\\\\\",\\\\r\\\\n\\\\\\\"Rating\\\\\\\": \\\\\\\"Excellent\\\\\\\",\\\\r\\\\n\\\\\\\"Genre\\\\\\\" : \\\\\\\"Drama\\\\\\\"\\\\r\\\\n}\\\\r\\\\n\\\",\\r\\n  \\\"{\\\\r\\\\n\\\\\\\"Id\\\\\\\": 3,\\\\r\\\\n\\\\\\\"Name\\\\\\\": \\\\\\\"Avengers Endgame\\\\\\\"\\\\r\\\\n\\\\\\\"Description\\\\\\\": \\\\\\\"The end of the Avengers series.\\\\\\\",\\\\r\\\\n\\\\\\\"Type\\\\\\\": \\\\\\\"Movie\\\\\\\",\\\\r\\\\n\\\\\\\"Rating\\\\\\\": \\\\\\\"Excellent\\\\\\\",\\\\r\\\\n\\\\\\\"Genre\\\\\\\" : \\\\\\\"Action\\\\\\\"\\\\r\\\\n}\\\\r\\\\n\\\"\\r\\n]\""

Сам ответ JSON выглядит для меня проблемой, слишком много \\ и других специальных символов. Я пытался очистить его с помощью строки. Заменить вызовы, но это не сработало. Есть идеи?

Фактическое сообщение об ошибке:

System.AggregateException: 'Произошла одна или несколько ошибок. (Ошибка преобразования значения ... json string ..... в тип 'System.Collections.Generic.List`1 [CatalogManager.Models.MediaManager.Media]'. Path '', строка 1, позиция 732.) '


Редактировать: попробовал некоторые из предложений в комментариях, и я получил следующий ответ JSON:

"[\"{\\\"Type\\\":0,\\\"Rating\\\":5,\\\"Genre\\\":\\\"Drama\\\",\\\"Name\\\":\\\"Memes\\\",\\\"Description\\\":\\\"A movie set during the American Civil War\\\",\\\"Id\\\":1}\",\"{\\r\\n\\\"Id\\\": 2,\\r\\n\\\"Name\\\": \\\"Gods and Generals\\\",\\r\\n\\\"Description\\\": \\\"A movie set during the American Civil War\\\",\\r\\n\\\"Type\\\": \\\"Movie\\\",\\r\\n\\\"Rating\\\": \\\"Excellent\\\",\\r\\n\\\"Genre\\\" : \\\"Drama\\\"\\r\\n}\\r\\n\",\"{\\r\\n\\\"Id\\\": 3,\\r\\n\\\"Name\\\": \\\"Avengers Endgame\\\"\\r\\n\\\"Description\\\": \\\"The end of the Avengers series.\\\",\\r\\n\\\"Type\\\": \\\"Movie\\\",\\r\\n\\\"Rating\\\": \\\"Excellent\\\",\\r\\n\\\"Genre\\\" : \\\"Action\\\"\\r\\n}\\r\\n\"]"

И когда я пытаюсь разобрать его с JObject, я получаю следующую ошибку:

System.AggregateException
  HResult=0x80131500
  Message=One or more errors occurred. (Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path '', line 1, position 1.)
  Source=System.Private.CoreLib
  StackTrace:
   at System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
   at CatalogManager.Pages.IndexModel.OnGet() in C:\Users\tpzap_000\source\repos\CatalogManager\Pages\Manager\Index.cshtml.cs:line 73
   at Microsoft.AspNetCore.Mvc.RazorPages.Internal.ExecutorFactory.ActionResultHandlerMethod.Execute(Object receiver, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker.<InvokeHandlerMethodAsync>d__30.MoveNext()

Inner Exception 1:
JsonReaderException: Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path '', line 1, position 1.

Final Edit: я попробовал предложение dbc, и это сработало. Я думаю, что основная проблема была на моей стороне API. Я анализировал свои файлы json как строки, а не в объекте своей модели Media, а затем использовал Newtonsoft для анализа его в правильно сформированный json. Я изменил свой код стороны API на следующий:

[HttpGet]
[Route("GetMedia/")]
public ActionResult<List<Media>> GetAll()    
{

    string[] files = Directory.GetFiles(this.config.Value.JSONFileDirectory);
    if (files.Length > 0)
    {
        try
        {
            List<Media> jsonFiles = new List<Media>();
            string json;

            foreach (var file in files)
            {
                using (StreamReader reader = new StreamReader(file))
                {
                    json = reader.ReadToEnd();
                    Media currentMedia = JsonConvert.DeserializeObject<Media>(json);

                    //jsonFiles.Add(Regex.Unescape(json));
                    jsonFiles.Add(currentMedia);
                }  
            }



            return Ok(jsonFiles);
        }
        catch (Exception e)
        {
            throw new Exception("Could not parse JSON file.", e);
        }
    }
    else
    {
        return NotFound("No files were found in the catalog.");
    }

}

И он успешно работал на переднем конце.

1 Ответ

2 голосов
/ 08 июля 2019

Ваша основная проблема заключается в том, что ваш сервер возвращает string, но клиент ожидает application/json контент, содержащий сериализованный List<Media>, что вызывает проблему совместимости во время десериализации.

Самый простой способ убедиться, что клиент и сервер совместимы, - это вернуть точно такой же тип с сервера, то есть List<Media> (или ActionResult<List<Media>>если вы хотите возможность возврата ошибки).Это позволяет платформе обрабатывать как сериализацию, так и согласование контента, так что вам не нужно это делать.Следующее делает работу:

[HttpGet]
[Route("GetMedia/")]
public ActionResult<List<Media>> GetAll()
{
    string[] files = Directory.GetFiles(this.config.Value.JSONFileDirectory);
    if (files.Length > 0)
    {
        try
        {
            var serializer = JsonSerializer.CreateDefault();
            return files.Select(f =>
                {
                    using (var reader = new StreamReader(f))
                    using (var jsonReader = new JsonTextReader(reader))
                    {
                        return serializer.Deserialize<Media>(jsonReader);
                    }
                })
                .ToList();
        }
        catch (Exception e)
        {
            throw new Exception("Could not parse JSON file.", e);
        }
    }
    else
    {
        return NotFound("No files were found in the catalog.");
    }
}

Примечания:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...