JAVA - Лучший подход к анализу огромного (особо большого) файла JSON
Я пытаюсь разобрать огромный файл JSON (например http://eu.battle.net/auction-data/258993a3c6b974ef3e6f22ea6f822720/auctions.json) с помощью библиотеки gson (http://code.google.com/p/google-gson/) в JAVA.
Я хотел бы знать, что лучше всего подходит для синтаксического анализа такого большого файла (около 80 тыс. строк), и если вы знаете хороший API, который может помочь мне в этом.
Некоторая идея...
- читайте по строкам и избавляйтесь от формата JSON: но это глупость.
- уменьшить файл JSON, разделив этот файл на многие другие: но для этого я не нашел хорошего Java API.
- используйте этот файл напрямую в качестве базы данных nonSql, сохраните файл и используйте его в качестве моей базы данных.
Я бы очень признателен за рекламу/помощь/сообщения/:-)
Благодарю.
Ответы
Ответ 1
Вам не нужно переключаться на Джексон. Gson 2.1 представил новый интерфейс TypeAdapter, который позволяет проводить сериализацию и десериализацию сериального дерева и потоковой передачи.
API эффективен и гибкий. См. Gson Streaming doc для примера объединения типов дерева и привязки. Это строго лучше, чем смешанные потоковые и древовидные режимы; с привязкой вы не теряете память, создавая промежуточное представление ваших значений.
Как и Джексон, у Gson есть API, чтобы рекурсивно пропустить нежелательное значение; Gson называет это skipValue().
Ответ 2
Я предлагаю взглянуть на 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() удобен: он позволяет пропускать полное дерево объектов или массив без необходимости запускать все события, содержащиеся в нем.
Ответ 3
Библиотека декларативного потокового сопоставления (DSM) позволяет вам определять сопоставления между вашими данными 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}