gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 06:49:44 25 4
gpt4 key购买 nike

我有一个可以返回 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);

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

注意:我无法修改服务器响应,这是一个公共(public) 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[] 相同的内容?

最佳答案

How can I convert those 2 responses dynamically to the appropriate type?

这取决于如何在这里解释“适当的类型”,因为一旦您每次都尝试处理从 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;
}

}

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

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=[]
-----------------------------------

关于java - 如何使用 Java Gson 库转换动态 JSON 响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42314056/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com