问题描述
我有一个包含很多对象的 JSON 文件,不是一个列表,而是一个如下:
{
"attr1": "value1","attr2": "value2","attr3": "value3",}
{
"attr1": "value1","attr3": "value3"
}
{
"attr1": "value1",}
...
如何获取这些对象的列表(包含 A
、attr1
、attr2
的 attr3
类)?
解决方法
Gson 支持所谓的“宽松”模式,该模式支持格式错误的 JSON 文档(包括奇怪的 JSON 令牌和“多合一”JSON 文档,就像您在问题中提到的那样。)这也使 Gson 能够检测 JSON用于区分多个值的文档结束标记。
一般来说,常见的算法如下:
jsonReader.setLenient(true);
final Collection<AttributeBag> actualBags = new ArrayList<>();
while ( jsonReader.peek() != JsonToken.END_DOCUMENT ) {
actualBags.add(gson.fromJson(jsonReader,elementType));
}
这是一种临时方法,您可以使用不同的幕后机制使其更通用:
读取列表
- 渴望,拉语义
- 仅支持列表,可能会占用整个内存
public static <T> List<T> readList(final Gson gson,final JsonReader jsonReader,final TypeToken<? extends T> typeToken)
throws IOException {
final boolean isLenient = jsonReader.isLenient();
try {
jsonReader.setLenient(true);
final Type type = typeToken.getType();
final List<T> list = new ArrayList<>();
while ( jsonReader.peek() != JsonToken.END_DOCUMENT ) {
list.add(gson.fromJson(jsonReader,type));
}
return list;
} finally {
jsonReader.setLenient(isLenient);
}
}
使用消费者阅读
- 渴望,推送语义
- 支持任何类型的消费者,不一定要添加到集合中
public static <T> void readPushing(final Gson gson,final TypeToken<? extends T> typeToken,final Consumer<? super T> consumer)
throws IOException {
final boolean isLenient = jsonReader.isLenient();
try {
jsonReader.setLenient(true);
final Type type = typeToken.getType();
while ( jsonReader.peek() != JsonToken.END_DOCUMENT ) {
consumer.accept(gson.<T>fromJson(jsonReader,type));
}
} finally {
jsonReader.setLenient(isLenient);
}
}
作为迭代器阅读
- 懒惰,拉语义
- 迭代器因懒惰而美丽
public static <T> Iterator<T> readAsIterator(final Gson gson,final TypeToken<? extends T> typeToken) {
final Type type = typeToken.getType();
return new Iterator<T>() {
@Override
public boolean hasNext() {
final boolean isLenient = jsonReader.isLenient();
try {
jsonReader.setLenient(true);
return jsonReader.peek() != JsonToken.END_DOCUMENT;
} catch ( final IOException ex ) {
throw new RuntimeException(ex);
} finally {
jsonReader.setLenient(isLenient);
}
}
@Override
public T next() {
final boolean isLenient = jsonReader.isLenient();
try {
jsonReader.setLenient(true);
return gson.fromJson(jsonReader,type);
} finally {
jsonReader.setLenient(isLenient);
}
}
};
}
以流形式阅读
- 懒惰,(推/)拉语义
- 懒惰是一种美德,Stream API 支持,一旦关闭流就关闭支持的资源
public static <T> Stream<T> readAsStream(final Gson gson,final TypeToken<? extends T> typeToken) {
final Type type = typeToken.getType();
final Spliterator<T> spliterator = new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE,0) {
@Override
public boolean tryAdvance(final Consumer<? super T> action) {
final boolean isLenient = jsonReader.isLenient();
try {
jsonReader.setLenient(true);
final JsonToken token = jsonReader.peek();
if ( token == JsonToken.END_DOCUMENT ) {
return false;
}
action.accept(gson.<T>fromJson(jsonReader,type));
return true;
} catch ( final IOException ex ) {
throw new RuntimeException(ex);
} finally {
jsonReader.setLenient(isLenient);
}
}
};
return StreamSupport.stream(spliterator,false)
.onClose(() -> {
try {
jsonReader.close();
} catch ( final IOException ex ) {
throw new RuntimeException(ex);
}
});
}
,
执行此操作的库是 Gson
。你可以这样使用它:
gson = Gson();
MyObject myObject = gson.fromJson(jsonString,MyObject.class);
您可以使用 Readers
输入数据或其他类型的对象。
您的 JSON 文件包含数据的第一件事没有结构,因此无法直接转换为 Java 对象。您需要修改 JSON 字符串以使其成为列表。然后您可以使用以下内容来检索对象列表。
List jsonList = new ObjectMapper().readValue(new File(filepath),List.class);
之后,您可以使用 instanceOf 将 Object 转换为它们各自的类。
jsonList.foreach(jsonObject -> {
if(jsonObject instanceOf MyClass1){
// cast it or perform some op
}
});