2012-02-22 181 views
27

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

我想知道什么是解析这种大文件(大约80k行)最好的解决方案,如果你可能知道可以帮助我处理这个问题的好API。通过线

一些想法...

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

我真的很感激adices/help/messages/:-) 谢谢。

+0

Java EE替代方法:javax.json.stream.JsonParser – xonya 2018-01-19 08:35:55

回答

27

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

该API高效灵活。有关组合树和绑定模式的示例,请参见Gson's Streaming doc。这比混合流和树模式严格得多;绑定你不浪费内存构建你的价值观的中间表示。

像杰克逊一样,Gson有API递归地跳过不需要的值; Gson称之为skipValue()

+0

我会检查一下!感谢分享 – Dax 2012-02-28 14:11:46

+0

有没有一个使用'TypeAdapter'将混合流解析到树解析中的好例子?我有一种情况,我想将它混入一个非常大的对象列表中。文档中的例子是流解析List的'Message's,但它并不显示如何将该流解析器绑定到树解析器。 (它显示了如何将树解析器绑定到流解析器中) – 2013-02-27 01:01:01

+0

例如:我有'CustomType'来定义对象映射,'CustomTypes扩展了ArrayList '。我创建了一个'TypeAdapter ',它为每个'CustomType'使用对象映射,但最后返回一个空列表以避免将整个列表存储在内存中(而不是将它们写入数据库)。然后使用对象映射简单地分析包含的对象。 – 2013-02-27 01:41:40

25

我会建议看看Jackson Api结合流和树模型解析选项是非常简单的:您可以通过流式传输整个文件,然后将单个对象读入树中结构体。

作为example,我们采取以下输入:

{ 
    "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()调用每次给人的下一个解析事件:启动对象,启动现场,启动阵列,启动对象,...,结束对象,...,高端磁盘阵列,...

jp.readValueAsTree()调用允许读取当前解析位置(JSON对象或数组)到JSON的通用JSON树模型中。一旦你有了这个,你可以随机地访问数据,而不管文件在文件中出现的顺序(在示例中,field1和field2并不总是以相同的顺序)。杰克逊也支持映射到您自己的Java对象。 jp.skipChildren()很方便:它允许跳过一个完整的对象树或数组,而无需运行其中包含的所有事件。

+0

您的代码真的很有用!我将它应用到了我的问题中,最终可以摆脱我的堆空间异常,因为我之前一次读取该文件:-) – 2013-06-21 11:11:45