如何使用Java Gson库转换动态JSON响应

我有一个可以返回JSON数组或对象的API。 示例JSON对象

{ "id": 1, "name": "name" } 

JSON数组:

 [ { "id": 1, "name": "name" }, { "id": 1, "name": "name" } ] 

将JSON对象响应映射到POJO时,我使用:

 MyEntity myEntity = new Gson().fromJson(jsonString, MyEntity.class); 

将JSON数组响应映射到POJO数组时,我使用:

 MyEntity[] myEntity = new GSON().fromJson(jsonString, MyEntity[].class); 

如何动态地将这两个响应转换为适当的类型?

注意:我无法修改服务器响应,这是一个公共API。

谢谢!

编辑:

我试图实现一个自动执行此操作的方法,但我遗漏了一些东西。 方法

 public  T convertResponseToEntity(Class classOfT) { JsonElement jsonElement = this.gson.fromJson(getResponseAsString(), JsonElement.class); if (jsonElement.isJsonArray()) { Type listType = new TypeToken(){}.getType(); return this.gson.fromJson(getResponseAsString(), listType); } return this.gson.fromJson(getResponseAsString(), (Type) classOfT); } 

它返回一个LinkedTreeMap列表。 如何修改代码以返回与Object[]相同的内容?

如何将这两个响应动态转换为适当的类型?

这取决于如何在这里解释“适当的类型”,因为一旦您尝试在每次需要时处理解析后的JSON对象,它将导致instanceof或visitor模式获得适当的类型。 如果您无法更改API,则可以平滑使用它的方式。 这里可能的选择之一是处理这样的响应,好像一切都是列表。 即使单个对象也可以作为仅包含一个元素的列表来处理(许多库只使用序列/列表来实现这一点:Java中的流API,.NET中的LINQ,JavaScript中的jQuery等)。

假设您有以下MyEntity类来处理从您需要的API获取的元素:

 // For the testing purposes, package-visible final fields are perfect // Gson can deal with final fields too final class MyEntity { final int id = Integer.valueOf(0); // not letting javac to inline 0 since it's primitive final String name = null; @Override public String toString() { return id + "=>" + name; } } 

接下来,让我们创建一个类型适配器,它始终将“true”列表和单个对象对齐,就好像它是一个列表:

 final class AlwaysListTypeAdapter extends TypeAdapter> { private final TypeAdapter elementTypeAdapter; private AlwaysListTypeAdapter(final TypeAdapter elementTypeAdapter) { this.elementTypeAdapter = elementTypeAdapter; } static  TypeAdapter> getAlwaysListTypeAdapter(final TypeAdapter elementTypeAdapter) { return new AlwaysListTypeAdapter<>(elementTypeAdapter); } @Override @SuppressWarnings("resource") public void write(final JsonWriter out, final List list) throws IOException { if ( list == null ) { out.nullValue(); } else { switch ( list.size() ) { case 0: out.beginArray(); out.endArray(); break; case 1: elementTypeAdapter.write(out, list.iterator().next()); break; default: out.beginArray(); for ( final T element : list ) { elementTypeAdapter.write(out, element); } out.endArray(); break; } } } @Override public List read(final JsonReader in) throws IOException { final JsonToken token = in.peek(); switch ( token ) { case BEGIN_ARRAY: final List list = new ArrayList<>(); in.beginArray(); while ( in.peek() != END_ARRAY ) { list.add(elementTypeAdapter.read(in)); } in.endArray(); return unmodifiableList(list); case BEGIN_OBJECT: return singletonList(elementTypeAdapter.read(in)); case NULL: return null; case END_ARRAY: case END_OBJECT: case NAME: case STRING: case NUMBER: case BOOLEAN: case END_DOCUMENT: throw new MalformedJsonException("Unexpected token: " + token); default: // A guard case: what if Gson would add another token someday? throw new AssertionError("Must never happen: " + token); } } } 

Gson TypeAdapter旨在以流媒体方式工作,因此从效率角度来看它们很便宜,但实现起来却不那么容易。 上面的write()方法只是为了不throw new UnsupportedOperationException(); 那里(我假设你只读过那个API,但不知道那个API是否会消耗“元素或列表”修改请求)。 现在有必要创建一个类型适配器工厂,让Gson为每种特定类型选择正确的类型适配器:

 final class AlwaysListTypeAdapterFactory implements TypeAdapterFactory { private static final TypeAdapterFactory alwaysListTypeAdapterFactory = new AlwaysListTypeAdapterFactory(); private AlwaysListTypeAdapterFactory() { } static TypeAdapterFactory getAlwaysListTypeAdapterFactory() { return alwaysListTypeAdapterFactory; } @Override public  TypeAdapter create(final Gson gson, final TypeToken typeToken) throws IllegalArgumentException { if ( List.class.isAssignableFrom(typeToken.getRawType()) ) { final Type elementType = getElementType(typeToken); // Class instances can be compared with == final TypeAdapter elementTypeAdapter = elementType == MyEntity.class ? gson.getAdapter(MyEntity.class) : null; // Found supported element type adapter? if ( elementTypeAdapter != null ) { @SuppressWarnings("unchecked") final TypeAdapter castTypeAdapter = (TypeAdapter) getAlwaysListTypeAdapter(elementTypeAdapter); return castTypeAdapter; } } // Not a type that can be handled? Let Gson pick a more appropriate one itself return null; } // Attempt to detect the list element type private static Type getElementType(final TypeToken typeToken) { final Type listType = typeToken.getType(); return listType instanceof ParameterizedType ? ((ParameterizedType) listType).getActualTypeArguments()[0] : Object.class; } } 

毕竟它是如何使用的:

 private static final Type responseItemListType = new TypeToken>() { }.getType(); private static final Gson gson = new GsonBuilder() .registerTypeAdapterFactory(getAlwaysListTypeAdapterFactory()) .create(); public static void main(final String... args) { test(""); test("{\"id\":1,\"name\":\"name\"}"); test("[{\"id\":1,\"name\":\"name\"},{\"id\":1,\"name\":\"name\"}]"); test("[]"); } private static void test(final String incomingJson) { final List list = gson.fromJson(incomingJson, responseItemListType); System.out.print("LIST="); System.out.println(list); System.out.print("JSON="); gson.toJson(list, responseItemListType, System.out); // no need to create an intermediate string, let it just stream System.out.println(); System.out.println("-----------------------------------"); } 

输出:

 LIST=null JSON=null ----------------------------------- LIST=[1=>name] JSON={"id":1,"name":"name"} ----------------------------------- LIST=[1=>name, 1=>name] JSON=[{"id":1,"name":"name"},{"id":1,"name":"name"}] ----------------------------------- LIST=[] JSON=[] ----------------------------------- 

只需将其解析为JsonElement并检查实际的元素类型:

 Gson g = new Gson(); JsonParser parser = new JsonParser(); JsonElement e = parser.parse( new StringReader(jsonString) ); if(e instanceof JsonObject) { MyEntity myEntity = g.fromJson(e, MyEntity.class); } else { MyEntity[] myEntity = g.fromJson(e, MyEntity[].class); }