web-dev-qa-db-fra.com

JAVA - Meilleure approche pour analyser un fichier JSON énorme (extra large)

J'essaie d'analyser un énorme fichier JSON (comme http://eu.battle.net/auction-data/258993a3c6b974ef3e6f22ea6f822720/auctions.json ) en utilisant la bibliothèque gson ( http://code.google.com/p/google-gson/ ) en Java.

Je voudrais savoir quelle est la meilleure approche pour analyser ce type de gros fichier (environ 80k lignes) et si vous connaissez une bonne API qui peut m'aider à traiter cela.

Une idée...

  1. lire ligne par ligne et se débarrasser du format JSON: mais c'est absurde.
  2. réduire le fichier JSON en divisant ce fichier en beaucoup d'autres: mais je n'ai trouvé aucun bon Java API pour cela.
  3. utilisez ce fichier directement comme base de données non SQL, conservez le fichier et utilisez-le comme ma base de données.

J'apprécierais vraiment les conseils/aide/messages/:-) Merci.

40
Dax

Vous n'avez pas besoin de passer à Jackson. Gson 2.1 a introduit une nouvelle interface TypeAdapter qui permet une arborescence mixte et une sérialisation et désérialisation en streaming.

L'API est efficace et flexible. Voir Gson's Streaming doc pour un exemple de combinaison d'arborescence et de liaison de modes. C'est strictement mieux que les modes mixtes de streaming et d'arborescence; avec la liaison, vous ne perdez pas de mémoire en créant une représentation intermédiaire de vos valeurs.

Comme Jackson, Gson possède des API pour ignorer récursivement une valeur indésirable; Gson appelle cela skipValue () .

35
Jesse Wilson

Je suggérerai de jeter un œil à Jackson Api il est très facile de combiner les options de streaming et d'analyse de modèle d'arbre: vous pouvez parcourir le fichier dans son ensemble de manière en streaming, puis lire individuellement objets dans une structure arborescente.

En tant que exemple , prenons l'entrée suivante:

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

Imaginez simplement que les champs sont clairsemés ou que les enregistrements ont une structure plus complexe.

L'extrait de code suivant illustre comment ce fichier peut être lu à l'aide d'une combinaison d'analyse de flux et de modèle d'arbre. Chaque enregistrement individuel est lu dans une arborescence, mais le fichier n'est jamais lu dans sa totalité dans la mémoire, ce qui permet de traiter des fichiers JSON de gigaoctets en utilisant une mémoire minimale.

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

Comme vous pouvez le deviner, l'appel nextToken () donne à chaque fois le prochain événement d'analyse: objet de début, champ de début, tableau de début, objet de début, ..., objet de fin, ..., tableau de fin, ...

L'appel jp.readValueAsTree() permet de lire ce qui se trouve à la position d'analyse actuelle, un objet ou un tableau JSON, dans le modèle d'arbre JSON générique de Jackson. Une fois que vous avez cela, vous pouvez accéder aux données de manière aléatoire, quel que soit l'ordre dans lequel les éléments apparaissent dans le fichier (dans l'exemple, field1 et field2 ne sont pas toujours dans le même ordre). Jackson prend en charge le mappage sur vos propres Java également. Le jp.skipChildren () est pratique: il permet de sauter une arborescence d'objets complète ou un tableau sans avoir à s'exécuter vous-même sur tous les événements contenus en elle.

36
vikiiii

Declarative Stream Mapping (DSM) la bibliothèque vous permet de définir des mappages entre vos données JSON ou XML et votre POJO. Vous n'avez donc pas besoin d'écrire un analyseur personnalisé. Il a un puissant support de script (Javascript, groovy, JEXL). Vous pouvez filtrer et transformer des données pendant que vous lisez. Vous pouvez appeler des fonctions pour une opération de données partielles pendant que vous lisez des données. DSM lit les données sous forme de flux et utilise donc une mémoire très faible.

Par exemple,

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

imaginez l'extrait ci-dessus fait partie de données JSON énormes et complexes. nous voulons seulement obtenir des choses qui ont un salaire supérieur à 10000.

Tout d'abord, nous devons définir les définitions de mappage comme suit. Comme vous le voyez, il s'agit simplement d'un fichier yaml qui contient le mappage entre les champs POJO et le champ de données JSON.

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

Créez FunctionExecutor pour le personnel de processus.

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

Utiliser DSM pour traiter 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);

Sortie: (Seuls les éléments dont le salaire est supérieur à 10000 sont inclus)

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