在直接使用带有自定义HttpClient::send的Java11 HttpResponse.BodyHandler将JSON反序列化为定制对象时,我遇到了问题。我在回答这个问题时遇到了这个问题。
我正在使用的版本:
我创建了一个简单的泛型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类:
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),但是它只是挂起。有人知道有什么问题吗?
发布于 2019-08-23 16:37:49
我刚刚发现了这个问题,它有同样的问题,但与GZIPInputStream。事实证明,HttpResponse.BodySubscribers.mapping是有缺陷的,而且不像文档所描述的那样工作。以下是官方的链接 OpenJDK bug站点。它是为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中描述的方法中。
映射函数使用客户端的执行器执行,因此可以用于映射任何响应体类型,包括阻塞
InputStream。但是,在mapper函数中执行任何阻塞操作都有可能阻塞执行器线程的未知时间(至少在阻塞操作结束之前),这最终可能会使执行者缺乏可用的线程。因此,在映射到所需类型可能会阻塞的情况下(例如,通过在InputStream上读取),然后映射到所需类型的Supplier并将阻塞操作推迟到调用者的线程调用Supplier::get时应该是首选的。
发布于 2021-09-16 15:23:49
通过使用杰克逊TypeReference,我能够单独使用泛型来实现它,而不需要冗余的Class<T>参数。
package com.company;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.http.HttpResponse;
import java.util.function.Supplier;
public class JsonBodyHandler<W> implements HttpResponse.BodyHandler<Supplier<W>> {
    public JsonBodyHandler() {
    }
    private static <W> Supplier<W> toSupplierOfType(InputStream inputStream) {
        return () -> {
            try (InputStream stream = inputStream) {
                return new ObjectMapper().readValue(stream, new TypeReference<W>() {
                });
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        };
    }
    private static <W> HttpResponse.BodySubscriber<Supplier<W>> asJSON() {
        return HttpResponse.BodySubscribers.mapping(
                HttpResponse.BodySubscribers.ofInputStream(),
                JsonBodyHandler::toSupplierOfType);
    }
    @Override
    public HttpResponse.BodySubscriber<Supplier<W>> apply(HttpResponse.ResponseInfo responseInfo) {
        return JsonBodyHandler.asJSON();
    }
}并在使用中
package com.company;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.time.Duration;
import java.util.List;
public class Main {
    private static final HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://myApi"))
            .timeout(Duration.ofMinutes(1))
            .header("Content-Type", "application/json")
            .build();
    private static final HttpClient client = HttpClient.newBuilder().build();
    public static void main(String[] args) throws InterruptedException {
        client.sendAsync(Main.request, new JsonBodyHandler<List<MyDto>>())
                .thenAccept(response -> {
                    List<MyDto> myDtos = response.body().get();
                    System.out.println(myDtos);
                }).join();
    }
}发布于 2021-05-28 12:45:24
在科特林:
    val objectMapper = ObjectMapper()
    fun jsonBodyHandler(): HttpResponse.BodyHandler<JsonNode> {
      val jsonNodeSubscriber = BodySubscribers.mapping(BodySubscribers.ofByteArray()) {
        objectMapper.readTree(it)
      }
      return HttpResponse.BodyHandler { jsonNodeSubscriber }
    }https://stackoverflow.com/questions/57629401
复制相似问题