使用WebClient獲取JSON對象列表
1.概述
我們的服務通常與其他REST服務進行通信以獲取信息。
從Spring 5開始,我們將使用WebClient
以反應性,非阻塞的方式執行這些請求。 WebClient
是新WebFlux
框架的一部分,該框架基於Project Reactor
構建。它具有流暢的反應式API,並且在其底層實現中使用HTTP協議。
當我們發出Web請求時,數據通常以JSON返回。 WebClient
可以為我們轉換此內容。
在這篇文章中,我們將介紹如何將一個JSON數組轉換為Java Array
的Object
, Array
POJO的,和List
使用POJO的WebClient
。
2.依賴關係
要使用WebClient,
我們需要在pom.xml:
添加幾個依賴項pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectreactor</groupId>
<artifactId>reactor-spring</artifactId>
<version>1.0.1.RELEASE</version>
</dependency>
3. JSON,POJO和服務
讓我們從端點http://localhost:8080/readers
開始,該端點以JSON數組的形式返回讀者及其喜愛的書籍的列表:
[{
"id": 1,
"name": "reader1",
"favouriteBook": {
"author": "Milan Kundera",
"title": "The Unbearable Lightness of Being"
}
我們將需要相應的Reader
和Book
類來處理數據:
public class Reader {
private int id;
private String name;
private Book favouriteBook;
// getters and setters..
}
public class Book {
private final String author;
private final String title;
// getters and setters..
}
對於我們的接口實現,我們編寫一個ReaderConsumerServiceImpl
並將其作為WebClient
的依賴項:
public class ReaderConsumerServiceImpl implements ReaderConsumerService {
private final WebClient webClient;
public ReaderConsumerServiceImpl(WebClient webclient) {
this.webclient = webclient;
}
// ...
}
4.映射JSON對象List
當我們從REST請求接收到JSON數組時,有多種方法可以將其轉換為Java集合。讓我們看一下各種選項,看看處理返回的數據有多麼容易。我們將研究提取讀者最喜歡的書。
4.1。 Mono
與Flux
Project Reactor
引入了Publisher: **Mono**
兩種實現Publisher: **Mono**
和**Flux**
。
當我們需要處理零到許多或可能無限的結果時, Flux<T>
很有用。我們可以以Twitter供稿為例。
當我們知道一次返回所有結果時(如在用例中一樣),我們可以使用Mono<T>
。
4.2。 WebClient
與Object
數組
首先,讓我們使用WebClient.get
進行GET
調用,並使用Object[]
類型的Mono
來收集響應:
Mono<Object[]> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Object[].class).log();
接下來,讓我們將主體提取到Object
數組中:
Object[] objects = response.block();
實際的Object
這裡是包含我們的數據中的任意結構。讓我們將其轉換為Reader
對象的數組。
為此,我們需要一個ObjectMapper
:
ObjectMapper mapper = new ObjectMapper();
在這裡,我們將其聲明為內聯,儘管通常將其作為類的private static final
成員來完成。
最後,我們準備提取讀者喜愛的書籍並將其收集到列表中:
return Arrays.stream(objects)
.map(object -> mapper.convertValue(object, Reader.class))
.map(Reader::getFavouriteBook)
.collect(Collectors.toList());
當我們要求Jackson的反序列化器將Object
作為目標類型生成時,它實際上將JSON反序列化為一系列LinkedHashMap
對象。使用convertValue
後處理效率低下。如果我們在反序列化期間向Jackson提供所需的類型,則可以避免這種情況。
4.3。帶Reader
陣列的WebClient
我們可以向WebClient
提供Reader[]
而不是Object[]
:
Mono<Reader[]> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Reader[].class).log();
Reader[] readers = response.block();
return Arrays.stream(readers)
.map(Reader:getFavouriteBook)
.collect(Collectors.toList());
在這裡,我們可以看到我們不再需要ObjectMapper.convertValue
。但是,我們仍然需要進行其他轉換以使用Java Stream
API並使我們的代碼與List
一起使用。
4.4。帶Reader
List
WebClient
如果我們希望Jackson生成Reader
的List
而不是數組,則需要描述我們要創建的List
。為此,我們向該方法提供一個由匿名內部類產生的ParameterizedTypeReference
:
Mono<List> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(new ParameterizedTypeReference<List>() {});
List readers = response.block();
return readers.stream()
.map(Reader::getFavouriteBook)
.collect(Collectors.toList());
這為我們提供了我們可以使用的List
。
讓我們更深入地探討為什麼需要使用**ParameterizedTypeReference** .
當類型信息在運行時可用時,Spring的WebClient可以輕鬆地將JSON反序列化為Reader.class
。
但是,對於泛型,如果我們嘗試使用List<Reader>.class
,則會發生類型擦除。因此,Jackson將無法確定泛型的類型參數。
通過使用ParameterizedTypeReference
,我們可以克服此問題。將其實例化為匿名內部類利用了以下事實:泛型類的子類包含編譯時類型信息,該信息不受類型擦除的影響,可以通過反射使用。
5.結論
在本教程中,我們看到了使用WebClient
處理JSON對象的三種不同方式.
我們看到了指定Object
數組類型和我們自己的自定義類的方法。
然後,我們學習瞭如何使用ParameterizedTypeReference
提供類型信息以生成List
。