c# - 如何反序列化对对象列表的 JSON 响应

标签 c# json asp.net-core json.net

我编写了一个 Web 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()
    {

    }
}

正在抓取和发送数据的 Web 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.");
    }
}

请注意,每个文件都包含一个先前序列化为 JSON 的 Media 对象。

调用上述端点的客户端代码:

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 响应本身对我来说似乎是一个问题,太多 \\ 和其他特殊字符。我尝试用 string .Replace 调用来清理它,但这没有用。有什么想法吗?

实际的错误消息:

System.AggregateException: '发生一个或多个错误。 (将值 ...json 字符串...转换为类型 'System.Collections.Generic.List`1[CatalogManager.Models.MediaManager.Media]' 时出错。路径 '',第 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.

最终编辑:我尝试了 dbc 的建议并且有效。我认为核心问题在于我的 API 方面,我将 json 文件解析为字符串,而不是解析为媒体模型对象,然后使用 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.");
    }

}

并且它在前端运行成功。

最佳答案

您的基本问题是您的服务器返回 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.");
    }
}

注释:

  • 通过直接流式传输来反序列化 JSON 文件比加载到中间文件中更有效 string ,然后反序列化该字符串。请参阅Performance Tips: Optimize Memory Usage了解详情。如果这不是问题,您可以这样做:

    return files.Select(f => JsonConvert.DeserializeObject<Media>(File.ReadAllText(f))).ToList();
    

关于c# - 如何反序列化对对象列表的 JSON 响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56926237/

相关文章:

c# - 如何检测我的程序是否在 Active Directory 环境中运行?

c# - 简化将输入检索到数组中的过程

java - 如何更紧凑地使用Gson的TypeToken?

.NET Core 依赖注入(inject) -> 获取接口(interface)的所有实现

css - 站点根目录中的图像在本地呈现,但不在 IIS 服务器上呈现

c# - Autofac、单元测试、C# - Autofac Moq 和 FakeItEasy 集成包之间的区别

html - 如何在 AngularJS 中使用 ng-options 和 ng-init

javascript - 使用键元素在 json 对象中查找值对

c# - 在 aspnet core 运行时检查托管服务器是 IIS 还是 Kestrel

c# - 绘制至少一个像素宽的对象