gson 使用流和DOM访问解析JSON?

qjp7pelc  于 2022-11-06  发布在  其他
关注(0)|答案(1)|浏览(201)

我有一个如下所示的JSON文档:

它是数组的集合-发现、资产、资产组等。我编写了一个函数,它接受文件名和请求的数组名称,并以字符串形式返回数组条目的ArrayList(我在客户端将其重新解析为JSON)当文件较小时,但是这个文件的大小超过了1.6GB,所以如果我尝试将其全部示例化为JSONObject的话,我会占用大量内存。我想尝试Jackson或GSon流API,但是我在尝试将流API与直接DOM访问混合起来时遇到了麻烦。比如,我想对JSON进行流处理,直到到达“assetGroups”节点,然后遍历该数组并返回其内容的List〈〉。这样做有意义吗?有什么帮助吗?

t40tm48m

t40tm48m1#

(The以下为Gson的数据)
您可能必须从JsonReader开始以流方式解析JSON文档。然后,首先使用其beginObject()方法输入顶级JSON对象。然后,在由hasNext()检查保护的循环中,使用nextName()获取下一个成员名称,并将其与所需名称进行比较。如果该名称不是您感兴趣的名称,您可以调用skipValue()来跳过它值:

JsonReader jsonReader = new JsonReader(reader);
jsonReader.beginObject();

while (jsonReader.hasNext()) {
    String name = jsonReader.nextName();

    if (name.equals(desiredName)) {

        ... // extract the data; see next section of this answer

        // Alternatively you could also return here already, ignoring the remainder
        // of the JSON data

    } else {
        jsonReader.skipValue();
    }
}

jsonReader.endObject();

if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
    throw new IllegalArgumentException("Trailing data after JSON value");
}

您可能还希望添加检查,以验证所需的成员确实存在于JSON文档中,并验证它只存在一次,而不是多次。
如果只想写回JSON文档的一个子集,而不对它进行任何内容修改,那么就不需要将它解析成DOM对象,而是直接从JsonReader中读取并写入JsonWriter

static void transferTo(JsonReader reader, JsonWriter writer) throws IOException {
    NumberHolder numberHolder = new NumberHolder();
    int nestingDepth = 0;

    while (true) {
        JsonToken token = reader.peek();
        switch (token) {
            case BEGIN_ARRAY:
                reader.beginArray();
                writer.beginArray();
                nestingDepth++;
                break;
            case END_ARRAY:
                reader.endArray();
                writer.endArray();

                nestingDepth--;
                if (nestingDepth <= 0) {
                    return;
                }

                break;
            case BEGIN_OBJECT:
                reader.beginObject();
                writer.beginObject();
                nestingDepth++;
                break;
            case NAME:
                writer.name(reader.nextName());
                break;
            case END_OBJECT:
                reader.endObject();
                writer.endObject();

                nestingDepth--;
                if (nestingDepth <= 0) {
                    return;
                }

                break;
            case BOOLEAN:
                writer.value(reader.nextBoolean());
                break;
            case NULL:
                reader.nextNull();
                writer.nullValue();
                break;
            case NUMBER:
                // Read the number as string
                String numberAsString = reader.nextString();

                // Slightly hacky workaround to preserve the original number value
                // without having to parse it (which could lead to precision loss)
                numberHolder.value = numberAsString;
                writer.value(numberHolder);

                break;
            case STRING:
                writer.value(reader.nextString());
                break;
            case END_DOCUMENT:
                throw new IllegalStateException("Unexpected end of document");
            default:
                throw new AssertionError("Unknown JSON token: " + token);
        }
    }
}

您可以在第一个程式码片段中标记为“撷取数据”的位置呼叫这个方法。
如果您确实需要相关的JSON文档部分作为DOM,那么您可以首先使用Gson.getAdapter来获取适配器,例如对于您的List<...>或对于JsonArray(通用DOM类;在这里,您不太可能冒转换过程中丢失任何精度的风险)。然后,您可以在第一个代码片段中标记为“extract the data”的位置使用该适配器的read(JsonReader)
建议不要直接使用Gson.fromJson(JsonReader, ...),因为它会更改给定JsonReader的配置;不幸的是,目前没有适当地记录这个缺陷(Gson issue)。
请注意,这两种方法都不保留原始JSON格式,但在内容方面应该没有区别。

相关问题