Java 使用GSON或Jackson将JSON反序列化为具有泛型参数的类

Java 使用GSON或Jackson将JSON反序列化为具有泛型参数的类,java,generics,jackson,gson,Java,Generics,Jackson,Gson,我从服务器收到的响应如下所示: { "timestamp" : 1, "some other data": "blah", "result" : { ... } } 各种各样的电话。我想在客户端做的是: class ServerResponse<T> { long timestamp; T result; } class服务器响应{ 长时间戳; T结果; } 然后与GSON或Jackson进行反序列化。由于类型擦除,我无法这样做。我用

我从服务器收到的响应如下所示:

{
  "timestamp" : 1,
  "some other data": "blah",
  "result" : {
     ...
  }
}
各种各样的电话。我想在客户端做的是:

class ServerResponse<T> {
    long timestamp;
    T result;

}
class服务器响应{
长时间戳;
T结果;
}
然后与GSON或Jackson进行反序列化。由于类型擦除,我无法这样做。我用这样的子类欺骗了它:

class SpecificResponse extends ServerRequest<SpecificType> {}
class SpecificResponse扩展了ServerRequest{}
但这需要一堆无用的类。谁有更好的办法


我还需要能够处理结果是数组的情况。

在这种情况下,典型的类型擦除解决方案是使用匿名类来维护超类信息,以便与反射一起使用

Jackson提供了类型以及使用它的重载

在您的示例中,您将使用

ServerResponse response = objectMapper.readValue(theJsonSource, new TypeReference<ServerResponse<SpecificType>>() {});
然后,启用

确定强制非数组是否可接受的功能 (在JSON中)用于Java集合的值(数组、,
java.util.Collection
)类型。如果启用,集合反序列化程序将 尝试处理非数组值,就好像它们周围有“隐式”值一样 JSON数组


我支持使用
@Pillar
解决方案
Jackson
,因为它非常严格。2行代码

下面是Gson版本,其工作方式相同,但您需要自定义反序列化程序和一些反射来实现这一点

public static class CustomDeserializer implements JsonDeserializer<ServerResponse> {
    @Override
    public ServerResponse deserialize(JsonElement je, Type respT,
                          JsonDeserializationContext jdc) throws JsonParseException {

        Type t = (respT instanceof ParameterizedType) ?
                ((ParameterizedType) respT).getActualTypeArguments()[0] :
                Object.class;

        JsonObject jObject = (JsonObject) je;

        ServerResponse serverResponse = new ServerResponse();

        //can add validation and null value check here
        serverResponse.timestamp = jObject.get("timestamp").getAsLong();

        JsonElement dataElement = jObject.get("result");

        if (dataElement != null) {
            if (dataElement.isJsonArray()) {
                JsonArray array = dataElement.getAsJsonArray();

                // can use ((ParameterizedType) respT).getActualTypeArguments()
                // instead of new Type[]{t} 
                // if you 100% sure that you will always provide type
                Type listT = ParameterizedTypeImpl.make(List.class, new Type[]{t}, null);

                serverResponse.result  = jdc.deserialize(array, listT);
            } else if(dataElement.isJsonObject()) {
                serverResponse.result = new ArrayList();
                serverResponse.result.add(jdc.deserialize(dataElement, t));
            }
        }
        return serverResponse;
    }
}
公共静态类CustomDeserializer实现JsonDeserializer{
@凌驾
公共服务器响应反序列化(JsonElement je,类型respT,
JsonDeserializationContext(jdc)引发JsonParseException{
类型t=(参数化类型的响应实例)?
((ParameterizedType)respT).getActualTypeArguments()[0]:
对象类;
JsonObject jObject=(JsonObject)je;
ServerResponse ServerResponse=newserverresponse();
//可以在此处添加验证和空值检查
serverResponse.timestamp=jObject.get(“timestamp”).getAsLong();
JsonElement dataElement=jObject.get(“结果”);
if(数据元素!=null){
if(dataElement.isJsonArray()){
JsonArray数组=dataElement.getAsJsonArray();
//可以使用((ParameterizedType)respT.getActualTypeArguments()
//而不是新类型[]{t}
//如果您100%确定您将始终提供类型
类型listT=ParameteredTypeImpl.make(List.class,新类型[]{t},null);
serverResponse.result=jdc.deserialize(数组,listT);
}else if(dataElement.isJsonObject()){
serverResponse.result=new ArrayList();
serverResponse.result.add(jdc.deserialize(dataElement,t));
}
}
返回服务器响应;
}
}
用例与Jackson非常相似:

Gson gson = new GsonBuilder()
           .registerTypeAdapter(ServerResponse.class, new CustomDeserializer())
           .create();

ServerResponse<MyObject> s = gson.fromJson(json, new TypeToken<ServerResponse<MyObject>>(){}.getType())
Gson Gson=new GsonBuilder()
.registerTypeAdapter(ServerResponse.class,新的CustomDeserializer())
.create();
ServerResponse s=gson.fromJson(json,new-TypeToken(){}.getType())

TypeToken
在Gson中,在Jackson中
TypeReference
。如果要将数组包装在
ServerResponse
中,则数组将更为困难。某些框架允许您在客户端请求中指定类类型,从而具体化类型参数并使请求返回具体类型。寻找带有类参数的请求api的变体。@如果更简单的话,我很高兴有一个二级ServerArrayRequest,其中变量是List result。我不介意两个,我不希望有几十个。Jackson有一个功能,可以让你这样做,但是你的Java代码必须有一个
列表。不幸的是,它不适用于数组情况,无论是t结果保存列表还是列表结果。不管是哪种情况,它都会给我一个结果中每个元素的列表。@Gabeschen对我来说很好,我认为我的问题仍然是擦除。我试图在泛型类本身中使用它——一个CustomVolleyRequest,其中Type是反序列化类型。因此,随着擦除,它再次成为一个对象。如果我能像上一节课一样通过考试。。。。我想我能做到,行得通。好的,所以每个请求调用1个boiler plate参数,而不是每个类型1个boiler place类。我认为这是一场胜利。大规模重构的时间到了
Gson gson = new GsonBuilder()
           .registerTypeAdapter(ServerResponse.class, new CustomDeserializer())
           .create();

ServerResponse<MyObject> s = gson.fromJson(json, new TypeToken<ServerResponse<MyObject>>(){}.getType())