2012-02-22 15 views
27

Sto cercando di analizzare un enorme file JSON (come http://eu.battle.net/auction-data/258993a3c6b974ef3e6f22ea6f822720/auctions.json) utilizzando la libreria gson (http://code.google.com/p/google-gson/) in JAVA.JAVA - Il miglior approccio per analizzare un file JSON enorme (extra large)

Mi piacerebbe sapere qual è il miglior approcio per analizzare questo tipo di file di grandi dimensioni (circa 80.000 linee) e se si può conoscere una buona API che può aiutarmi a elaborare questo. Linea

Alcuni idea ...

  1. lettura per riga e sbarazzarsi del formato JSON: ma questa è una sciocchezza.
  2. ridurre il file JSON dividendo questo file in molti altri: ma non ho trovato alcuna buona API Java per questo.
  3. utilizzare questo file direttamente come database non SQL, conservare il file e utilizzarlo come database.

Apprezzerei molto annunci/aiuto/messaggi/:-) Grazie.

+0

Un'alternativa Java EE: javax.json.stream.JsonParser – xonya

risposta

27

Non è necessario passare a Jackson. Gson 2.1 ha introdotto una nuova interfaccia TypeAdapter che consente la serializzazione e la deserializzazione di alberi e streaming.

L'API è efficiente e flessibile. Vedere Gson's Streaming doc per un esempio di combinazione di albero e modalità vincolante. Questo è strettamente migliore delle modalità miste streaming e albero; con il binding non sprechi la memoria costruendo una rappresentazione intermedia dei tuoi valori.

Come Jackson, Gson ha API per saltare in modo ricorsivo un valore indesiderato; Gson chiama questo skipValue().

+0

Lo controllerò! Grazie per aver condiviso – Dax

+0

Esiste un buon esempio di utilizzo di 'TypeAdapter' per l'analisi mista del flusso nell'analisi degli alberi? Ho un caso in cui voglio mescolarlo in una lista di oggetti che diventa molto grande. L'esempio nella documentazione è lo stream che analizza un elenco di 'Message's ma non mostra come legherebbe il parser del flusso in un parser ad albero. (Mostra come si lega un parser ad albero in un parser di flusso) –

+0

Ad esempio: ho 'CustomType' per definire il mapping degli oggetti, e' CustomTypes estende ArrayList '. Faccio un 'TypeAdapter ' che usa il mapping degli oggetti per ogni' CustomType', ma restituisce semplicemente una lista vuota alla fine per evitare di memorizzare l'intera lista in memoria (invece di scriverli su un database). E quindi l'oggetto contenitore viene analizzato semplicemente usando la mappatura degli oggetti. –

25

Suggerisco di dare un'occhiata a Jackson Api è molto facile combinare le opzioni di analisi dello streaming e del modello ad albero: è possibile spostarsi nel file nel suo complesso in modo streaming e quindi leggere singoli oggetti in un albero struttura.

Come example, prendiamo il seguente testo:

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

Provate a immaginare i campi di essere sparsa o le registrazioni avere una struttura più complessa.

Il seguente frammento illustra come questo file può essere letto utilizzando una combinazione di analisi del flusso e del modello ad albero. Ogni singolo record viene letto in una struttura ad albero, ma il file non viene mai letto nella sua interezza nella memoria, rendendo possibile elaborare dimensioni di gigabyte di file JSON mentre si utilizza una memoria minima.

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(); 
      } 
     }     
     } 
    } 

Come si può intuire, il nextToken() chiamare ogni volta che dà il prossimo evento di analisi: iniziare oggetto, avviare campo, iniziare a matrice, avviare oggetto, ..., oggetto end, ..., un array end , ...

La chiamata jp.readValueAsTree() consente di leggere ciò che è nella posizione di analisi corrente, un oggetto o array JSON, nel modello di albero JSON generico di Jackson. Una volta ottenuto questo, è possibile accedere ai dati in modo casuale, indipendentemente dall'ordine in cui le cose appaiono nel file (nel campo di esempio 1 e nel campo 2 non sono sempre nello stesso ordine). Jackson supporta anche la mappatura sui propri oggetti Java. Il jp.skipChildren() è comodo: permette di saltare un albero di oggetti completo o un array senza doverti eseguire su tutti gli eventi in esso contenuti.

+0

Il tuo codice è stato davvero utile! L'ho adattato al mio problema e alla fine ho potuto eliminare le eccezioni dello spazio del mio heap perché ho letto il file in una volta sola :-) –

Problemi correlati