Java HttpClient–将JSON响应映射到Java类

2023/06/09

1. 概述

众所周知,Java 11中引入的HttpClient类有助于从服务器请求HTTP资源。它支持同步和异步编程模式。

在本教程中,我们将探讨将HTTP响应从HttpClient映射到普通旧Java对象(POJO)类的不同方法。

2. 示例设置

让我们写一个简单的程序,Todo。该程序将使用伪造的REST API。我们将执行GET请求,然后处理响应。

2.1 Maven依赖项

我们将使用Maven管理我们的依赖关系。让我们将GsonJackson依赖项添加到我们的pom.xml中,以使这些库可用于我们的程序:

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

2.2 示例项目

在本教程中,我们将使用伪造的REST API进行快速原型制作。

首先,让我们查看调用GET请求时的示例API响应:

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

示例API返回具有四个属性的JSON响应。JSON响应有多个对象,但为简单起见,我们将跳过它们。

接下来,让我们创建一个用于数据绑定的POJO类。类字段匹配JSON数据属性。我们将包括构造函数、getter、setter、equals()和toString():

public class Todo {

    int userId;
    int id;
    String title;
    boolean completed;

    // Standard constructors, getters, setters, equals(), and toString()
}

然后,让我们创建一个包含我们的逻辑的TodoAppClient类:

public class TodoAppClient {

    ObjectMapper objectMapper = new ObjectMapper();
    Gson gson = new GsonBuilder.create();

    // ...
}

此外,我们创建了ObjectMapper和GsonBuilder的新实例。这使得任何方法都可以访问和重用它。在方法中创建ObjectMapper的实例可能是一项昂贵的操作。

最后,我们将编写一个在示例API上同步执行GET请求的方法:

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。响应是一个JSON字符串,以便于在程序中进行操作。在后面的部分中,我们将探索将响应映射到POJO类的方法。

让我们为sampleApiRequest()方法编写一个单元测试:

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

该测试确定API调用的响应不为null。

3. 使用Jackson将响应映射到POJO类

Jackson是一个流行的Java JSON库。它有助于序列化和反序列化JSON以进行进一步操作。我们将使用它反序列化来自示例设置的JSON响应。我们会将响应映射到Todo POJO类。

让我们增强包含客户端逻辑的类。我们将创建一个新方法并调用sampleApiRequest()方法以使响应可用于映射:

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

接下来,我们声明了一个Todo数组。最后,我们从ObjectMapper调用readValue()将JSON字符串映射到POJO类。

让我们通过将返回的Todo与预期的Todo新实例进行比较来测试该方法:

@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());
}

该测试将预期结果与映射的JSON进行比较。它确定它们是相等的。

4. 使用Gson将响应映射到POJO类

Gson是Google的一个Java库,它在Java生态系统中与Jackson一样受欢迎。它有助于将JSON String映射到Java对象以供进一步处理,该库还可以将Java对象转换为JSON。

我们将使用它将示例设置中的JSON响应映射到其等效的POJO类Todo。让我们在包含我们的逻辑的类中编写一个新方法syncGson()。

我们将调用sampleApi()使JSON字符串可用于反序列化:

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

首先,我们创建了一个Todo类型的列表。然后我们调用fromJson()方法将JSON字符串映射到POJO类。

JSON字符串现在映射到POJO类以供进一步操作和处理。

让我们通过将预期结果与返回的Todo进行比较来为syncGson()编写单元测试:

@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. 异步调用

现在,让我们异步实现API调用。在异步模式中,线程不等待彼此完成。这种编程模式使获取数据更加健壮和可扩展。

让我们异步获取示例API并将JSON响应映射到POJO类。

5.1 使用Jackson异步调用和映射到POJO类

在本教程中,我们使用两个Java库来反序列化JSON响应。让我们用Jackson实现异步调用映射。首先,让我们在TodoAppClient中创建一个方法readValueJackson()。

该方法反序列化JSON响应并将其映射到POJO类:

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);
}

该方法发出异步GET请求并通过调用readValueJackson()将JSON字符串映射到POJO类。

最后,让我们编写一个单元测试来比较反序列化的JSON响应与预期的Todo实例:

@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());
}

预期结果和映射的JSON响应相等。

5.2 使用Gson异步调用和映射到POJO类

让我们通过将异步JSON响应映射到POJO类来进一步增强程序。首先,让我们创建一个方法来反序列化TodoAppClient中的JSON字符串:

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);
}

该方法发出异步GET请求并将JSON响应映射到POJO类。最后,我们调用了readValueGson()来执行将响应映射到POJO类的过程。

让我们写一个单元测试。我们会将预期的Todo新实例与映射响应进行比较:

@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());
}

测试表明预期结果与映射的JSON响应匹配。

6. 总结

在本文中,我们学习了四种在使用HttpClient时将JSON响应映射到POJO类的方法。此外,我们还深入研究了HttpClient的同步和异步编程模式。此外,我们使用Jackson和Gson库反序列化JSON响应并将JSON字符串映射到POJO类。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章