【问题标题】:How to deserialize a JSON response to an object list如何反序列化对对象列表的 JSON 响应
【发布时间】:2019-07-07 21:27:55
【问题描述】:

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

实际的错误信息:

System.AggregateException: '发生一个或多个错误。 (错误转换值 ...json 字符串 ... 以键入 'System.Collections.Generic.List`1[CatalogManager.Models.MediaManager.Media]'。路径 '',第 1 行,位置 732。)'


编辑:尝试了 cmets 中的一些建议,我收到了以下 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.");
    }

}

它在前端成功运行。

【问题讨论】:

  • asp.net 会将您的结果转换为 JSON。只需返回 jsonFiles(当然还要更改返回类型)
  • 在你 GetAllMedia 中,方法尝试添加这一行,然后用你在调试控制台中得到的结果回击我。 var jobject = JObject.Parse(content); Console.WriteLine(jobject)。并将return JsonConvert.SerializeObject(jsonFiles, Formatting.Indented); 更改为return Ok(jsonFiles)
  • 尝试了您的建议,但 JObject.Parse 无法解析它。用结果更新了帖子。 @Sir Rufo,我确实在 API 端的 return 语句中去掉了序列化,这似乎是朝着正确方向迈出的一步。我认为 json 更干净一些。
  • 1) 您的方法具有签名ActionResult&lt;string&gt;,但您实际上想要返回什么内容类型? text/plain 还是 application/json? 2)响应应该包含什么,究竟是什么? 3)你为什么要Regex.Unescape(json)?
  • 1) 我更愿意返回 application/json。 2)只是我从文件中提取的json数据,就是这样。 3)我这样做是为了尝试清理我从文件中提取的 json 字符串。它似乎有一堆特殊字符(\r、\n、\\ 等)。一旦我添加了 Rufo 爵士的建议,我就删除了它。

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


【解决方案1】:

您的基本问题是您的服务器返回一个string,但客户端期望application/json 内容包含一个序列化的List&lt;Media&gt;,从而导致反序列化期间出现兼容性问题。

确保客户端和服务器兼容的最简单方法是从服务器返回完全相同的类型,即List&lt;Media&gt;(或ActionResult&lt;List&lt;Media&gt;&gt;,如果您希望返回一个错误)。这允许框架同时处理序列化和内容协商,因此您不必这样做。下面的工作:

[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();
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多