Java >> Java tutorial >  >> Tag >> Json

JAVA - Bedste tilgang til at analysere enorm (ekstra stor) JSON-fil

Jeg vil foreslå at tage et kig på Jackson Api, det er meget nemt at kombinere streaming- og træmodelparsing-mulighederne:du kan bevæge dig gennem filen som helhed på en streaming måde og derefter læse individuelle objekter ind i en træstruktur.

Som et eksempel, lad os tage følgende input:

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

Forestil dig bare, at felterne er sparsomme, eller at posterne har en mere kompleks struktur.

Det følgende uddrag illustrerer, hvordan denne fil kan læses ved hjælp af en kombination af stream og træmodel-parsing. Hver enkelt post læses i en træstruktur, men filen læses aldrig i sin helhed ind i hukommelsen, hvilket gør det muligt at behandle JSON-filer gigabyte i størrelse, mens der bruges minimal hukommelse.

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

Som du kan gætte, giver nextToken()-kaldet hver gang den næste parsing-begivenhed:startobjekt, startfelt, startarray, startobjekt, ..., slutobjekt, ..., slutarray, ...

jp.readValueAsTree() call giver mulighed for at læse, hvad der er ved den aktuelle parsingposition, et JSON-objekt eller et array, i Jacksons generiske JSON-træmodel. Når du har dette, kan du få adgang til dataene tilfældigt, uanset i hvilken rækkefølge tingene optræder i filen (i eksemplet er felt1 og felt2 ikke altid i samme rækkefølge). Jackson understøtter også kortlægning til dine egne Java-objekter. jp.skipChildren() er praktisk:det gør det muligt at springe over et komplet objekttræ eller en matrix uden at skulle køre dig selv over alle begivenhederne i det.


Du behøver ikke at skifte til Jackson. Gson 2.1 introducerede en ny TypeAdapter-grænseflade, der tillader blandet træ- og streaming-serialisering og deserialisering.

API'et er effektivt og fleksibelt. Se Gsons Streaming-dokument for et eksempel på at kombinere træ og binding tilstande. Dette er strengt taget bedre end blandet streaming og trætilstande; med binding spilder du ikke hukommelse på at bygge en mellemrepræsentation af dine værdier.

Ligesom Jackson har Gson API'er til rekursivt at springe over en uønsket værdi; Gson kalder dette skipValue().


Declarative Stream Mapping (DSM) bibliotek giver dig mulighed for at definere kortlægninger mellem dine JSON- eller XML-data og din POJO. Så du behøver ikke at skrive en brugerdefineret parser. Det har kraftfuld scripting (Javascript, groovy, JEXL) understøttelse. Du kan filtrere og transformere data, mens du læser. Du kan kalde funktioner til delvis datadrift, mens du læser data. DSM læser data som en Stream, så den bruger meget lav hukommelse.

For eksempel

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

forestil dig, at ovenstående uddrag er en del af enorme og komplekse JSON-data. vi ønsker kun at få ting, der har en løn, der er højere end 10.000 .

Først og fremmest skal vi definere kortlægningsdefinitioner som følger. Som du kan se, er det kun en yaml-fil, der indeholder kortlægningen mellem POJO-felter og felt af JSON-data.

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

Opret FunctionExecutor til procespersonale.

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

Brug DSM til at behandle 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);

Output:(Kun ting, der har en højere løn end 10.000 er inkluderet)

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

Java tag