JsonMapping使用可分页字段测试终结点时的异常



我需要调用一个需要Pageable字段的端点:

@GetMapping
public Page<ProductDTO> listProducts(Pageable pageable) {
return productService.findProducts(pageable); 
}

在我的测试中,我有以下代码:

MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.add("page", String.valueOf(0));
URI url = defaultURI(port, "/products", parameters);
ParameterizedTypeReference<RestResponsePage<ProductDTO>> type = new ParameterizedTypeReference<RestResponsePage<ProductDTO>>() {};
ResponseEntity<RestResponsePage<ProductDTO>> response = restTemplate.exchange(url.toString(), HttpMethod.GET, httpEntity, type);

PageImpl不包含默认构造函数,因此为了避免这个问题,我创建了一个类似于以下内容的类以传递给ParameterizedTypeReference

@JsonIgnoreProperties(ignoreUnknown = true) @Getter @Setter
public class RestResponsePage<T> extends PageImpl<T> implements Serializable {
private static final long serialVersionUID = 3844794233375694591L;
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public RestResponsePage(@JsonProperty("content") List<T> content,
@JsonProperty("number") int page,
@JsonProperty("size") int size,
@JsonProperty("totalElements") long totalElements) {
super(content, new PageRequest(page, size), totalElements);
}
public RestResponsePage(List<T> content, Pageable pageable, long totalElements) {
super(content, pageable, totalElements);
}
public RestResponsePage(List<T> content) {
super(content);
}
public RestResponsePage() {
super(new ArrayList<T>());
}
}

问题是我仍然收到以下错误:

Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of org.springframework.data.domain.Pageable: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: java.io.PushbackInputStream@75564689; line: 38, column: 16] (through reference chain: com.shaunyl.util.ResponsePageImpl["pageable"])

为什么它一直说我正在通过一个抽象类?ResponsePageImpl是一个类而不是抽象类。

谢谢

package com.td.support;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import java.util.ArrayList;
import java.util.List;
public class RestResponsePage<T> extends PageImpl<T> {
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public RestResponsePage(@JsonProperty("content") List<T> content,
@JsonProperty("number") int number,
@JsonProperty("size") int size,
@JsonProperty("totalElements") Long totalElements,
@JsonProperty("pageable") JsonNode pageable,
@JsonProperty("last") boolean last,
@JsonProperty("totalPages") int totalPages,
@JsonProperty("sort") JsonNode sort,
@JsonProperty("first") boolean first,
@JsonProperty("numberOfElements") int numberOfElements) {
super(content, PageRequest.of(number, size), totalElements);
}
public RestResponsePage(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public RestResponsePage(List<T> content) {
super(content);
}
public RestResponsePage() {
super(new ArrayList<>());
}
}

Spring Boot 2.0 上面可能没问题。

我找到了一种更优雅的方法来解决这个问题。事实上,这是杰克逊序列化的问题。

首先,定义一个杰克逊模块:

public class PageJacksonModule extends Module {
@Override
public String getModuleName() {
return "PageJacksonModule";
}
@Override
public Version version() {
return new Version(0,1,0, "", null,null);
}
@Override
public void setupModule(SetupContext context) {
context.setMixInAnnotations(Page.class, PageMixIn.class);
}
@JsonDeserialize(as = SimplePageImpl.class)
private interface PageMixIn{ }

static class SimplePageImpl<T> implements Page<T> {
private final Page<T> delegate;
public SimplePageImpl(
@JsonProperty("content") List<T> content,
@JsonProperty("page")int number,
@JsonProperty("size") int size,
@JsonProperty("totalElements") long totalElements){
delegate = new PageImpl<>(content, PageRequest.of(number, size), totalElements);
}

@JsonProperty
@Override
public int getTotalPages() {
return delegate.getTotalPages();
}
@JsonProperty
@Override
public long getTotalElements() {
return delegate.getTotalElements();
}
@JsonProperty("page")
@Override
public int getNumber() {
return delegate.getNumber();
}
@JsonProperty
@Override
public int getSize() {
return delegate.getSize();
}
@JsonProperty
@Override
public int getNumberOfElements() {
return delegate.getNumberOfElements();
}
@JsonProperty
@Override
public List<T> getContent() {
return delegate.getContent();
}
@JsonProperty
@Override
public boolean hasContent() {
return delegate.hasContent();
}
@JsonIgnore
@Override
public Sort getSort() {
return delegate.getSort();
}
@JsonProperty
@Override
public boolean isFirst() {
return delegate.isFirst();
}
@JsonProperty
@Override
public boolean isLast() {
return delegate.isLast();
}
@JsonIgnore
@Override
public boolean hasNext() {
return delegate.hasNext();
}
@JsonIgnore
@Override
public boolean hasPrevious() {
return delegate.hasPrevious();
}
@JsonIgnore
@Override
public Pageable nextPageable() {
return delegate.nextPageable();
}

@JsonIgnore
@Override
public Pageable previousPageable() {
return delegate.previousPageable();
}

@JsonIgnore
@Override
public <U> Page<U> map(Function<? super T, ? extends U> converter) {
return delegate.map(converter);
}
@JsonIgnore
@Override
public Iterator<T> iterator() {
return delegate.iterator();
}
}
}

然后注入它:

@Configuration
public class JacksonConfig {
@Bean
public Module pageJacksonModule() {
return new PageJacksonModule();
}
}

最后,您可以使用Page对象。

当然,测试:

@RunWith(SpringJUnit4ClassRunner.class)
@JsonTest
public class PageImplTest {
@Autowired
ObjectMapper mapper;
@Test
public  void page() throws IOException {
String inputPageStr = "{"content":[{"name":"n1","gender":"boy","age":23},{"name":"n2","gender":"girl","age":20}],"pageable":"INSTANCE","totalPages":1,"last":true,"totalElements":2,"sort":{"sorted":false,"unsorted":true,"empty":true},"first":true,"numberOfElements":2,"size":2,"number":0,"empty":false}";
Page<PageItem> pageItems = mapper
.readValue(inputPageStr, new TypeReference<Page<PageItem>>() {});
Assert.assertNotNull(pageItems);
}
static class PageItem {
private String name;
private String gender;
private int age;
public PageItem() {
}
public PageItem(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
@Configuration
static class TestConfig{
@Bean
public Module pageJacksonModule() {
return new PageJacksonModule();
}
}
}

最新更新