2014-07-19 6 views
5

ich eine Datei, die ein JSON-Array von Objekten enthält:Verwenden Jackson zum Streamen ein Array von Json Parse Objekte

[ { "test1": "abc" }, { "test2": [1, 2, 3] } ]

ich wünsche Verwendung Jacksons JsonParser verwenden, um eine Eingabestrom aus dieser Datei zu nehmen, und bei jedem Aufruf .NEXT(), ich will es ein Objekt aus dem Array zurück bis es keine Objekte mehr hat oder ausfällt.

Ist das möglich?

Anwendungsfall: Ich habe eine große Datei mit einem JSON-Array mit einer großen Anzahl von Objekten mit unterschiedlichen Schemas gefüllt. Ich möchte ein Objekt nach dem anderen holen, um nicht alles in den Speicher zu laden.

EDIT:

Ich habe ganz vergessen zu erwähnen. Meine Eingabe ist eine Zeichenfolge, die im Laufe der Zeit hinzugefügt wird. Es sammelt sich langsam im Laufe der Zeit an. Ich hatte gehofft, es Objekt für Objekt analysieren zu können, das geparste Objekt vom String entfernend.

Aber ich nehme an, das macht nichts! Ich kann das manuell tun, solange der jsonParser den Index in die Zeichenfolge zurückgibt.

Antwort

10

Was Sie suchen, heißt Jackson Streaming API. Hier ist ein Code-Snippet mit Jackson Streaming API, das Ihnen helfen könnte, das zu erreichen, was Sie brauchen. Antwort

ObjectMapper mapper = new ObjectMapper(); 
JsonParser parser = mapper.getFactory().createParser(new File(...)); 
if(parser.nextToken() != JsonToken.START_ARRAY) { 
    throw new IllegalStateException("Expected an array"); 
} 
while(parser.nextToken() == JsonToken.START_OBJECT) { 
    // read everything from this START_OBJECT to the matching END_OBJECT 
    // and return it as a tree model ObjectNode 
    ObjectNode node = mapper.readTree(parser); 

    // do whatever you need to do with this object 
} 

parser.close(); 
20

Ja, können Sie diese Art von Teil-Streaming-Teilbaum-Modell Verarbeitung Stil mit einem ObjectMapper erreichen. Sie können auch einen JsonPointer verwenden, um die Startposition zu finden, wenn sie in einem Dokument verschachtelt ist. Auf diese Weise wird vermieden, dass die leicht umständliche Streaming-Token-Methode angepasst wird, um zum Startpunkt zu gelangen. In diesem Fall ist der basePath "/", aber es kann ein beliebiger Pfad sein, den JsonPointer versteht.

Path sourceFile = Paths.get("/path/to/my/file.json"); 
// Point the basePath to a starting point in the file 
JsonPointer basePath = JsonPointer.compile("/"); 
ObjectMapper mapper = new ObjectMapper(); 
try (InputStream inputSource = Files.newInputStream(sourceFile); 
    JsonParser baseParser = mapper.getFactory().createParser(inputSource); 
    JsonParser filteredParser = new FilteringParserDelegate(baseParser, 
        new JsonPointerBasedFilter(basePath), false, false);) { 
    // Call nextToken once to initialize the filteredParser 
    JsonToken basePathToken = filteredParser.nextToken(); 
    if (basePathToken != JsonToken.START_ARRAY) { 
     throw new IllegalStateException("Base path did not point to an array: found " 
             + basePathToken); 
    } 
    while (filteredParser.nextToken() == JsonToken.START_OBJECT) { 
     // Parse each object inside of the array into a separate tree model 
     // to keep a fixed memory footprint when parsing files 
     // larger than the available memory 
     JsonNode nextNode = mapper.readTree(filteredParser); 
     // Consume/process the node for example: 
     JsonPointer fieldRelativePath = JsonPointer.compile("/test1"); 
     JsonNode valueNode = nextNode.at(fieldRelativePath); 
     if (!valueNode.isValueNode()) { 
      throw new IllegalStateException("Did not find value at " 
        + fieldRelativePath.toString() 
        + " after setting base to " + basePath.toString()); 
     } 
     System.out.println(valueNode.asText()); 
    } 
} 
+1

Hey Ian, nach fast 2 Jahren funktioniert dieser Code tatsächlich noch und rettete meinen Tag. Nur zu versuchen, zu bestätigen, jedes Mal, wenn Mapper den Lese-Baum bearbeitet, bis das END_OBJECT-Token übereinstimmt, wo es gestartet wurde, wird der "Cursor" des Parsers auch dorthin bewegt, richtig? also, wenn ich nach der while-Schleife noch einen 'parser.nextToken()' mache, sollte ich das nächste Objekt nach dem lesen, was gerade gelesen wird, richtig? –

+1

@JamesJiang korrekt. Der 'readTree', der einem Parser gegeben wird, der auf dem' START_OBJECT' positioniert ist, wird Ereignisse von dem Parser verbrauchen, bis er das passende 'END_OBJECT' erreicht und den Parser darauf belassen wird. –

1

Dies ist eine späte Antwort, die baut auf Ian Robert:

JsonFactory factory = new JsonFactory(); 
JsonParser parser = factory.createJsonParser(new File(yourPathToFile)); 

JsonToken token = parser.nextToken(); 
if (token == null) { 
    // return or throw exception 
} 

// the first token is supposed to be the start of array '[' 
if (!JsonToken.START_ARRAY.equals(token)) { 
    // return or throw exception 
} 

// iterate through the content of the array 
while (true) { 

    token = parser.nextToken(); 
    if (!JsonToken.START_OBJECT.equals(token)) { 
     break; 
    } 
    if (token == null) { 
     break; 
    } 

    // parse your objects by means of parser.getXxxValue() and/or other parser's methods 

} 
Verwandte Themen