2012-02-22 3418 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