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

时间:2019-07-07 21:27:55

标签: 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.");
    }
}

请注意,每个文件都包含单个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响应本身似乎是一个问题,\\和其他特殊字符太多了。我试图用字符串.Replace调用清理它,但这没有用。有什么想法吗?

实际错误消息:

System.AggregateException:'发生一个或多个错误。 (将值... json字符串.....转换为类型'System.Collections.Generic.List`1 [CatalogManager.Models.MediaManager.Media]'时出错。路径”,第1行,位置732。)'< / p>


编辑:尝试了注释中的一些建议,我收到了以下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文件解析为字符串,而不是解析为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 个答案:

答案 0 :(得分:2)

您的基本问题是服务器正在返回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();