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

时间:2016-04-14 04:48:50

标签: java generics jackson gson

我的服务器响应如下:

{
  "timestamp" : 1,
  "some other data": "blah",
  "result" : {
     ...
  }
}

进行各种通话。我想做客户端的是:

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

}

然后用GSON或Jackson反序列化。由于类型擦除,我无法这样做。我欺骗了使用像这样的子类:

class SpecificResponse extends ServerRequest<SpecificType> {}

但这需要一堆无用的课程。谁有更好的方法?

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

2 个答案:

答案 0 :(得分:1)

在这种情况下,类型擦除的典型解决方案是type token hack,它利用匿名类来维护超类信息以用于反射。

杰克逊提供TypeReference类型以及ObjectMapper#readValue重载来使用它。

在您的示例中,您将使用

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

请注意,这不是类型安全的,因此您必须注意,您尝试分配的类型与您在匿名类实例创建表达式中使用的泛型类型参数兼容。

至于在JSON中支持单个值和数组,您可以将字段更改为某种Collection类型。例如,

List<T> results

然后,启用DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY

  

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

答案 1 :(得分:0)

我支持@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;
    }
}

用例与杰克逊非常相似:

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

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