JAVA - 解析大型(超大)JSON文件的最佳方法

2022-08-31 16:42:02

我正在尝试使用JAVA中的gson库(http://code.google.com/p/google-gson/)解析一些巨大的JSON文件(如 http://eu.battle.net/auction-data/258993a3c6b974ef3e6f22ea6f822720/auctions.json)。

我想知道解析这种大文件(约80k行)的最佳选择是什么,以及您是否知道可以帮助您处理此问题的良好API。

一些想法...

  1. 逐行阅读并摆脱JSON格式:但这是无稽之谈。
  2. 通过将此文件拆分为许多其他文件来减少JSON文件:但是我没有找到任何好的Java API。
  3. 直接将此文件用作非Sql数据库,保留该文件并将其用作我的数据库。

我真的会很感激 adices/帮助/消息/ :-)谢谢。


答案 1

你不需要切换到杰克逊。Gson 2.1 引入了一个新的 TypeAdapter 接口,允许混合树和流式序列化和反序列化。

该 API 高效且灵活。有关组合树和绑定模式的示例,请参阅 Gson 的流式处理文档。这严格优于混合流和树模式;通过绑定,您不会浪费内存来构建值的中间表示形式。

像Jackson一样,Gson也有API来递归跳过不需要的值;Gson 称之为 skipValue()。


答案 2

我建议看看Jackson Api,结合流式处理和树模型解析选项非常容易:您可以以流式处理的方式将文件作为一个整体移动,然后将单个对象读取到树结构中。

例如,让我们采用以下输入:

{ 
  "records": [ 
    {"field1": "aaaaa", "bbbb": "ccccc"}, 
    {"field2": "aaa", "bbb": "ccc"} 
  ] ,
  "special message": "hello, world!" 
}

想象一下,字段是稀疏的,或者记录具有更复杂的结构。

以下代码段说明了如何使用流和树模型分析的组合来读取此文件。每个单独的记录都以树结构读取,但文件永远不会完整地读入内存中,因此可以在使用最小内存的同时处理大小为千兆字节的 JSON 文件。

import org.codehaus.jackson.map.*;
import org.codehaus.jackson.*;

import java.io.File;

public class ParseJsonSample {
    public static void main(String[] args) throws Exception {
        JsonFactory f = new MappingJsonFactory();
        JsonParser jp = f.createJsonParser(new File(args[0]));
        JsonToken current;
        current = jp.nextToken();
        if (current != JsonToken.START_OBJECT) {
            System.out.println("Error: root should be object: quiting.");
            return;
        }
        while (jp.nextToken() != JsonToken.END_OBJECT) {
            String fieldName = jp.getCurrentName();
            // move from field name to field value
            current = jp.nextToken();
            if (fieldName.equals("records")) {
                if (current == JsonToken.START_ARRAY) {
                    // For each of the records in the array
                    while (jp.nextToken() != JsonToken.END_ARRAY) {
                        // read the record into a tree model,
                        // this moves the parsing position to the end of it
                        JsonNode node = jp.readValueAsTree();
                        // And now we have random access to everything in the object
                        System.out.println("field1: " + node.get("field1").getValueAsText());
                        System.out.println("field2: " + node.get("field2").getValueAsText());
                    }
                } else {
                    System.out.println("Error: records should be an array: skipping.");
                    jp.skipChildren();
                }
            } else {
                System.out.println("Unprocessed property: " + fieldName);
                jp.skipChildren();
            }
        }
    }
}

正如您可以猜到的,nextToken() 调用每次都会给出下一个解析事件:start object, start field, start array, start object, ..., end object, ..., end array, ...

该调用允许将当前解析位置(JSON 对象或数组)的内容读取到 Jackson 的通用 JSON 树模型中。完成此操作后,您可以随机访问数据,而不管内容在文件中的显示顺序如何(在示例中,field1 和 field2 并不总是以相同的顺序)。Jackson 也支持映射到你自己的 Java 对象。jp.skipChildren() 很方便:它允许跳过一个完整的对象树或数组,而不必自己运行其中包含的所有事件。jp.readValueAsTree()