gpt4 book ai didi

java - 使用 Java 11 HttpClient 和带有 Jackson 的自定义 BodyHandler 反序列化 JSON 停止并且不会继续

转载 作者:行者123 更新时间:2023-12-01 13:10:21 25 4
gpt4 key购买 nike

我在使用 Java 11 HttpClient::send 直接将 JSON 反序列化为自定义对象时遇到问题定制 HttpResponse.BodyHandler .我在回答 this SO question 时遇到了这个问题.

我使用的版本:

  • OpenJDK 11
  • jackson 2.9.9.3

  • 我创建了一个简单的通用 JsonBodyHandler实现 HttpResponse.BodyHandler 的类:
    public class JsonBodyHandler<W> implements HttpResponse.BodyHandler<W> {

    private final Class<W> wClass;

    public JsonBodyHandler(Class<W> wClass) {
    this.wClass = wClass;
    }

    @Override
    public HttpResponse.BodySubscriber<W> apply(HttpResponse.ResponseInfo responseInfo) {
    return asJSON(wClass);
    }

    }
    asJSON方法定义为:
    public static <W> HttpResponse.BodySubscriber<W> asJSON(Class<W> targetType) {
    HttpResponse.BodySubscriber<String> upstream = HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8);

    return HttpResponse.BodySubscribers.mapping(
    upstream,
    (String body) -> {
    try {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readValue(body, targetType);
    } catch (IOException e) {
    throw new UncheckedIOException(e);
    }
    });
    }

    所以它返回一个自定义 HttpResponse.BodySubscriber获取主体为 String然后应用从 JSON 到给定 targetType 的映射
    测试它的代码:
    public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {

    HttpRequest request = HttpRequest.newBuilder(new URI("https://jsonplaceholder.typicode.com/todos/1"))
    .header("Accept", "application/json")
    .build();

    Model model = HttpClient.newHttpClient()
    .send(request, new JsonBodyHandler<>(Model.class))
    .body();

    System.out.println(model);

    }

    Model类(class) :
    public class Model {
    private String userId;
    private String id;
    private String title;
    private boolean completed;


    //getters setters constructors toString
    }

    输出符合预期:
    Model{userId='1', id='1', title='delectus aut autem', completed=false}

    但是,当我更改 asJSON 时阅读方法 InputStream而不是 String第一的 :
    public static <W> HttpResponse.BodySubscriber<W> asJSON(Class<W> targetType) {
    HttpResponse.BodySubscriber<InputStream> upstream = HttpResponse.BodySubscribers.ofInputStream();

    return HttpResponse.BodySubscribers.mapping(
    upstream,
    (InputStream is) -> {
    try (InputStream stream = is) {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readValue(stream, targetType);
    } catch (IOException e) {
    throw new UncheckedIOException(e);
    }
    });
    }

    使用 ObjectMapper 调用读取值后挂起并且它没有继续(我已经检查它是否成功地从端点获得了响应,状态代码是 200)但是它只是挂起。有谁知道可能是什么问题?

    最佳答案

    我刚刚找到 this SO question有同样的问题,但 GZIPInputStream .原来HttpResponse.BodySubscribers.mapping有问题,无法按文档工作。这是link到官方 OpenJDK 错误站点。它已针对 OpenJDK 13 修复。因此,一种解决方法是使用 HttpResponse.BodySubscribers::ofString而不是 HttpResponse.BodySubscribers::ofInputStream作为 HttpResponse.BodySubscribers::mapping 的上游- 在我的问题中显示了如何做到这一点。

    或者,正如@daniel 在评论中指出的那样,更好的解决方案是返回 Supplier而不是模型类:

    public static <W> HttpResponse.BodySubscriber<Supplier<W>> asJSON(Class<W> targetType) {
    HttpResponse.BodySubscriber<InputStream> upstream = HttpResponse.BodySubscribers.ofInputStream();

    return HttpResponse.BodySubscribers.mapping(
    upstream,
    inputStream -> toSupplierOfType(inputStream, targetType));
    }

    public static <W> Supplier<W> toSupplierOfType(InputStream inputStream, Class<W> targetType) {
    return () -> {
    try (InputStream stream = inputStream) {
    ObjectMapper objectMapper = new ObjectMapper();
    return objectMapper.readValue(stream, targetType);
    } catch (IOException e) {
    throw new UncheckedIOException(e);
    }
    };
    }
    JsonBodyHandler也使用 Supplier :
    public class JsonBodyHandler<W> implements HttpResponse.BodyHandler<Supplier<W>> {

    private final Class<W> wClass;

    public JsonBodyHandler(Class<W> wClass) {
    this.wClass = wClass;
    }

    @Override
    public HttpResponse.BodySubscriber<Supplier<W>> apply(HttpResponse.ResponseInfo responseInfo) {
    return asJSON(wClass);
    }

    }

    然后我们可以这样称呼它:
    public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {

    HttpRequest request = HttpRequest.newBuilder(new URI("https://jsonplaceholder.typicode.com/todos/1"))
    .header("Accept", "application/json")
    .build();

    Model model = HttpClient.newHttpClient()
    .send(request, new JsonBodyHandler<>(Model.class))
    .body()
    .get();

    System.out.println(model);

    }

    这甚至是在 OpenJDK 13 docs 中描述的推广方式。 ):

    The mapping function is executed using the client's executor, and can therefore be used to map any response body type, including blocking InputStream. However, performing any blocking operation in the mapper function runs the risk of blocking the executor's thread for an unknown amount of time (at least until the blocking operation finishes), which may end up starving the executor of available threads. Therefore, in the case where mapping to the desired type might block (e.g. by reading on the InputStream), then mapping to a Supplier of the desired type and deferring the blocking operation until Supplier::get is invoked by the caller's thread should be preferred.

    关于java - 使用 Java 11 HttpClient 和带有 Jackson 的自定义 BodyHandler 反序列化 JSON 停止并且不会继续,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57629401/

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