saw*_*303 5 java http httpclient java-http-client java-11
我是Java 11 HttpClient的新手,想尝试一下。我有一个简单的GET请求,该请求返回JSON,我想将JSON响应映射到名为的Java类Questionnaire
。
我知道我可以将响应立即转换为String或类似这样的输入流
HttpRequest request = HttpRequest.newBuilder(new URI(String.format("%s%s", this.baseURI, "/state")))
.header(ACCEPT, APPLICATION_JSON)
.PUT(noBody()).build();
HttpResponse<String> response = this.client.send(request, HttpResponse.BodyHandlers.ofString());
Run Code Online (Sandbox Code Playgroud)
我该如何写一些将JSON字符串转换为我的Questionnaire类的东西?
HttpResponse<Questionnaire> response = this.client.send(request, HttpResponse.BodyHandlers./* what can I do here? */);
Run Code Online (Sandbox Code Playgroud)
我使用Jackson将JSON转换为Java类实例。Jackson是否支持新的Java标准HttpClient?
更新1 我不够精确,对此感到抱歉。我正在寻找一个阻止获取示例。我知道http://openjdk.java.net/groups/net/httpclient/recipes.html#jsonGet
mic*_*alk 11
HttpClient::sendAsync
仅适用于 Java 11 的解决方案基于此链接,您可以执行以下操作:
public static void main(String[] args) throws IOException, URISyntaxException, ExecutionException, InterruptedException {
UncheckedObjectMapper uncheckedObjectMapper = new UncheckedObjectMapper();
HttpRequest request = HttpRequest.newBuilder(new URI("https://jsonplaceholder.typicode.com/todos/1"))
.header("Accept", "application/json")
.build();
Model model = HttpClient.newHttpClient()
.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenApply(uncheckedObjectMapper::readValue)
.get();
System.out.println(model);
}
class UncheckedObjectMapper extends com.fasterxml.jackson.databind.ObjectMapper {
/**
* Parses the given JSON string into a Map.
*/
Model readValue(String content) {
try {
return this.readValue(content, new TypeReference<Model>() {
});
} catch (IOException ioe) {
throw new CompletionException(ioe);
}
}
}
class Model {
private String userId;
private String id;
private String title;
private boolean completed;
//getters setters constructors toString
}
Run Code Online (Sandbox Code Playgroud)
我使用了一些提供示例 JSON 输入和示例模型类的虚拟端点,以Model
使用 Jackson将响应直接映射到类。
HttpClient::send
和HttpClient::sendAsync
我通过定义 custom 找到了一种方法HttpResponse.BodyHandler
:
public class JsonBodyHandler<W> implements HttpResponse.BodyHandler<W> {
private Class<W> wClass;
public JsonBodyHandler(Class<W> wClass) {
this.wClass = wClass;
}
@Override
public HttpResponse.BodySubscriber<W> apply(HttpResponse.ResponseInfo responseInfo) {
return asJSON(wClass);
}
public static <T> HttpResponse.BodySubscriber<T> asJSON(Class<T> 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);
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
然后我称之为:
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);
}
Run Code Online (Sandbox Code Playgroud)
回应是:
Model{userId='1', id='1', title='delectus aut autem', completed=false}
Run Code Online (Sandbox Code Playgroud)
JavaDocHttpResponse.BodySubscribers::mapping
对解决这个问题特别有用。它可以进一步提高使用HttpResponse.BodySubscribers::ofInputStream
,而不是HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8)
来定义BodySubscriber
的JsonBodyHandler
。
小智 8
简化 Java 11 HttpClient::send 的 @michalk 解决方案
HttpService 类示例:
public class HttpService {
private final HttpClient httpClient= HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build();
public HttpService() {}
public <T> T sendGetRequest(String url, Class<T> responseType) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder().GET().uri(URI.create(url)).header("Accept", "application/json").build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
return new ObjectMapper().readValue(response.body(), responseType);
}
public <T> List<T> sendGetListRequest(String url, Class<T> responseType) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder().GET().uri(URI.create(url)).header("Accept", "application/json").build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(response.body(), objectMapper.getTypeFactory().constructCollectionType(List.class, responseType));
}}
Run Code Online (Sandbox Code Playgroud)
模型类示例:
public class Model {
private String id;
public Model() {}
public String getId() { return this.id; }
public void setId(String id) { this.id = id; }
@Override
public String toString() { return "Model{" + "id='" + id + '\'' + '}'; }}
Run Code Online (Sandbox Code Playgroud)
发送 HTTP GET 请求:
public class Main {
public static void main(String[] args) {
try {
HttpService httpService = new HttpService();
Model model = httpService.sendGetRequest("http://localhost:8080/api/v1/models/1", Model.class);
System.out.println("Single Object:" + model);
System.out.print('\n');
List<Model> models = httpService.sendGetListRequest("http://localhost:8080/api/v1/models", Model.class);
for(Model m: models) { System.out.println("Object:" + m); }
}
catch (IOException | InterruptedException e) {
System.err.println("Failed to send GET request: " + e.getMessage());
}
}}
Run Code Online (Sandbox Code Playgroud)
回复:
Single Object: Model{id='1'}
Object: Model{id='1'}
Object: Model{id='2'}
Object: Model{id='3'}
Run Code Online (Sandbox Code Playgroud)
所需的 Maven 依赖项 (pom.xml):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)