使用 java/GSON 解析大型 JSON,无法读取 JSON 结构

parsing large JSON with java/GSON, can't read the JSON structure

我正在尝试使用 Java 和 GSON 解析来自 Musicbrainz.org

的 JSON 格式的大型(大约 10GB)数据库转储

JSON 文件具有此结构。没有 '[' ']' 表示这将是一个对象数组,并且每个对象之间没有 ','。不知道为什么,这个 JSON 文件就是这样。

{
    "id": "d0ab06e1-751a-414b-a976-da72670391b1",
    "name": "Arcing Wires",
    "sort-name": "Arcing Wires"
}
{
    "id": "6f0c2c16-dd7e-4268-a484-bc7b2ac78108",
    "name": "Another",
    "sort-name": "Another"
}
{
    "id": "e062b6cd-5506-47b0-afdb-72f4279ec38c",
    "name": "Agent S",
    "sort-name": "Agent S"
}

这是我正在使用的代码:

        try(JsonReader jsonReader = new JsonReader(
            new InputStreamReader(
                    new FileInputStream(jsonFilePath), StandardCharsets.UTF_8))) {
        Gson gson = new GsonBuilder().create();
        jsonReader.beginArray();
        while (jsonReader.hasNext()) {
            Artist mapped = gson.fromJson(jsonReader, Artist.class);
            //TODO do something with the object
            }
        }
        jsonReader.endArray();
    }
    catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

我映射的class是这样的:

public class Artist {

@SerializedName("id")
public String id;
@SerializedName("name")
public String name;
@SerializedName("sort-name")
public String sortName;

}

我遇到的错误:

Exception in thread "main" java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:350)
at DBLoader.parse(DBLoader.java:39)
at DBLoader.main(DBLoader.java:23)

我相信 GSON 期望的结构与我声明的结构不同,但我不明白我应该如何定义这种没有逗号和括号的 JSON。 有什么线索吗? 谢谢

它看起来像 jsonl 格式,其中每一行都是有效的 JSON 对象。 (阅读更多 here
您可以逐行读取文件并转换为对象。我认为它会起作用。

JSON 默认只声明一个最高值(是的,这将是一个 有效的 JSON 文档 ),但是有 JSON streaming 使用任意技术将多个 JSON 元素连接成一个流,假设流消费者可以解析它(read more). Gson supports a so-called lenient mode that turns off the "one top value only" mode (and does some more things irrelevant to the question) for JsonReader: setLenient。开启宽松模式,可以一个一个读取JSON个元素,结果发现这个模式可以用来parse/readline-delimitedJSON连接 JSON 值 因为它们只是由 Gson 忽略的零个或多个空格分隔(因此更奇特 记录分隔符分隔 JSONlength-prefixed JSON 不受支持)。它对您不起作用的原因是您的初始代码假定流包含单个 JSON 数组(显然不是:它应该是不符合 JSON 数组语法的元素流)。

一个简单的通用 JSON 流支持可能看起来像这样(使用 Stream API 因为它比 Iterator 更丰富 API,但它很好地展示一个想法,您可以轻松地将其适应迭代器、回调、可观察流,无论您喜欢什么):

@UtilityClass
public final class JsonStreamSupport {

    public static <T> Stream<T> parse(@WillNotClose final JsonReader jsonReader, final Function<? super JsonReader, ? extends T> readElement) {
        final boolean isLenient = jsonReader.isLenient();
        jsonReader.setLenient(true);
        final Spliterator<T> spliterator = new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, Spliterator.ORDERED) {
            @Override
            public boolean tryAdvance(final Consumer<? super T> action) {
                try {
                    final JsonToken token = jsonReader.peek();
                    if ( token == JsonToken.END_DOCUMENT ) {
                        return false;
                    }
                    // TODO: read more elements in batch
                    final T element = readElement.apply(jsonReader);
                    action.accept(element);
                    return true;
                } catch ( final IOException ex ) {
                    throw new RuntimeException(ex);
                }
            }
        };
        return StreamSupport.stream(spliterator, false)
                .onClose(() -> jsonReader.setLenient(isLenient));
    }

}

然后:

JsonStreamSupport.<Artist>parse(jsonReader, jr -> gson.fromJson(jr, Artist.class))
        .forEach(System.out::println);

输出(假设 Artist 有 Lombok 生成的 toString()):

Artist(id=d0ab06e1-751a-414b-a976-da72670391b1, name=Arcing Wires, sortName=Arcing Wires)
Artist(id=6f0c2c16-dd7e-4268-a484-bc7b2ac78108, name=Another, sortName=Another)
Artist(id=e062b6cd-5506-47b0-afdb-72f4279ec38c, name=Agent S, sortName=Agent S)

这种方法 JSON 流式传输保存了多少字节,以便在您尝试使用的服务中使用它?我不知道。