当前位置: 首页 > 知识库问答 >
问题:

Android:解析JSON文件时使用了一个大属性,内存使用率很低

夏飞跃
2023-03-14

我想知道是否有一种“正确”的方法来使用杰克逊解析JSON文件,其中JSON文件包含一个巨大的属性,而无需将整个流加载到内存中。我需要保持低内存,因为它是一个Android应用程序。我不是在这里问如何使用Android:解析大JSON文件,而是一个属性非常大,其他的并不重要。

例如,假设我有以下内容:

{
    "filename": "afilename.jpg",
    "data": "**Huge data here, about 20Mb base64 string**",
    "mime": "mimeType",
    "otherProperties": "..."
}

如果需要,可以将数据属性提取到新文件中(通过输出流或其他含义),但我无法使用杰克逊实现这一点。我可以使用其他库,我只是认为杰克逊是理想的,这要归功于它的流式应用编程接口。

谢啦

共有2个答案

阙项禹
2023-03-14

编辑:对于这个问题,这不是一个好的答案——如果子树是要绑定的对象,它会起作用,但如果问题是一个大的Base64编码字符串,则不会起作用。

如果我正确理解了这个问题,是的,如果你的输入由一系列JSON对象或数组组成,你可以增量读取文件,但仍然可以绑定数据。

如果是这样的话,您可以使用JsonParser将流推进到指向第一个对象(其开始对象标记),然后在JsonParserJsonParser.readValueAs())或ObjectMapperObjectMapper.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令牌上的信息。

劳高爽
2023-03-14

最后,我成功地恢复了我的海量数据,其中,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在组装线时会寻找连续的反斜杠+换行符。 换种说法:反斜线必须是换行符之前该行的最后一个字符。