1. 概述
我們的服務經常需要與其他的 REST 服務進行通信,以獲取信息。
在 Spring 中,我們可以使用 <em >RestTemplate</em> 來執行同步的 HTTP 請求。數據通常以 JSON 格式返回,<em >RestTemplate</em> 可以自動將其轉換為我們需要的格式。
在本教程中,我們將探索如何將 JSON 數組轉換為 Java 中三種不同的對象結構:Array 中的 Object、Array 中的 POJO 以及 List 中的 POJO。
2. JSON, POJO 和服務
假設我們有一個端點 http://localhost:8080/users 返回以下 JSON 格式的用户列表:
[{
"id": 1,
"name": "user1",
}, {
"id": 2,
"name": "user2"
}]我們將會需要相應的 用户 類來處理數據:
public class User {
private int id;
private String name;
// getters and setters..
}對於我們的接口實現,我們編寫一個 UserConsumerServiceImpl,其中 RestTemplate 作為其依賴項:
public class UserConsumerServiceImpl implements UserConsumerService {
private final RestTemplate restTemplate;
public UserConsumerServiceImpl(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
...
}3. 將 JSON 對象列表映射
當 REST 請求的響應是一個 JSON 數組時,有幾種方法可以將它轉換為 Java 集合。
讓我們看看這些選項以及它們如何輕鬆地處理返回的數據。我們將查看從 REST 服務返回的某些用户對象中提取用户名。
3.1. 使用 Object 數組的 RestTemplate
首先,我們使用 RestTemplate.getForEntity 方法並使用 ResponseEntity 類型為 Object[] 以收集響應:
ResponseEntity<Object[]> responseEntity =
restTemplate.getForEntity(BASE_URL, Object[].class);接下來,我們可以將正文提取到我們的 Object 數組中:
Object[] objects = responseEntity.getBody();這裏實際的 對象 只是包含我們數據的任意結構,但沒有使用我們的 用户 類型。讓我們將其轉換為我們的 用户 對象。
為此,我們需要一個 對象映射器:
ObjectMapper mapper = new ObjectMapper();我們可以將其聲明為內聯,儘管這通常作為類的一個私有靜態常量成員來完成。
最後,我們準備提取用户名:
return Arrays.stream(objects)
.map(object -> mapper.convertValue(object, User.class))
.map(User::getName)
.collect(Collectors.toList());使用這種方法,我們可以將任意類型的數據有效地轉換為 Java 中的 數組對象。這在僅需要計數結果時非常方便。
然而,它並不適合進一步的處理。我們需要付出額外的努力將其轉換為我們能夠處理的類型。
Jackson 解序列化器實際上會將 JSON 序列化為一系列 LinkedHashMap 對象,當我們要求它將目標類型設置為 Object 時。使用 convertValue 進行後處理是一種低效的開銷。
如果我們直接向 Jackson 提供所需的類型,就可以避免這種情況。
3.2. 使用 User 數組的 RestTemplate
我們可以將 User[] 提供給 RestTemplate,而不是 Object[]。
ResponseEntity<User[]> responseEntity =
restTemplate.getForEntity(BASE_URL, User[].class);
User[] userArray = responseEntity.getBody();
return Arrays.stream(userArray)
.map(User::getName)
.collect(Collectors.toList());我們能看出,我們不再需要 ObjectMapper.convertValue。 ResponseEntity 內部已經包含 User 對象。 但為了使用 Java 的 Stream API 以及為了使我們的代碼能夠與 List 配合工作,我們仍然需要進行一些額外的轉換。
3.3. 使用 User 列表和 ParameterizedTypeReference
如果我們需要 Jackson 產生一個 List 中的 User 對象,而不是數組,則需要描述我們想要創建的 List。為此,我們需要使用 RestTemplate 的 exchange 方法。
該方法接受由匿名內部類產生的 ParameterizedTypeReference:
ResponseEntity<List<User>> responseEntity =
restTemplate.exchange(
BASE_URL,
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<User>>() {}
);
List<User> users = responseEntity.getBody();
return users.stream()
.map(User::getName)
.collect(Collectors.toList());這會產生我們想要使用的列表。
讓我們更深入地瞭解為什麼我們需要使用參數化類型引用。
在第一個和第二個示例中,Spring 可以輕鬆地將 JSON 解析為User.class類型令牌,其中運行時可以完全獲取類型信息。
然而,使用泛型時,如果嘗試使用List<User>.class,則會發生類型擦除。因此,Jackson 將無法確定<>內部的類型。
我們可以通過使用一個父類型令牌參數化類型引用來克服這個問題。實例化它為匿名內部類——new ParameterizedTypeReference<List<User>>() {}——利用了泛型類子類的編譯時類型信息不受類型擦除的影響,並且可以通過反射來消費這些信息。
4. 結論
在本文中,我們探討了三種使用 RestTemplate 處理 JSON 對象的方法。我們瞭解瞭如何指定 Object 數組的類型以及自定義類的類型。
然後,我們學習瞭如何通過使用 ParameterizedTypeReference 提供類型信息,從而生成 List。