繁体   English   中英

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

[英]How to convert dynamic JSON reponse with Java Gson library

我有一个可以返回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> T convertResponseToEntity(Class<T> classOfT)
{
    JsonElement jsonElement =  this.gson.fromJson(getResponseAsString(), JsonElement.class);

    if (jsonElement.isJsonArray()) {
       Type listType = new TypeToken<T>(){}.getType();
       return this.gson.fromJson(getResponseAsString(), listType);
    }

    return this.gson.fromJson(getResponseAsString(), (Type) classOfT);
}

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

只需将其解析为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);
}

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

这取决于如何在此处解释“适当的类型”,因为一旦您每次需要处理从JSON解析的对象时,它将导致instanceof或访客模式获得适当的类型。 如果您无法更改API,则可以简化其使用方式。 这里可能的选项之一是像对待所有内容一样处理此类响应。 甚至单个对象也可以仅作为一个元素处理为列表(许多库仅使用具有以下事实的序列/列表进行处理:Java中的Stream 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<T>
        extends TypeAdapter<List<T>> {

    private final TypeAdapter<T> elementTypeAdapter;

    private AlwaysListTypeAdapter(final TypeAdapter<T> elementTypeAdapter) {
        this.elementTypeAdapter = elementTypeAdapter;
    }

    static <T> TypeAdapter<List<T>> getAlwaysListTypeAdapter(final TypeAdapter<T> elementTypeAdapter) {
        return new AlwaysListTypeAdapter<>(elementTypeAdapter);
    }

    @Override
    @SuppressWarnings("resource")
    public void write(final JsonWriter out, final List<T> 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<T> read(final JsonReader in)
            throws IOException {
        final JsonToken token = in.peek();
        switch ( token ) {
        case BEGIN_ARRAY:
            final List<T> 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 <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken)
            throws IllegalArgumentException {
        if ( List.class.isAssignableFrom(typeToken.getRawType()) ) {
            final Type elementType = getElementType(typeToken);
            // Class<T> 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<T> castTypeAdapter = (TypeAdapter<T>) 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<List<MyEntity>>() {
}.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<MyEntity> 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=[]
-----------------------------------

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM