如何使用 Java 11 HttpClient 和 Jackson 将 JSON 响应映射到 Java class?
How to map a JSON response to a Java class using Java 11 HttpClient and Jackson?
我是 Java 11 HttpClient 的新手,想尝试一下。我有一个 return JSON 的简单 GET 请求,我想将 JSON 响应映射到名为 Questionnaire
的 Java class。
我知道我可以将开箱即用的响应转换为字符串或像这样的输入流
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());
我怎样才能像这样编写将 JSON 字符串转换为我的问卷 class 的东西?
HttpResponse<Questionnaire> response = this.client.send(request, HttpResponse.BodyHandlers./* what can I do here? */);
我使用 Jackson 将 JSON 转换为 Java class 实例。 Jackson 是否支持新的 Java 标准 HttpClient?
更新 1
我不够精确,对此感到抱歉。我正在寻找一个阻塞获取示例。我知道 http://openjdk.java.net/groups/net/httpclient/recipes.html#jsonGet
仅 Java 11 HttpClient::sendAsync
的解决方案
基于this link你可以这样做:
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
}
我使用了一些提供示例 JSON 输入和示例模型 class 的虚拟端点,使用 Jackson 将响应直接映射到 Model
class。
Java 11 HttpClient::send
和 HttpClient::sendAsync
的解决方案
我通过定义自定义找到了一种方法 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);
}
});
}
}
那我就叫它:
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{userId='1', id='1', title='delectus aut autem', completed=false}
JavaHttpResponse.BodySubscribers::mapping
的文档对于解决这个问题特别有用。可以进一步改进,使用 HttpResponse.BodySubscribers::ofInputStream
而不是 HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8)
来为 JsonBodyHandler
.
定义 BodySubscriber
为 Java 11 HttpClient::send
简化@michalk 解决方案
HttpService Class 示例:
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));
}}
模型Class示例:
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 + '\'' + '}'; }}
正在发送 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());
}
}}
回复:
Single Object: Model{id='1'}
Object: Model{id='1'}
Object: Model{id='2'}
Object: Model{id='3'}
必需的 Maven 依赖项(pom.xml):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
如果您可以包含依赖项,请查看 Methanol (disclaimer: I'm the library's author). The library has special BodyHandler
implementations for object mapping. You can add JSON support by installing the jackson adapter。
var request = MutableRequest.GET("https://example.com")
.header("Accept", "application/json");
var modelResponse = client.send(request, MoreBodyHandlers.ofObject(Model.class));
// Use TypeRef<T> for complex types
var modelListResponse = client.send(request, MoreBodyHandlers.ofObject(new TypeRef<List<Model>>() {}));
我是 Java 11 HttpClient 的新手,想尝试一下。我有一个 return JSON 的简单 GET 请求,我想将 JSON 响应映射到名为 Questionnaire
的 Java class。
我知道我可以将开箱即用的响应转换为字符串或像这样的输入流
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());
我怎样才能像这样编写将 JSON 字符串转换为我的问卷 class 的东西?
HttpResponse<Questionnaire> response = this.client.send(request, HttpResponse.BodyHandlers./* what can I do here? */);
我使用 Jackson 将 JSON 转换为 Java class 实例。 Jackson 是否支持新的 Java 标准 HttpClient?
更新 1 我不够精确,对此感到抱歉。我正在寻找一个阻塞获取示例。我知道 http://openjdk.java.net/groups/net/httpclient/recipes.html#jsonGet
仅 Java 11 HttpClient::sendAsync
的解决方案
基于this link你可以这样做:
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
}
我使用了一些提供示例 JSON 输入和示例模型 class 的虚拟端点,使用 Jackson 将响应直接映射到 Model
class。
Java 11 HttpClient::send
和 HttpClient::sendAsync
的解决方案
我通过定义自定义找到了一种方法 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);
}
});
}
}
那我就叫它:
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{userId='1', id='1', title='delectus aut autem', completed=false}
JavaHttpResponse.BodySubscribers::mapping
的文档对于解决这个问题特别有用。可以进一步改进,使用 HttpResponse.BodySubscribers::ofInputStream
而不是 HttpResponse.BodySubscribers.ofString(StandardCharsets.UTF_8)
来为 JsonBodyHandler
.
BodySubscriber
为 Java 11 HttpClient::send
简化@michalk 解决方案HttpService Class 示例:
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));
}}
模型Class示例:
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 + '\'' + '}'; }}
正在发送 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());
}
}}
回复:
Single Object: Model{id='1'}
Object: Model{id='1'}
Object: Model{id='2'}
Object: Model{id='3'}
必需的 Maven 依赖项(pom.xml):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
如果您可以包含依赖项,请查看 Methanol (disclaimer: I'm the library's author). The library has special BodyHandler
implementations for object mapping. You can add JSON support by installing the jackson adapter。
var request = MutableRequest.GET("https://example.com")
.header("Accept", "application/json");
var modelResponse = client.send(request, MoreBodyHandlers.ofObject(Model.class));
// Use TypeRef<T> for complex types
var modelListResponse = client.send(request, MoreBodyHandlers.ofObject(new TypeRef<List<Model>>() {}));