java - 使用 java/GSON 解析大型 JSON,无法读取 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();
}
我映射的类是这样的:
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。有什么线索吗?谢谢
解决方案
默认情况下,JSON 仅声明一个最高值(是的,这将是一个有效的 JSON 文档),但有JSON 流使用任意技术将多个 JSON 元素连接到单个流中,假设流使用者可以解析它(阅读更多)。Gson 支持所谓的 lenient 模式,该模式关闭“仅一个最高值”模式(并做一些与问题无关的更多事情)JsonReader
:. setLenient
打开 lenient 模式后,您可以一个一个地读取 JSON 元素,事实证明,此模式可用于解析/读取行分隔的 JSON和连接的 JSON 值因为它们只是由 Gson 忽略的零个或多个空格分隔(因此不支持更多奇特的记录分隔符分隔的 JSON和长度前缀的 JSON)。它对您不起作用的原因是您的初始代码假定流包含单个 JSON 数组(显然不是:它应该是不符合 JSON 数组语法的元素流)。
一个简单的通用 JSON 流支持可能看起来像这样(使用 Stream API 来获得比现有更丰富的 API Iterator
,但展示一个想法很好,您可以轻松地将其调整为迭代器、回调、可观察流,无论您喜欢什么):
@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-generated toString()
):
艺术家(id=d0ab06e1-751a-414b-a976-da72670391b1,name=Arcing Wires,sortName=Arcing Wires)
艺术家(id=6f0c2c16-dd7e-4268-a484-bc7b2ac78108,name=Another,sortName=Another)
艺术家(id= e062b6cd-5506-47b0-afdb-72f4279ec38c,名称=Agent S,sortName=Agent S)
这种方法(JSON 流)保存了多少字节,以便在您尝试使用的服务中使用它?我不知道。
推荐阅读
- java - 字符串到密钥的转换/反之亦然
- java - 使用 openssl 在 c++ 中加密文件,使用 AES/CBC/PKCS5Padding 在 java 中解密
- python-3.x - 循环和列表的多维输入问题
- python - Python中Paramiko上的NoValidConnectionError
- flutter - 在颤动中如何在单击按钮时调用容器中的方法?
- swift - 如何将 NSAttributedString 发送到服务器并返回相同的属性
- php - mysqli_real_escape_string runs the script
- sql - 如何用case语句优化左外连接?
- identityserver4 - 访问 API 时,收到 500 Internal Server Error。从 Identity Server4 登录表单成功登录并重定向到 Angular 前端后
- matlab - 以像素为中心的小邻域