我想知道是否有一种“正确”的方法来使用杰克逊解析JSON文件,其中JSON文件包含一个巨大的属性,而无需将整个流加载到内存中。我需要保持低内存,因为它是一个Android应用程序。我不是在这里问如何使用Android:解析大JSON文件,而是一个属性非常大,其他的并不重要。
例如,假设我有以下内容:
{
"filename": "afilename.jpg",
"data": "**Huge data here, about 20Mb base64 string**",
"mime": "mimeType",
"otherProperties": "..."
}
如果需要,可以将数据属性提取到新文件中(通过输出流或其他含义),但我无法使用杰克逊实现这一点。我可以使用其他库,我只是认为杰克逊是理想的,这要归功于它的流式应用编程接口。
谢啦
编辑:对于这个问题,这不是一个好的答案——如果子树是要绑定的对象,它会起作用,但如果问题是一个大的Base64编码字符串,则不会起作用。
如果我正确理解了这个问题,是的,如果你的输入由一系列JSON对象或数组组成,你可以增量读取文件,但仍然可以绑定数据。
如果是这样的话,您可以使用JsonParser
将流推进到指向第一个对象(其开始对象标记),然后在JsonParser
(JsonParser.readValueAs()
)或ObjectMapper
(ObjectMapper.readValue(JsonParser,type)
)中使用数据绑定方法。
类似的东西:
ObjectMapper mapper = new ObjectMapper();
JsonParser jp = mapper.getJsonFactory().createJsonParser(new File("file.json"));
while (jp.nextToken() != null) {
MyPojo pojo = jp.readValueAs(MyPojo.class);
// do something
}
(注意:根据JSON的确切结构,您可能需要跳过一些元素——在调用readValueAs()时,解析器必须已收到启动JSON对象绑定的START_元素)。
或者,更简单的是,您可以在ObjectReader
中使用方法readValues
:
ObjectReader r = mapper.reader(MyPojo.class);
MappingIterator<MyPojo> it = r.readValues(new File("file.json"));
while (it.hasNextValue()) {
MyPojo pojo. = it.nextValue();
// do something with it
}
在这两种情况下,Jackson data binder只读取生成单个对象(MyPojo或任何类型)所需的JSON标记JsonParser
本身只需要足够的内存来包含单个JSON令牌上的信息。
最后,我成功地恢复了我的海量数据,其中,in
是一个输入流,位于我想要解析数据的json文件上,out
是我要将数据写入的文件:
public boolean extrationContenuDocument(FileInputStream in, FileOutputStream out, FileInfo info)
throws JsonParseException, IOException {
SerializedString keyDocContent = new SerializedString("data");
boolean isDone = false;
JsonParser jp = this.jsonFactory.createJsonParser(in);
// Let's move our inputstream cursor until the 'data' property is found
while (!jp.nextFieldName(keyDocContent)) {
Log.v("Traitement JSON", "Searching for 'data' property ...");
}
// Found it? Ok, move the inputstream cursor until the begining of it's
// content
JsonToken current = jp.nextToken();
// if the current token is not String value it means u didn't found the
// 'data' property or it's content is not a correct => stop
if (current == JsonToken.VALUE_STRING) {
Log.v("Traitement JSON", "Property 'data' found");
// Here it gets a little tricky cause if the file is not big enough
// all the content of the 'data' property could be read directly
// insted of using this
if (info.getSize() > TAILLE_MIN_PETIT_FICHER) {
Log.v("Traitement JSON", "the content of 'data' is too big to be read directly -> using buffered reading");
// JsonParser uses a buffer to read, there is some data that
// could have been read by it, i need to fetch it
ByteArrayOutputStream debutDocStream = new ByteArrayOutputStream();
int premierePartieRead = jp.releaseBuffered(debutDocStream);
byte[] debutDoc = debutDocStream.toByteArray();
// Write the head of the content of the 'data' property, this is
// actually what as read from the inputstream by the JsonParser
// when did jp.nextToken()
Log.v("Traitement JSON", "Write the head");
out.write(debutDoc);
// Now we need to write the rest until we find the tail of the
// content of the 'data' property
Log.v("Traitement JSON", "Write the middle");
// So i prepare a buffer to continue reading the inputstream
byte[] buffer = new byte[TAILLE_BUFFER_GROS_FICHER];
// The escape char that determines where to stop reading will be "
byte endChar = (byte) '"';
// Fetch me some bytes from the inputstream
int bytesRead = in.read(buffer);
int bytesBeforeEndChar = 0;
int deuxiemePartieRead = 0;
boolean isDocContentFin = false;
// Are we at the end of the 'data' property? Keep writing the
// content of the 'data' property if it's not the case
while ((bytesRead > 0) && !isDocContentFin) {
bytesBeforeEndChar = 0;
// Since am using a buffer the escape char could be in the
// middle of it, gotta look if it is
for (byte b : buffer) {
if (b != endChar) {
bytesBeforeEndChar++;
} else {
isDocContentFin = true;
break;
}
}
if (bytesRead > bytesBeforeEndChar) {
Log.v("Traitement JSON", "Write the tail");
out.write(buffer, 0, bytesBeforeEndChar);
deuxiemePartieRead += bytesBeforeEndChar;
} else {
out.write(buffer, 0, bytesRead);
deuxiemePartieRead += bytesRead;
}
bytesRead = in.read(buffer);
}
Log.v("Traitement JSON", "Bytes read: " + (premierePartieRead + deuxiemePartieRead) + " (" + premierePartieRead + " head,"
+ deuxiemePartieRead + " tail)");
isDone = true;
} else {
Log.v("Traitement JSON", "File is small enough to be read directly");
String contenuFichier = jp.getText();
out.write(contenuFichier.getBytes());
isDone = true;
}
} else {
throw new JsonParseException("The property " + keyDocContent.getValue() + " couldn't be found in the Json Stream.", null);
}
jp.close();
return isDone;
}
它不漂亮,但很有魅力@斯塔克斯曼让我知道你的想法。
这现在是一个实现的特性,请参见:https://github.com/FasterXML/jackson-core/issues/14和JsonParser.readBinaryValue()
我想使用Typescript解析/处理一个25 MB的JSON文件,并过滤/排序对象。。我写的代码花了几分钟(有时会超时),不确定为什么会发生这种情况,或者是否有其他方法可以提高代码的效率。 注意:代码在一个小文件上工作
问题内容: 我想使用 GSON* 在 JAVA中 解析此 JSON 文件: *** 但是我不知道如何加入root元素: 描述符 ,之后是 app3 元素,最后是 name 元素。 我遵循了本教程http://www.mkyong.com/java/gson-streaming-to-read-and-write- json/ ,但是它没有显示具有root和childs元素的情况。 问题答案: Im
我学习了这个教程http://www.mkyong.com/java/gson-streaming-to-read-and-write-json/,但它没有显示root和childs元素的情况。
问题内容: 我正在创建一个Android应用程序,该应用程序应将Json从文件或URL解析为jsonarray和jsonobjects。问题是我的json是3.3 mb,当我使用一个简单的代码时,是这样的:(现在无法访问我的真实代码,因为我正在工作,从教程中复制了一些代码;因此其中可能有一些错误) (假设我已经有我的输入流内容) 当我在Android设备上运行此代码时,将字符串解析为jsonArr
我正在创建一个android应用程序,它应该解析一个Json从一个文件或url到一个json数组和json对象。问题是我的json是3.3mb,当我使用一个简单的代码时,就像这样:(不能访问我的真实代码,因为我在工作,从教程中复制了一些代码;所以可能会有一些错误) (假设我已经有了inputstream内容) 当我在android设备上运行这段代码时,在将字符串解析为jsonArray时,会出现O
问题内容: 我将sql存储在属性文件中,并使用spring注入它,这有效: 但出于可读性,我想要这样: 我需要使用什么正确的文本格式? 问题答案: 在行的末尾使用\ 另外,请注意任何尾随空格,因为Java在组装线时会寻找连续的反斜杠+换行符。 换种说法:反斜线必须是换行符之前该行的最后一个字符。