1. 概述

Java 11引入的HttpClient类,是处理HTTP请求的利器。它支持同步和异步两种编程模式,让开发者能灵活处理网络通信。

本文将深入探讨如何将HttpClient获取的JSON响应数据映射为POJO(Plain Old Java Object)对象,这是实际开发中的高频需求。

2. 示例项目搭建

我们通过一个Todo示例程序来演示,该程序会调用一个模拟REST API,执行GET请求并处理响应数据。

2.1 Maven依赖

使用Maven管理依赖,在pom.xml中添加Gson和Jackson库:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.10.1</version>
</dependency>
        
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.16.0</version>
</dependency>

2.2 项目结构

我们使用JSONPlaceholder提供的模拟API进行快速原型开发。

API响应示例:

[
  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  },
]

响应包含四个属性,为简化演示,我们只处理单个对象。

创建POJO类:

public class Todo {
 
    int userId;
    int id;
    String title;
    boolean completed;
    
    // 标准构造器、getter/setter、equals()和toString()
}

创建客户端类:

public class TodoAppClient { 
    
    ObjectMapper objectMapper = new ObjectMapper();
    Gson gson = new GsonBuilder().create();      
    
    // ...
}

⚠️ 性能提示ObjectMapperGson实例应复用,避免在方法内频繁创建。

同步请求方法:

public class TodoAppClient { 

    // ...   

    String sampleApiRequest() throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
          .uri(URI.create("https://jsonplaceholder.typicode.com/todos"))
          .build();
 
        HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
 
        return response.body();
    }

    // ...
}

BodyHandlers.ofString()将响应体转换为String格式,便于后续处理。

单元测试:

@Test
public void givenSampleRestApi_whenApiIsConsumedByHttpClient_thenCompareJsonString() throws Exception {
    TodoAppClient sampleApi = new TodoAppClient();
    assertNotNull(sampleApi.sampleApiRequest());
}

3. 使用Jackson映射响应

Jackson是Java生态中最流行的JSON处理库,提供强大的序列化和反序列化能力。

映射方法实现:

public Todo syncJackson() throws Exception {
    
    String response = sampleApiRequest();  
    Todo[] todo = objectMapper.readValue(response, Todo[].class); 
    
    return todo[0];
 }

关键步骤:

  1. 获取JSON字符串
  2. 使用readValue()映射为Todo数组
  3. 返回首个元素

单元测试:

@Test
public void givenSampleApiCall_whenResponseIsMappedByJackson_thenCompareMappedResponseByJackson() throws Exception {
    Todo expectedResult = new Todo(1, 1, "delectus aut autem", false); 
    TodoAppClient jacksonTest = new TodoAppClient();
    assertEquals(expectedResult, jacksonTest.syncJackson());
}

✅ 验证映射结果与预期对象一致。

4. 使用Gson映射响应

Gson是Google出品的JSON库,与Jackson并驾齐驱,同样提供JSON与Java对象的转换能力。

映射方法实现:

public Todo syncGson() throws Exception {
    String response = sampleApiRequest();
    List<Todo> todo = gson.fromJson(response, new TypeToken<List<Todo>>(){}.getType());
    return todo.get(0);
}

核心操作:

  1. 获取JSON字符串
  2. 使用fromJson()配合TypeToken处理泛型
  3. 返回列表首元素

单元测试:

@Test
public void givenSampleApiCall_whenResponseIsMappedByGson_thenCompareMappedGsonJsonResponse() throws Exception {
    Todo expectedResult = new Todo(1, 1, "delectus aut autem", false);   
    TodoAppClient gsonTest = new TodoAppClient();
    assertEquals(expectedResult, gsonTest.syncGson()); 
}

5. 异步调用处理

异步编程能显著提升系统吞吐量,HttpClient原生支持异步模式。

5.1 Jackson异步映射

反序列化方法:

List<Todo> readValueJackson(String content) {
    try {
        return objectMapper.readValue(content, new TypeReference<List<Todo>>(){});
    } catch (IOException ioe) {
        throw new CompletionException(ioe);
    }
}

异步请求方法:

public Todo asyncJackson() throws Exception {
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://jsonplaceholder.typicode.com/todos"))
      .build(); 
  
    TodoAppClient todoAppClient = new TodoAppClient();
    List<Todo> todo = HttpClient.newHttpClient()
      .sendAsync(request, BodyHandlers.ofString())
      .thenApply(HttpResponse::body)
      .thenApply(todoAppClient::readValueJackson)
      .get();
 
    return todo.get(0);
}

执行流程:

  1. 发送异步请求
  2. 获取响应体
  3. 调用反序列化方法
  4. 阻塞获取结果

单元测试:

@Test
public void givenSampleApiAsyncCall_whenResponseIsMappedByJackson_thenCompareMappedJacksonJsonResponse() throws Exception {
    Todo expectedResult = new Todo(1, 1, "delectus aut autem", false);  
    TodoAppClient sampleAsyncJackson = new TodoAppClient();
    assertEquals(expectedResult, sampleAsyncJackson.asyncJackson());
}

5.2 Gson异步映射

反序列化方法:

List<Todo> readValueGson(String content) {
    return gson.fromJson(content, new TypeToken<List<Todo>>(){}.getType());
}

异步请求方法:

public Todo asyncGson() throws Exception {
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://jsonplaceholder.typicode.com/todos"))
      .build();
    TodoAppClient todoAppClient = new TodoAppClient();
    List<Todo> todo = HttpClient.newHttpClient()
      .sendAsync(request, BodyHandlers.ofString())
      .thenApply(HttpResponse::body)
      .thenApply(todoAppClient::readValueGson)
      .get();
  
    return todo.get(0);
}

单元测试:

@Test
public void givenSampleApiAsyncCall_whenResponseIsMappedByGson_thenCompareMappedGsonResponse() throws Exception {
    Todo expectedResult = new Todo(1, 1, "delectus aut autem", false); 
    TodoAppClient sampleAsyncGson = new TodoAppClient();
    assertEquals(expectedResult, sampleAsyncGson.asyncGson());
}

6. 总结

本文系统介绍了四种将JSON响应映射为Java对象的方法:

场景 同步方案 异步方案
Jackson ✅ 已实现 ✅ 已实现
Gson ✅ 已实现 ✅ 已实现

关键要点:

  1. HttpClient是Java 11+处理HTTP请求的标准工具
  2. Jackson和Gson都能高效完成JSON映射
  3. 异步模式适合高并发场景
  4. 复用ObjectMapper/Gson实例可提升性能

完整示例代码可在GitHub获取。实际开发中,建议根据项目需求选择合适的库和调用模式。


原始标题:Java HttpClient – Map JSON Response to Java Class