我在Android中使用GSON解析JSON对象,该对象的一部分包含多个嵌套对象,这些对象包含所有相同的字段.例如,JSON结构看起来与此类似:
{
"name": "nestedJSONExample",
"divisions": {
"division1": {
"id": string
"name": string,
"alsoKnownAs": [
string
],
}
"division2": {
"id": string
"name": string,
"alsoKnownAs": [
string
],
}
...
"division99" {
"id": string
"name": string,
"alsoKnownAs": [
string
],
}
}
}
在此示例中,所有“ division ##”嵌套对象都包含所有相同的字段,是否可以在不为每个“ division ##”对象创建模型类的情况下将此JSON解析为Java类?
即我可以创建如下的Java结构吗?
divisions.division##.id
不必为每个部门划分班级?
解决方法:
您似乎有些困惑:您不需要为每个Division ##节点使用一个映射类,因为无论属性名称如何,您都可以多次重用一个类.关于您喜欢的方式,您可能需要从零到两个自定义映射类:
> 0个自定义映射类(如果自行遍历已解析的JSON对象);
> 1个自定义映射类,如果应用高级解析技术并将映射与类型适配器或JSON对象组合在一起;
> 2个自定义映射类,用于精确映射.
下面的示例使用Java 8语言功能和Java 8 Stream API编写,但是可以轻松地用Java 6重写.下面的JSON常量只是带有以下JSON文档的字符串:
{
"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") // get the divisions property
.getAsJsonObject()
.entrySet() // and traverse its key/value pairs
.stream()
.map(Entry::getValue) // discarding the keys
.map(JsonElement::getAsJsonObject)
.map(jo -> jo.get("id")) // take the id property from the every `division` object
.map(JsonElement::getAsJsonPrimitive)
.map(JsonPrimitive::getAsString)
.collect(toList());
System.out.println(ids);
精确映射
在这里,您可能只需要两个映射类来描述JSON对象之间的关系.除法节点可以只是一个包含任意键和除法值的Map.
final class OuterWithMap {
//String name;
Map<String, Division> divisions;
}
final class Division {
String id;
//String name;
//List<String> alsoKnownAs;
}
final Gson gson = new Gson();
final List<String> ids = gson.fromJson(JSON, OuterWithMap.class)
.divisions
.values() // use map values only ignoring the keys
.stream()
.map(d -> d.id)
.collect(toList());
System.out.println(ids);
不完全对应
这是最复杂的方法,它显示了使用Gson解析JSON并将给定JSON文档映射到映射类的高级技术可能无法反映真实结构,因此可以即时进行转换.
final class OuterWithList {
//String name;
@JsonAdapter(NoKeysTypeAdapterFactory.class)
List<Division> divisions;
}
final class NoKeysTypeAdapterFactory
implements TypeAdapterFactory {
// No accessible constructor needed - Gson can instantiate it itself
private NoKeysTypeAdapterFactory() {
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
// Is it a list?
if ( List.class.isAssignableFrom(typeToken.getRawType()) ) {
// Try to determine the list element type
final Type elementType = getElementType(typeToken.getType());
// And create a custom type adapter instance bound to the specific list type
@SuppressWarnings("unchecked")
final TypeAdapter<T> typeAdapter = (TypeAdapter<T>) getNoKeysTypeAdapter(gson, elementType);
return typeAdapter;
}
// Otherwise just tell Gson try to find another appropriate parser
return null;
}
private static Type getElementType(final Type type) {
// Is it a generic type with type parameters?
if ( type instanceof ParameterizedType ) {
final ParameterizedType parameterizedType = (ParameterizedType) type;
// If yes, then just take the first type argument since java.util.List can only one type
return parameterizedType.getActualTypeArguments()[0];
}
// Otherwise java.lang.Object due to either Java generics type erasure or raw types usage
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<>();
// Make sure that the next JSON stream token is `{`
in.beginObject();
// Read until the object ends
while ( in.peek() != END_OBJECT ) {
// Ignore the found JSON object property name
in.nextName();
// And delegate the property value parsing to a downstream parser
final E element = gson.fromJson(in, elementType);
list.add(element);
}
// Make sure that the JSON stream is finished with the `}` token
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]