java-解析包含多个嵌套对象的JSON对象,而不为每个嵌套对象创建类

我在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]

上一篇:java-忽略RuntimeTypeAdapterFactory中未注册的子类型


下一篇:JSON对象的Java表示