我在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
],
}
}
}
在这个例子中,所有的"划分##"嵌套对象包含所有相同的字段,是否有办法将此JSON解析为Java类,而无需为每个" division ##"创建模型类。宾语?
即。我可以创建一个Java结构,如:
divisions.division##.id
无需为每个部门制作课程?
答案 0 :(得分:3)
您似乎有点困惑:您不需要每个division##
节点的映射类,因为您可以多次重用一个类,而不管属性名称如何。您可能需要从零到两个自定义映射类关于您喜欢的方式:
以下示例使用Java 8语言功能和Java 8 Stream API编写,但可以轻松地使用Java 6重写。下面的JSON
常量只是String
,带有以下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对象之间的关系。 divisions
节点只能Map
保存任意键和Division
值。
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]
答案 1 :(得分:1)