java—解析巨大(超大)json文件的最佳方法

92dk7w1h  于 2021-07-03  发布在  Java
关注(0)|答案(3)|浏览(1601)

我试图解析一些巨大的json文件(比如http://eu.battle.net/auction-data/258993a3c6b974ef3e6f22ea6f822720/auctions.json)使用gson库(http://code.google.com/p/google-gson/)在 java 。
我想知道什么是最好的方法来解析这种大文件(约80k行),如果你可能知道好的api,可以帮助我处理这个。
一些想法。。。
逐行阅读并去掉json格式:但这是胡说八道。
通过将这个文件拆分成许多其他文件来减少json文件:但是我没有找到任何好的javaapi。
直接将此文件用作非SQL数据库,保留该文件并将其用作我的数据库。
我真的很感激adices/帮助/留言/:-)谢谢。

ep6jt1vc

ep6jt1vc1#

你不需要换成Jackson。gson2.1引入了一个新的typeadapter接口,它允许混合树和流序列化以及反序列化。
api是高效和灵活的。请参阅gson的streaming doc,以获取结合树模式和绑定模式的示例。这是严格优于混合流和树模式;通过绑定,您不会浪费内存来构建值的中间表示。
像jackson一样,gson有api递归地跳过不需要的值;gson调用这个skipvalue()。

plicqrtu

plicqrtu2#

我建议看一下jacksonapi,将流和树模型解析选项结合起来非常容易:您可以以流的方式将文件作为一个整体移动,然后将单个对象读入树结构。
作为一个例子,让我们采用以下输入:

{ 
  "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、。。。
这个 jp.readValueAsTree() 调用允许将当前解析位置的内容(json对象或数组)读入jackson的通用json树模型。一旦您有了这个,您就可以随机地访问数据,而不管文件中出现的顺序如何(在示例中,field1和field2并不总是以相同的顺序出现)。jackson也支持Map到您自己的java对象。skipchildren()很方便:它允许跳过一个完整的对象树或数组,而不必自己运行其中包含的所有事件。

v8wbuo2f

v8wbuo2f3#

声明流Map(dsm)库允许您定义json或xml数据与pojo之间的Map。所以不需要编写自定义解析器。我̇t具有强大的脚本(javascript、groovy、jexl)支持。您可以在阅读时过滤和转换数据。您可以在读取数据时调用函数进行部分数据操作。dsm以流的形式读取数据,因此它使用非常低的内存。
例如,

{
    "company": {
         ....
        "staff": [
            {
                "firstname": "yong",
                "lastname": "mook kim",
                "nickname": "mkyong",
                "salary": "100000"
            },
            {
                "firstname": "low",
                "lastname": "yin fong",
                "nickname": "fong fong",
                "salary": "200000"
            }
        ]
    }
}

假设上面的代码片段是巨大而复杂的json数据的一部分。我们只想要薪水超过10000的东西。
首先,我们必须定义如下的Map定义。如您所见,它只是一个yaml文件,包含pojo字段和json数据字段之间的Map。

result:
      type: object     # result is map or a object.
      path: /.+staff  # path is regex. its match with /company/staff
      function: processStuff  # call processStuff function when /company/stuff tag is closed
      filter: self.data.salary>10000   # any expression is valid in JavaScript, Groovy or JEXL
      fields:
        name:  
          path: firstname
        sureName:
          path: lastname
        userName:
          path: nickname
        salary: long

为流程人员创建functionexecutor。

FunctionExecutor processStuff=new FunctionExecutor(){

            @Override
            public void execute(Params params) {

                // directly serialize Stuff class
                //Stuff stuff=params.getCurrentNode().toObject(Stuff.class);

                Map<String,Object> stuff= (Map<String,Object>)params.getCurrentNode().toObject();
                System.out.println(stuff);
                // process stuff ; save to db. call service etc.
            }
        };

使用dsm处理json

DSMBuilder builder = new DSMBuilder(new File("path/to/mapping.yaml")).setType(DSMBuilder.TYPE.XML);

       // register processStuff Function
        builder.registerFunction("processStuff",processStuff);

        DSM dsm= builder.create();
        Object object =  dsm.toObject(xmlContent);

产出:(只包括工资高于10000的员工)

{firstName=low, lastName=yin fong, nickName=fong fong, salary=200000}

相关问题