使用 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 忽略的零个或多个空格分隔(因此更奇特 记录分隔符分隔 JSON 和 length-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 流式传输保存了多少字节,以便在您尝试使用的服务中使用它?我不知道。
我正在尝试使用 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 忽略的零个或多个空格分隔(因此更奇特 记录分隔符分隔 JSON 和 length-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 流式传输保存了多少字节,以便在您尝试使用的服务中使用它?我不知道。