JAVA - лучший подход для разбора огромного (очень большого) файла JSON - PullRequest
38 голосов
/ 22 февраля 2012

Я пытаюсь проанализировать какой-то огромный JSON-файл (например, http://eu.battle.net/auction-data/258993a3c6b974ef3e6f22ea6f822720/auctions.json), используя библиотеку gson (http://code.google.com/p/google-gson/) в JAVA.

Я хотел бы знать, какой метод лучше всего подходит)проанализировать этот вид большого файла (около 80 тыс. строк) и, если вы знаете хороший API, который может помочь мне в его обработке.

Некоторая идея ...

  1. чтение построчнои избавьтесь от формата JSON: но это чепуха.
  2. уменьшите файл JSON, разбив этот файл на множество других: но я не нашел для этого хорошего Java API.
  3. используйте этосохраните файл и используйте его в качестве базы данных.

Я был бы очень признателен за adices / help / messages / :-) Спасибо.

Ответы [ 3 ]

35 голосов
/ 22 февраля 2012

Предлагаю взглянуть на Jackson Api , очень легко объединить параметры потоковой передачи и анализа древовидной модели: вы можете перемещаться по файлу в целом потоковым способом, а затем читать отдельные объекты в древовидную структуру.

В качестве примера , давайте возьмем следующий ввод:

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

Только представьте, что поля редки или записи имеют более сложную структуру.

Следующий фрагмент иллюстрирует, как можно прочитать этот файл, используя комбинацию анализа потока и древовидной модели. Каждая отдельная запись читается в древовидной структуре, но файл никогда полностью не читается в память, что позволяет обрабатывать файлы JSON размером в гигабайты при минимальном объеме памяти.

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

Как вы можете догадаться, вызов nextToken () каждый раз дает следующее событие синтаксического анализа: начальный объект, начальное поле, начальный массив, начальный объект, ..., конечный объект, ..., конечный массив, ...

Вызов jp.readValueAsTree() позволяет считывать то, что находится в текущей позиции синтаксического анализа, объект JSON или массив, в общую модель дерева JSON Джексона. Если у вас есть это, вы можете получить доступ к данным в произвольном порядке, независимо от того, в каком порядке они появляются в файле (в примере field1 и field2 не всегда находятся в одинаковом порядке). Джексон также поддерживает отображение на ваши собственные объекты Java. Jp.skipChildren () удобен: он позволяет пропускать полное дерево объектов или массив без необходимости запускать все события, содержащиеся в нем.

34 голосов
/ 27 февраля 2012

Вам не нужно переключаться на Джексона. В Gson 2.1 появился новый интерфейс TypeAdapter , который разрешает сериализацию и десериализацию смешанного дерева и потоковой передачи.

API эффективен и гибок. См. Потоковый документ Gson для примера объединения дерева и режимов привязки . Это строго лучше, чем смешанные потоковые и древовидные режимы; со связыванием вы не тратите память на построение промежуточного представления ваших значений.

Как и Джексон, у Gson есть API для рекурсивного пропуска нежелательного значения; Gson называет это skipValue () .

1 голос
/ 06 мая 2019

Декларативное потоковое сопоставление (DSM) * Библиотека 1002 * позволяет определять сопоставления между данными JSON или XML и вашим POJO. Так что вам не нужно писать собственный анализатор. Он имеет мощную поддержку сценариев (Javascript, groovy, JEXL). Вы можете фильтровать и преобразовывать данные во время чтения. Вы можете вызывать функции для частичной обработки данных во время чтения данных. DSM считывает данные в виде потока, поэтому он использует очень мало памяти.

Например,

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

представьте, что приведенный выше фрагмент является частью огромных и сложных данных JSON. мы только хотим получить материал с зарплатой выше 10000 .

Прежде всего, мы должны определить определения отображения следующим образом. Как видите, это всего лишь файл yaml, который содержит отображение между полями POJO и полем данных 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

Создать FunctionExecutor для персонала процесса.

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

Использование DSM для обработки 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);

Вывод: (включается только то, что имеет зарплату выше 10000)

{firstName=low, lastName=yin fong, nickName=fong fong, salary=200000}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...