您似乎有些困惑:针对每个
division##
节点,您不需要一个映射类,因为您可以多次重复使用一个类,而不管属性名称如何。您可能需要零到两个自定义映射类,具体取决于您的喜好方式:
- 如果自己遍历解析后的JSON对象,则不需要自定义映射类;
- 如果应用高级解析技术并将映射与类型适配器或JSON对象结合使用,则需要1个自定义映射类;
- 精确映射需要2个自定义映射类。
下面的示例是使用Java 8语言特性和Java 8 Stream API编写的,但可以轻松地使用Java 6重新编写。下面的
JSON
常量只是一个包含以下JSON文档的
String
:
{
"name": "nestedJSONExample",
"divisions": {
"division1": {"id": "id1", "name": "name1", "alsoKnownAs": ["alsoKnownAs1A"]},
"division2": {"id": "id2", "name": "name2", "alsoKnownAs": ["alsoKnownAs2A"]},
"division3": {"id": "id3", "name": "name3", "alsoKnownAs": ["alsoKnownAs3A"]},
"division4": {"id": "id4", "name": "name4", "alsoKnownAs": ["alsoKnownAs4A"]},
"division5": {"id": "id5", "name": "name5", "alsoKnownAs": ["alsoKnownAs5A"]},
"division6": {"id": "id6", "name": "name6", "alsoKnownAs": ["alsoKnownAs6A"]}
}
}
无映射
JsonElement
是一个内置的Gson类,表示任何JSON元素。通过结合JsonElement
类及其子类元素,Gson可以构建反映给定JSON文档结构的JSON树。因此,只需从根部遍历即可。
final Gson gson = new Gson();
final List<String> ids = gson.fromJson(JSON, JsonElement.class)
.getAsJsonObject()
.get("divisions")
.getAsJsonObject()
.entrySet()
.stream()
.map(Entry::getValue)
.map(JsonElement::getAsJsonObject)
.map(jo -> jo.get("id"))
.map(JsonElement::getAsJsonPrimitive)
.map(JsonPrimitive::getAsString)
.collect(toList());
System.out.println(ids);
精确映射
在这里,您只需要使用两个映射类来描述JSON对象之间的关系。 divisions
节点可以仅是一个包含任意键和Division
值的Map
。
final class OuterWithMap {
Map<String, Division> divisions;
}
final class Division {
String id;
}
final Gson gson = new Gson();
final List<String> ids = gson.fromJson(JSON, OuterWithMap.class)
.divisions
.values()
.stream()
.map(d -> d.id)
.collect(toList());
System.out.println(ids);
不精确的映射
这是最复杂的一个,展示了使用Gson解析JSON和将给定的JSON文档映射到映射类中的高级技术。由于映射可能无法反映真实结构,因此需要在运行时进行转换。
final class OuterWithList {
@JsonAdapter(NoKeysTypeAdapterFactory.class)
List<Division> divisions;
}
final class NoKeysTypeAdapterFactory
implements TypeAdapterFactory {
private NoKeysTypeAdapterFactory() {
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( List.class.isAssignableFrom(typeToken.getRawType()) ) {
final Type elementType = getElementType(typeToken.getType());
@SuppressWarnings("unchecked")
final TypeAdapter<T> typeAdapter = (TypeAdapter<T>) getNoKeysTypeAdapter(gson, elementType);
return typeAdapter;
}
return null;
}
private static Type getElementType(final Type type) {
if ( type instanceof ParameterizedType ) {
final ParameterizedType parameterizedType = (ParameterizedType) type;
return parameterizedType.getActualTypeArguments()[0];
}
return Object.class;
}
}
final class NoKeysTypeAdapter<E>
extends TypeAdapter<List<E>> {
private final Gson gson;
private final Type elementType;
private NoKeysTypeAdapter(final Gson gson, final Type elementType) {
this.gson = gson;
this.elementType = elementType;
}
static <E> TypeAdapter<List<E>> getNoKeysTypeAdapter(final Gson gson, final Type elementType) {
return new NoKeysTypeAdapter<>(gson, elementType);
}
@Override
public void write(final JsonWriter out, final List<E> value) {
throw new UnsupportedOperationException();
}
@Override
public List<E> read(final JsonReader in)
throws IOException {
final List<E> list = new ArrayList<>();
in.beginObject();
while ( in.peek() != END_OBJECT ) {
in.nextName();
final E element = gson.fromJson(in, elementType);
list.add(element);
}
in.endObject();
return list;
}
}
使用特殊的查询库
有一些类似于JsonPath的库可以使查询JSON文档变得更加容易。 JsonPath
可以在没有Gson的情况下工作,但据我所知,它使用另一个JSON解析库,不会自己解析JSON(但实际上我不知道具体情况)。以下是使用的例子:
final JsonPath jsonPath = JsonPath.compile("$.divisions.*.id");
final List<String> ids = jsonPath.<JSONArray>read(JSON)
.stream()
.map(o -> (String) o)
.collect(toList());
System.out.println(ids);
所有以上四个示例的输出都是:
[id1,id2,id3,id4,id5,id6]
JsonElement
,然后遍历它。 - Lyubomyr Shaydariv