我正在尝试使用JAVA中的gson库(http://eu.battle.net/auction-data/258993a3c6b974ef3e6f22ea6f822720/auctions.json)解析一些巨大的JSON文件(如http://code.google.com/p/google-gson/)。
我想知道什么是解析这种大文件(大约80k行)的最好的approch,如果你知道可以帮助我处理这个的好API。
有些想法......
我真的很感谢adices / help / messages / :-) 谢谢。
答案 0 :(得分:35)
我建议看看Jackson Api将流式和树型解析选项结合起来非常容易:您可以以流式方式整个文件移动,然后读取单个对象进入树状结构。
作为example,我们采取以下输入:
{
"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()调用都会给出下一个解析事件:start object,start field,start array,start object,...,end object,...,end array,...
jp.readValueAsTree()
调用允许将当前解析位置(JSON对象或数组)中的内容读入Jackson的通用JSON树模型中。一旦你有了这个,你可以随机访问数据,无论事物在文件中出现的顺序如何(在示例中field1和field2并不总是以相同的顺序)。 Jackson也支持映射到您自己的Java对象上。 jp.skipChildren()很方便:它允许跳过一个完整的对象树或数组,而不必自己运行它包含的所有事件。
答案 1 :(得分:34)
您无需切换到杰克逊。 Gson 2.1引入了一个新的TypeAdapter接口,允许混合树和流序列化和反序列化。
API高效灵活。有关组合树和绑定模式的示例,请参阅Gson's Streaming doc。这比混合流和树模式严格要好;通过绑定,您不会浪费内存来构建您的值的中间表示。
像杰克逊一样,Gson拥有递归跳过不需要的值的API; Gson称之为skipValue()。
答案 2 :(得分:1)
Declarative Stream Mapping (DSM)库允许您定义JSON或XML数据与POJO之间的映射。因此,您无需编写自定义解析器。 İt具有强大的脚本(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}