1. Overview
1.概述
Our services often communicate with other REST services to fetch information.
我们的服务经常与其他REST服务通信,以获取信息。
From Spring 5, we get to use WebClient to perform these requests in a reactive, non-blocking way. WebClient is part of the new WebFlux Framework, built on top of Project Reactor. It has a fluent, reactive API, and it uses HTTP protocol in its underlying implementation.
从Spring 5开始,我们可以使用WebClient来以反应式、非阻塞的方式执行这些请求。WebClient是新的WebFlux框架的一部分,建立在Project Reactor之上。它有一个流畅的、反应式的API,并在其底层实现中使用HTTP协议。
When we make a web request, the data is often returned as JSON. WebClient can convert this for us.
当我们发出网络请求时,数据通常以JSON形式返回。WebClient可以为我们转换。
In this article, we’ll find out how to convert a JSON Array into a Java Array of Object, Array of POJO, and a List of POJO using WebClient.
在这篇文章中,我们将了解如何使用WebClient将JSON数组转换为Object的JavaArray、Array的POJO和POJO的List。
2. Dependencies
2.依赖性
To use WebClient, we’ll need to add a couple of dependencies to our pom.xml:
为了使用WebClient,我们需要在我们的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, and Service
3.JSON, POJO, 和服务
Let’s start with an endpoint http://localhost:8080/readers that returns a list of readers with their favorite books as a JSON array:
让我们从一个端点http://localhost:8080/readers开始,该端点以JSON数组的形式返回读者及其喜爱的书籍的列表。
[{ "id": 1, "name": "reader1", "favouriteBook": { "author": "Milan Kundera", "title": "The Unbearable Lightness of Being" }
}, { "id": 2, "name": "reader2" "favouriteBook": { "author": "Douglas Adams", "title": "The Hitchhiker's Guide to the Galaxy" } }]
We’ll require the corresponding Reader and Book classes to process data:
我们将需要相应的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..
}
For our interface implementation, we write a ReaderConsumerServiceImpl with WebClient as its dependency:
对于我们的接口实现,我们编写了一个ReaderConsumerServiceImpl ,将WebClient 作为其依赖。
public class ReaderConsumerServiceImpl implements ReaderConsumerService {
private final WebClient webClient;
public ReaderConsumerServiceImpl(WebClient webclient) {
this.webclient = webclient;
}
// ...
}
4. Mapping a List of JSON Objects
4.映射一个List的JSON对象
When we receive a JSON array from a REST request, there are multiple ways to convert it to a Java collection. Let’s look at the various options and see how easy it is to process the data returned. We’ll look at extracting the readers’ favorite books.
当我们从REST请求中收到一个JSON数组时,有多种方法可以将其转换为一个Java集合。让我们看看各种选项,看看处理返回的数据有多容易。我们来看看提取读者最喜欢的书。
4.1. Mono vs. Flux
4.1. Mono与Flux的对比
Project Reactor has introduced two implementations of Publisher: Mono and Flux.
Project Reactor已经引入了Publisher的两个实现。Mono和Flux。
Flux<T> is useful when we need to handle zero to many or potentially infinite results. We can think of a Twitter feed as an example.
Flux<T>在我们需要处理从零到多或可能是无限的结果时很有用。我们可以把Twitter的feed作为一个例子。
When we know that the results are returned all at once – as in our use case – we can use Mono<T>.
当我们知道结果是一次性返回的–如我们的用例–我们可以使用Mono<T>。
4.2. WebClient with Object Array
4.2.WebClient与Object阵列
First, let’s make the GET call with WebClient.get and use a Mono of type Object[] to collect the response:
首先,让我们用WebClient.get进行GET调用,并使用Mono的Object[]类型来收集响应。
Mono<Object[]> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Object[].class).log();
Next, let’s extract the body into our array of Object:
接下来,让我们将主体提取到我们的Object数组中。
Object[] objects = response.block();
The actual Object here is an arbitrary structure that contains our data. Let’s convert it into an array of Reader objects.
这里实际的Object是一个任意的结构,包含我们的数据。让我们把它转换成一个Reader对象的数组。
For this, we’ll need an ObjectMapper:
为此,我们将需要一个ObjectMapper。
ObjectMapper mapper = new ObjectMapper();
Here, we declared it inline, though this is usually done as a private static final member of the class.
在这里,我们内联声明了它,尽管这通常是作为类的private static final成员进行的。
Lastly, we’re ready to extract the readers’ favorite books and collect them to a list:
最后,我们准备提取读者最喜欢的书,并将它们收集到一个列表中。
return Arrays.stream(objects)
.map(object -> mapper.convertValue(object, Reader.class))
.map(Reader::getFavouriteBook)
.collect(Collectors.toList());
When we ask the Jackson deserializer to produce Object as the target type, it actually deserializes JSON into a series of LinkedHashMap objects. Post-processing with convertValue is inefficient. We can avoid this if we provide our desired type to Jackson during deserialization.
当我们要求Jackson反序列化器产生Object作为目标类型时,它实际上将JSON反序列化为一系列的LinkedHashMap对象。用convertValue进行后处理的效率很低。如果我们在反序列化过程中向Jackson提供我们想要的类型,我们可以避免这种情况。
4.3. WebClient with Reader Array
4.3.WebClient与Reader Array
We can provide Reader[] instead of Object[] to our 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());
Here, we can observe that we no longer need the ObjectMapper.convertValue. However, we still need to do additional conversions to use the Java Stream API and for our code to work with a List.
在这里,我们可以看到,我们不再需要ObjectMapper.convertValue。然而,我们仍然需要进行额外的转换,以使用Java Stream API,并使我们的代码能够与List一起工作。
4.4. WebClient with Reader List
4.4.WebClient与Reader List
If we want Jackson to produce a List of Readers instead of an array, we need to describe the List we want to create. To do this, we provide a ParameterizedTypeReference produced by an anonymous inner class to the method:
如果我们希望Jackson产生一个List的Reader,而不是一个数组,我们需要描述我们想要创建的List。为此,我们向该方法提供一个由匿名内部类产生的ParameterizedTypeReference。
Mono<List<Reader>> response = webClient.get()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(new ParameterizedTypeReference<List<Reader>>() {});
List<Reader> readers = response.block();
return readers.stream()
.map(Reader::getFavouriteBook)
.collect(Collectors.toList());
This gives us the List that we can work with.
这给了我们可以使用的List。
Let’s take a deeper dive into why we need to use the ParameterizedTypeReference.
让我们深入了解一下为什么我们需要使用ParameterizedTypeReference。
Spring’s WebClient can easily deserialize the JSON into a Reader.class when the type information is available at runtime.
当类型信息在运行时可用时,Spring的WebClient可以轻松地将JSON反序列化到Reader.class。
With generics, however, type erasure occurs if we try to use List<Reader>.class. So, Jackson will not be able to determine the generic’s type parameter.
然而,对于泛型,如果我们试图使用List<Reader>.class,就会发生type erasure。所以,Jackson将无法确定泛型的类型参数。
By using ParameterizedTypeReference, we can overcome this problem. Instantiating it as an anonymous inner class exploits the fact that subclasses of generic classes contain compile-time type information that is not subject to type erasure and can be consumed through reflection.
通过使用ParameterizedTypeReference,我们可以克服这个问题。将其实例化为匿名内类,可以利用泛型类的子类包含编译时的类型信息这一事实,这些信息不受类型清除的影响,可以通过反射来消耗。
5. Conclusion
5.总结
In this tutorial, we saw three different ways of processing JSON objects using WebClient. We saw ways of specifying the types of arrays of Object and our own custom classes.
在本教程中,我们看到了使用WebClient处理JSON对象的三种不同方式。我们看到了指定Object和我们自己的自定义类的数组类型的方法。
We then learned how to provide the type of information to produce a List by using the ParameterizedTypeReference.
然后我们学习了如何通过使用ParameterizedTypeReference来提供信息类型以产生一个List。
As always, the code for this article is available over on GitHub.
一如既往,本文的代码可在GitHub上获得over。