在.NET中解析大型json文件

Yav*_*nov 29 c# json.net deserialization json-deserialization

到目前为止,我已经使用了Json.Net的"JsonConvert.Deserialize(json)"方法,该方法运行得很好,说实话,我不需要更多的东西.

我正在开发一个后台(控制台)应用程序,它不断从不同的URL下载json内容,然后将结果反序列化为.Net对象列表.

 using (WebClient client = new WebClient())
 {
      string json = client.DownloadString(stringUrl);

      var result = JsonConvert.DeserializeObject<List<Contact>>(json);

 }
Run Code Online (Sandbox Code Playgroud)

上面的简单代码片段似乎并不完美,但它可以完成这项工作.当文件很大(15000个联系人--48 MB文件)时,JsonConvert.DeserializeObject不是解决方案,并且该行抛出异常类型的JsonReaderException.

下载的json是一个数组,这就是样本的样子.Contact是反序列化的json对象的容器类.

[
  {
    "firstname": "sometext",
    "lastname": "sometext"
  },
  {
    "firstname": "sometext",
    "lastname": "sometext"
  },
  {
    "firstname": "sometext",
    "lastname": "sometext"
  },
  {
    "firstname": "sometext",
    "lastname": "sometext"
  }
]
Run Code Online (Sandbox Code Playgroud)

我最初的猜测是内存不足.出于好奇,我试图将其解析为JArray,这也导致了同样的异常.

我已经开始深入研究Json.Net文档并阅读类似的线程.由于我还没有设法制作出有效的解决方案,我决定在这里发一个问题.

我很感激任何建议/代码片段,它可以帮助我研究问题,了解更多信息并最终找到解决方案.

谢谢 :)

更新:逐行反序列化时,我得到了同样的错误:"[.路径'',第600003行,第1位." 所以我做的是下载其中两个并在Notepad ++中检查它们.我注意到的是,如果数组长度超过12000,则在第12000个元素之后,"["关闭,另一个数组开始.换句话说,json看起来完全像这样:

[
  {
    "firstname": "sometext",
    "lastname": "sometext"
  },
  {
    "firstname": "sometext",
    "lastname": "sometext"
  },
  {
    "firstname": "sometext",
    "lastname": "sometext"
  },
  {
    "firstname": "sometext",
    "lastname": "sometext"
  }
]
[
  {
    "firstname": "sometext",
    "lastname": "sometext"
  },
  {
    "firstname": "sometext",
    "lastname": "sometext"
  },
  {
    "firstname": "sometext",
    "lastname": "sometext"
  },
  {
    "firstname": "sometext",
    "lastname": "sometext"
  }
]
Run Code Online (Sandbox Code Playgroud)

Bri*_*ers 37

正如您在更新中正确诊断的那样,问题是JSON有一个关闭,]紧接着是一个开始[以开始下一个集合.这种格式使JSON在整体上无效,这就是Json.Net抛出错误的原因.幸运的是,这个问题似乎经常出现,Json.Net实际上有一个特殊的设置来处理它.如果JsonTextReader直接使用a 来读取JSON,则可以将SupportMultipleContent标志设置为true,然后使用循环单独反序列化每个项目.这应该允许您以内存有效的方式成功处理非标准JSON,无论有多少数组或每个数组中有多少项.

    using (WebClient client = new WebClient())
    using (Stream stream = client.OpenRead(stringUrl))
    using (StreamReader streamReader = new StreamReader(stream))
    using (JsonTextReader reader = new JsonTextReader(streamReader))
    {
        reader.SupportMultipleContent = true;

        var serializer = new JsonSerializer();
        while (reader.Read())
        {
            if (reader.TokenType == JsonToken.StartObject)
            {
                Contact c = serializer.Deserialize<Contact>(reader);
                Console.WriteLine(c.FirstName + " " + c.LastName);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

这里有完整的演示:https://dotnetfiddle.net/2TQa8p


Kri*_*sic 17

Json.NET支持直接从流中反序列化.这是一种使用一次StreamReader读取JSON字符串而不是将整个JSON字符串加载到内存中来反序列化JSON的方法.

using (WebClient client = new WebClient())
{
    using (StreamReader sr = new StreamReader(client.OpenRead(stringUrl)))
    {
        using (JsonReader reader = new JsonTextReader(sr))
        {
            JsonSerializer serializer = new JsonSerializer();

            // read the json from a stream
            // json size doesn't matter because only a small piece is read at a time from the HTTP request
            IList<Contact> result = serializer.Deserialize<List<Contact>>(reader);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

参考:JSON.NET性能提示

  • 此代码可能无法将整个流加载到内存中,但肯定会将整个联系人列表加载到内存中.除非Contact对象从流中丢弃大量数据,否则你只是将内存问题推向了下游. (5认同)

小智 5

我在python中做了类似的事情,文件大小为5 GB.我将文件下载到一些临时位置并逐行读取以形成类似于SAX工作原理的JSON对象.对于使用json.net的c#,您可以下载文件,使用流阅读器读取文件,并将该流传递给JsonTextReader并使用JTokens.ReadFrom(您的JSonTextReader对象)将其解析为JObject