我有一个非常大的相同JSON对象的JSON文件(1000+ MB)。例如:
[ { "id": 1, "value": "hello", "another_value": "world", "value_obj": { "name": "obj1" }, "value_list": [ 1, 2, 3 ] }, { "id": 2, "value": "foo", "another_value": "bar", "value_obj": { "name": "obj2" }, "value_list": [ 4, 5, 6 ] }, { "id": 3, "value": "a", "another_value": "b", "value_obj": { "name": "obj3" }, "value_list": [ 7, 8, 9 ] }, ... ]
JSON根列表中的每个项目都采用相同的结构,因此可以单独进行反序列化。我已经编写了C#类来接收此数据,并且反序列化包含单个对象但不包含列表的JSON文件可以按预期工作。
最初,我试图直接在循环中反序列化我的对象:
JsonSerializer serializer = new JsonSerializer(); MyObject o; using (FileStream s = File.Open("bigfile.json", FileMode.Open)) using (StreamReader sr = new StreamReader(s)) using (JsonReader reader = new JsonTextReader(sr)) { while (!sr.EndOfStream) { o = serializer.Deserialize<MyObject>(reader); } }
这没有用,抛出了一个异常,清楚地表明一个对象是期望的,而不是列表。我的理解是,此命令将只读取JSON文件根级别中包含的单个对象,但是由于我们有对象 列表 ,因此这是无效的请求。
我的下一个想法是反序列化为对象的C#列表:
JsonSerializer serializer = new JsonSerializer(); List<MyObject> o; using (FileStream s = File.Open("bigfile.json", FileMode.Open)) using (StreamReader sr = new StreamReader(s)) using (JsonReader reader = new JsonTextReader(sr)) { while (!sr.EndOfStream) { o = serializer.Deserialize<List<MyObject>>(reader); } }
这确实成功了。但是,它仅在某种程度上减少了高RAM使用率的问题。在这种情况下,看起来应用程序一次要对一个序列进行反序列化,因此不会将整个JSON文件读入RAM,但是由于C#List对象现在包含所有RAM中JSON文件中的数据。这仅解决了问题。
然后,我决定简单地尝试进入循环之前,从流的开头删除单个字符(以消除[)sr.Read()。然后,第一个对象确实读取成功,但随后的对象读取成功,但“意外令牌”除外。我的猜测是,这是对象之间的逗号和空格,使读者无法使用。
[
sr.Read()
如示例中所示,仅删除方括号是行不通的,因为对象确实包含其自己的原始列表。},就像您看到的那样,即使尝试用作分隔符也行不通,因为对象中包含子对象。
},
我的目标是能够一次从流中读取对象。读取一个对象,对其进行处理,然后将其从RAM中丢弃,然后读取下一个对象,依此类推。这样就无需将整个JSON字符串或数据的全部内容作为C#对象加载到RAM中。
我想念什么?
这应该可以解决您的问题。基本上,它的工作方式与您的初始代码相同,不同之处{在于,它仅在读者点击流中的字符时反序列化对象,否则它会跳至下一个对象,直到找到另一个起始对象标记为止。
{
JsonSerializer serializer = new JsonSerializer(); MyObject o; using (FileStream s = File.Open("bigfile.json", FileMode.Open)) using (StreamReader sr = new StreamReader(s)) using (JsonReader reader = new JsonTextReader(sr)) { while (reader.Read()) { // deserialize only when there's "{" character in the stream if (reader.TokenType == JsonToken.StartObject) { o = serializer.Deserialize<MyObject>(reader); } } }