Jackson解码泛型类型

vaj7vani  于 2022-11-09  发布在  其他
关注(0)|答案(1)|浏览(95)

我正在构建一个客户端/服务器应用程序,并将请求和响应消息封装在如下所示的通用Java类中:

@SuperBuilder
@Jacksonized
@Getter
@Setter
public class AppResponse<T extends AppResponse.Results> {
    public static final String SUCCESS_ATTRIBUTE = "success";
    public static final String MESSAGE_ATTRIBUTE = "message";
    public static final String REQUEST_ID_ATTRIBUTE = AppRequest.REQUEST_ID_ATTRIBUTE;
    public static final String RESULTS_ATTRIBUTE = "results";

    @JsonProperty(SUCCESS_ATTRIBUTE)
    boolean success;

    @JsonProperty(MESSAGE_ATTRIBUTE)
    String message;

    @JsonProperty(REQUEST_ID_ATTRIBUTE)
    String requestID;

    @JsonProperty(RESULTS_ATTRIBUTE)
    T results;

    @SuperBuilder
    @Jacksonized
    @Getter
    @Setter
    public static class Results {

    }
}

然后我有一个扩展AppResponse的类。每个命令响应包的结果可以通过网络发送。例如:

@SuperBuilder
@Getter
@Setter
@Jacksonized
public class AppCreateForumPostResults extends AppResponse.Results {
    @JsonProperty("id")
    UUID id;
}

因此,当服务器使用Jackson将其编码为JSON时,我会得到一个如下所示的JSON文档:

{
  "success" : true,
  "requestID" : "eL55P4Sz",
  "results" : {
    "id" : "0b48d389-2407-43c4-ad1b-ab52599c2d7f"
  }
}

当客户端应用程序收到此消息时,它首先将其解码为ObjectNode:

objectNode = (ObjectNode) AppObjectMapper.OBJECT_MAPPER.readTree(message);

然后将其解码为泛型对象,因为它还不知道泛型的具体类型是什么:

JavaType genericType = AppObjectMapper.OBJECT_MAPPER.getTypeFactory().constructParametricType(
    AppResponse.class,
    AppResponse.Results.class
);

AppResponse<AppResponse.Results> genericResponse = AppObjectMapper.OBJECT_MAPPER.treeToValue(
    objectNode,
    genericType
);

然后,它查询已发送的requestID的HashMap,以找出它应该期望的结果类类型。

private final Map<String, CallbackEntry<? extends AppResponse.Results>> callbacks = new HashMap<>();

而CallbackEntry定义为:

@Builder
@Value
public static class CallbackEntry<T extends AppResponse.Results> {
    Callback<T> method;
    Class<T> resultsClass;
}

最后,Callback定义为:

public interface Callback<T extends AppResponse.Results> {
    void handleResponse(AppResponse<T> results);
}

因此,一旦我们在HashMap中找到了requestID,我们就知道结果包含什么具体类,并且可以对其进行解码:

CallbackEntry<? extends AppResponse.Results> callbackEntry = callbacks.get(genericResponse.getRequestID());

callbacks.remove(genericResponse.getRequestID());

JavaType concreteType = AppObjectMapper.OBJECT_MAPPER.getTypeFactory().constructParametricType(
    AppResponse.class,
    callbackEntry.resultsClass
);

AppResponse<? extends AppResponse.Results> concreteResponse = AppObjectMapper.OBJECT_MAPPER.treeToValue(
    objectNode,
    concreteType
);

到目前为止,一切都很好。这一切都和预期的一样。如果我打印出concreteResponse对象,它里面的数据完全正确。但是,当我去实际调用回调方法时,我得到了一个错误。调用如下所示:

callbackEntry.method.handleResponse(concreteResponse);

我在这行代码中得到的错误是:

[127,49] incompatible types: com.whatever.app.common.AppResponse<capture#1 of ? extends com.whatever.app.common.AppResponse.Results> cannot be converted to com.whatever.app.common.AppResponse<capture#2 of ? extends com.whatever.app.common.AppResponse.Results>

我对这个错误信息感到有点困惑。我猜它一定与处理泛型的方式有关,但我就是想不出正确的咒语是什么。我在谷歌上搜索了很多次,到处都发现了类似的错误,但它们通常是由于有人使用了?而不是T,(我认为)这里不是这样。
那么,我错过了什么?
任何帮助都将不胜感激!

cunj1qz1

cunj1qz11#

我可以通过如下转换函数参数来实现:

callbackEntry.method.handleResponse(
    (concreteResponse.getClass()).cast(concreteResponse)
);

这会显示未检查工作分派的警告,但它可以运作。

相关问题