Bester Ansatz, um riesige (extra große) JSON-Dateien zu analysieren

Lesezeit: 6 Minuten

Benutzeravatar von Dax
Dax

Ich versuche, eine riesige JSON-Datei (wie z http://eu.battle.net/auction-data/258993a3c6b974ef3e6f22ea6f822720/auctions.json) mit der gson-Bibliothek (http://code.google.com/p/google-gson/) in Java.

Ich würde gerne wissen, was der beste Ansatz ist, um diese Art von großer Datei (etwa 80.000 Zeilen) zu analysieren, und ob Sie vielleicht eine gute API kennen, die mir bei der Verarbeitung helfen kann.

Einige Ideen

  1. Zeile für Zeile lesen und das JSON-Format loswerden: aber das ist Unsinn.
  2. Reduzieren Sie die JSON-Datei, indem Sie diese Datei in viele andere aufteilen: aber ich habe keine gute Java-API dafür gefunden.
  3. Verwenden Sie diese Datei direkt als Nicht-Sql-Datenbank, behalten Sie die Datei und verwenden Sie sie als meine Datenbank.

  • Eine Java-EE-Alternative: javax.json.stream.JsonParser

    – xonia

    19. Januar 2018 um 8:35 Uhr

Sie müssen nicht zu Jackson wechseln. Gson 2.1 führte ein neues ein TypeAdapter Schnittstelle, die Mixed-Tree- und Streaming-Serialisierung und -Deserialisierung ermöglicht.

Die API ist effizient und flexibel. Sehen Gsons Streaming-Dokument für ein Beispiel zum Kombinieren von Baum und Bindung Modi. Dies ist absolut besser als gemischte Streaming- und Baummodi; Mit der Bindung verschwenden Sie keinen Speicher, um eine Zwischendarstellung Ihrer Werte zu erstellen.

Wie Jackson verfügt Gson über APIs, um einen unerwünschten Wert rekursiv zu überspringen; Gson nennt das überspringenWert().

  • Gibt es ein gutes Beispiel für die Verwendung von TypeAdapter zu gemischtem Stream-Parsing in Tree-Parsing? Ich habe einen Fall, in dem ich es in eine Liste von Objekten mischen möchte, die sehr groß wird. Das Beispiel in der Dokumentation ist das Stream-Parsing einer Liste von Messages, aber es zeigt nicht, wie Sie diesen Stream-Parser in einen Baum-Parser einbinden würden. (Es zeigt, wie Sie einen Baum-Parser in einen Stream-Parser einbinden.)

    – Dandre Allison

    27. Februar 2013 um 1:01 Uhr

  • Zum Beispiel: Ich habe CustomType Objektzuordnung zu definieren und CustomTypes extends ArrayList<CustomType>. Ich mache ein TypeAdapter<CustomTypes> die Objektzuordnung für jeden verwendet CustomType, gibt aber am Ende nur eine leere Liste zurück, um zu vermeiden, dass die gesamte Liste im Speicher gespeichert wird (schreiben Sie sie stattdessen in eine Datenbank). Und dann wird das enthaltende Objekt einfach mit Objektzuordnung analysiert.

    – Dandre Allison

    27. Februar 2013 um 1:41 Uhr

  • @DandreAllison: Ich habe das auch selbst gebraucht. Die Lösung besteht darin, einen JsonParser zu erstellen und dann aufzurufen parse(JsonReader json)die den nächsten Wert verbraucht und den Leser voranbringt.

    – Sebastian N.

    25. Mai 2016 um 14:01 Uhr

  • ich mag diese idee über stream json. Bu reparieren Sie bitte Ihren defekten Link TypeArray!

    – Serg Burlaka

    16. April 2018 um 9:01 Uhr

  • TypeAdapterFactory ?

    – aderchox

    13. September 2021 um 6:39 Uhr

vikiiiis Benutzeravatar
vikiiii

Ich werde vorschlagen, einen Blick darauf zu werfen Jackson API Es ist sehr einfach, die Streaming- und Baummodell-Parsing-Optionen zu kombinieren: Sie können sich auf Streaming-Weise durch die Datei als Ganzes bewegen und dann einzelne Objekte in eine Baumstruktur einlesen.

Als ein Beispielnehmen wir die folgende Eingabe:

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

Stellen Sie sich vor, die Felder seien spärlich oder die Datensätze hätten eine komplexere Struktur.

Das folgende Snippet veranschaulicht, wie diese Datei mit einer Kombination aus Stream- und Tree-Model-Parsing gelesen werden kann. Jeder einzelne Datensatz wird in einer Baumstruktur gelesen, aber die Datei wird nie vollständig in den Speicher gelesen, wodurch es möglich ist, Gigabyte große JSON-Dateien mit minimalem Speicherverbrauch zu verarbeiten.

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

Wie Sie sich vorstellen können, gibt der nextToken()-Aufruf jedes Mal das nächste Parsing-Ereignis aus: Startobjekt, Startfeld, Startarray, Startobjekt, …, Endobjekt, …, Endarray, …

Der jp.readValueAsTree() call ermöglicht es, das, was sich an der aktuellen Parsing-Position befindet, ein JSON-Objekt oder -Array, in Jacksons generisches JSON-Baummodell einzulesen. Sobald Sie dies haben, können Sie wahllos auf die Daten zugreifen, unabhängig von der Reihenfolge, in der die Dinge in der Datei erscheinen (im Beispiel sind Feld1 und Feld2 nicht immer in der gleichen Reihenfolge). Jackson unterstützt auch das Mapping auf Ihre eigenen Java-Objekte. Das jp.skipChildren() ist praktisch: Es ermöglicht das Überspringen eines kompletten Objektbaums oder eines Arrays, ohne dass Sie alle darin enthaltenen Ereignisse durchlaufen müssen.

  • Dein Code war wirklich hilfreich! Ich habe es an mein Problem angepasst und konnte endlich meine Heap-Space-Ausnahmen loswerden, weil ich die Datei vorher in einem Rutsch gelesen habe 🙂

    – Konrad Höffner

    21. Juni 2013 um 11:11 Uhr

Deklaratives Stream-Mapping (DSM) Mit der Bibliothek können Sie Zuordnungen zwischen Ihren JSON- oder XML-Daten und Ihrem POJO definieren. Sie müssen also keinen benutzerdefinierten Parser schreiben. Es hat eine leistungsstarke Unterstützung für Scripting (Javascript, Groovy, JEXL). Sie können Daten filtern und transformieren, während Sie lesen. Sie können Funktionen für partielle Datenoperationen aufrufen, während Sie Daten lesen. DSM liest Daten als Stream, sodass sehr wenig Speicher verwendet wird.

Zum Beispiel,

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

Stellen Sie sich vor, das obige Snippet ist Teil riesiger und komplexer JSON-Daten. wir wollen nur bekommen Zeug, das ein höheres Gehalt hat als 10000.

Zunächst müssen wir Mapping-Definitionen wie folgt definieren. Wie Sie sehen, handelt es sich lediglich um eine Yaml-Datei, die die Zuordnung zwischen POJO-Feldern und JSON-Datenfeldern enthält.

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 für Prozessmitarbeiter erstellen.

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.
            }
        };

Verwenden Sie DSM, um JSON zu verarbeiten

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

Ausgabe: (Nur Sachen mit einem Gehalt von mehr als 10000 sind enthalten)

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

1449900cookie-checkBester Ansatz, um riesige (extra große) JSON-Dateien zu analysieren

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy