当前位置: 首页 > 知识库问答 >
问题:

使用Jackson自定义反序列化器通过RestTemplate解析Spring-Boot 2.1.0页面响应

马峻
2023-03-14
{
   "content": [ "3" ],
   "pageable": {
     "sort": {
       "sorted": false,
       "unsorted": true,
       "empty": true
     },
     "offset": 0,
     "pageSize": 20,
     "pageNumber": 0,
     "unpaged": false,
     "paged": true
  },
  "last": true,
  "totalPages": 1,
  "totalElements": 1,
  "size": 20,
  "number": 0,
  "numberOfElements": 1,
  "first": true,
  "sort": {
    "sorted": false,
    "unsorted": true,
    "empty": true
  },
  "empty": false
}
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 
Cannot construct instance of `org.mypackage.RestPageResponse`, 
problem: argument type mismatch at [Source: (String)
"{ "content":["1","2","3"],"first":true,"last":false,  "number":0,"numberOfElements":3,"size":3,"totalElements":90,"totalPages":30,  "sort":[{"direction":"ASC","property":"name","ignoreCase":false,"nullHandling":"NATIVE","ascending":true,"descending":false}]  }"; 
line: 1, column: 260]

下面是响应(域模型)的Java类。我已经将排序数据建模为这个类中的一个字段,这可能是错误的,应该有一种方法将它在一个可分页对象中传递给超类。

import java.util.ArrayList;
import java.util.List;

import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

public class RestPageResponse<T> extends PageImpl<T> {

    private static final long serialVersionUID = 5835593096562217592L;

    private Sort sort;

    public RestPageResponse() {
        super(new ArrayList<T>());
    }

    public RestPageResponse(List<T> content) {
        super(content);
    }

    public RestPageResponse(List<T> content, Pageable pageable, long total) {
        super(content, pageable, total);
    }

    /*
     * https://stackoverflow.com/questions/34647303/spring-resttemplate-with-paginated-api
     */
    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
    public RestPageResponse(//
        @JsonProperty("content") List<T> content, // PageImpl
        @JsonProperty("number") int number, // PageImpl
        @JsonProperty("size") int size, // PageImpl
        @JsonProperty("totalElements") long totalElements, // PageImpl
        @JsonProperty("pageable") JsonNode pageable, //
        @JsonProperty("sort") JsonNode sort, //
        @JsonProperty("totalPages") int totalPages, // computed
        @JsonProperty("first") boolean first, // computed
        @JsonProperty("last") boolean last, // computed
        @JsonProperty("empty") boolean empty, // computed
        @JsonProperty("numberOfElements") int numberOfElements // computed
    ) {
    super(content, PageRequest.of(number, size), totalElements);
    }

    @Override
    public Sort getSort() {
        return sort;
    }

    @JsonDeserialize(using = CustomSortDeserializer.class)
    public void setSort(Sort sort) {
        this.sort = sort;
    }

}

这是自定义的反序列化程序,归入https://blog.thecookinkitchen.com/how-to-consump-page-respons-from-a-service-in-spring-boot-97293c18ba

import java.io.IOException;

import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;

public class CustomSortDeserializer extends JsonDeserializer<Sort> {
    @Override
    public Sort deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
        ArrayNode node = jp.getCodec().readTree(jp);
        Order[] orders = new Order[node.size()];
        int i = 0;
        for (JsonNode obj : node) {
            orders[i] = new Order(Direction.valueOf(obj.get("direction").asText()), obj.get("property").asText());
            i++;
        }
        return Sort.by(orders);
    }
}

下面是一个JUnit测试类,它包含Spring-Boot1.5响应和2.1响应的案例,它们是非常不同的:

import java.lang.invoke.MethodHandles;

import org.acumos.cds.transport.RestPageResponse;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class DeserializerTest {

    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    /*
     * Content emitted by Spring-Boot v1.5.x REST endpoint
     */
    @SuppressWarnings("rawtypes")
    @Test
    public void testRestPageResponse15() {
        String springBoot15 = "{" //
                + " \"content\":[\"1\",\"2\",\"3\"],\"first\":true,\"last\":false, " //
                + " \"number\":0,\"numberOfElements\":3,\"size\":3,\"totalElements\":90,\"totalPages\":30, " //
                + " \"sort\":[{\"direction\":\"ASC\",\"property\":\"name\",\"ignoreCase\":false,\"nullHandling\":\"NATIVE\",\"ascending\":true,\"descending\":false}] "//
                + " }";
        try {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode sb15 = mapper.readTree(springBoot15);
            logger.info("SpringBoot 1.5 JSON {}", sb15.toString());
            RestPageResponse r = mapper.readValue(springBoot15, RestPageResponse.class);
            logger.info("Parsed JSON {}", mapper.writeValueAsString(r));
            Assert.assertNotNull(r.getContent());
            Assert.assertEquals(r.getContent().size(), 3);
            Assert.assertTrue(r.isFirst());
            Assert.assertFalse(r.isLast());
            Assert.assertEquals(r.getNumber(), 0);
            Assert.assertEquals(r.getNumberOfElements(), 3);
            Assert.assertEquals(r.getSize(), 3);
            Assert.assertEquals(r.getTotalPages(), 30);
            Assert.assertEquals(r.getTotalElements(), 90);
        } catch (Exception ex) {
            logger.error("testRestPageResponse15 failed", ex);
        }
    }

    /*
     * Content emitted by Spring-Boot v2.1.0 REST endpoint
     */
    @SuppressWarnings("rawtypes")
    @Test
    public void testRestPageResponse21() {
        String springBoot21 = "{"//
                + " \"content\":[\"4\",\"5\",\"6\"], \"empty\": false, "//
                + " \"pageable\": { \"sort\": {\"sorted\": false, \"unsorted\": true, \"empty\": true },"
                + " \"offset\": 0, \"pageSize\": 20, \"pageNumber\": 0, \"unpaged\": false, \"paged\": true },"
                + " \"last\": true, \"totalPages\": 1, \"totalElements\": 1, \"size\": 20, \"number\": 0, "
                + " \"numberOfElements\": 1,  \"first\": true, \"sort\": { \"sorted\": false, \"unsorted\": true, \"empty\": true }"
                + "}";
        try {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode sb21 = mapper.readTree(springBoot21);
            logger.info("SpringBoot 2.1 JSON {}", sb21.toString());
            RestPageResponse s = mapper.readValue(springBoot21, RestPageResponse.class);
            logger.info("Parsed JSON {}", mapper.writeValueAsString(s));
            Assert.assertNotNull(s.getContent());
            Assert.assertEquals(s.getContent().size(), 3);
            Assert.assertTrue(s.isFirst());
            Assert.assertTrue(s.isLast());
            Assert.assertEquals(s.getNumber(), 0);
            Assert.assertEquals(s.getNumberOfElements(), 3);
            Assert.assertEquals(s.getSize(), 20);
            Assert.assertEquals(s.getTotalPages(), 1);
            Assert.assertEquals(s.getTotalElements(), 3);

        } catch (Exception ex) {
            logger.error("testRestPageResponse21 failed", ex);
        }
    }

}

这怎么这么难?https://jira.spring.io/browse/datacmns-1061上的spring人员说“使用HateoAs”,这一点我完全没有弄明白。提前道谢。

共有1个答案

金珂
2023-03-14

这是我所做的使排序工作。

类,该类使用Rest模板来使用API

package br.com.ironforge.springboottutorial.javaclient;

import java.util.Arrays;
import java.util.List;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import br.com.ironforge.springboottutorial.model.PageableResponse;
import br.com.ironforge.springboottutorial.model.Student;

public class JavaSpringClientTest {

    public static void main(String[] args) {

        RestTemplate restTemplate = new RestTemplateBuilder().rootUri("http://localhost:8080/v1/protected/students")
                .basicAuthentication("username", "password").build();

        ResponseEntity<PageableResponse<Student>> exchange = restTemplate.exchange("/?sort=id,asc", HttpMethod.GET, null,
                new ParameterizedTypeReference<PageableResponse<Student>>() {
                });
        System.out.println(exchange.getBody().getContent());
    }
}

类,用于对API调用进行分页和排序

package br.com.ironforge.springboottutorial.model;

import java.util.ArrayList;
import java.util.List;

import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;


public class PageableResponse<T> extends PageImpl<T> {

    private boolean last;
    private boolean first;
    private int totalPages;

    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
    public PageableResponse(@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 PageableResponse(List<T> content, Pageable pageable, long total) {
        super(content, pageable, total);
    }

    public PageableResponse(List<T> content) {
        super(new ArrayList<T>());
    }

}
 类似资料:
  • 我正在尝试使用RestTemplate使用REST服务。我无法反序列化JSON响应。我正在使用一个自定义反序列化程序,我的JSON有3个节点,但看起来只有一个节点到达反序列化程序。以下是更多详细信息。 以下是JSON的响应: 我正在使用@jsondeselizer对属性Hello的响应类使用自定义反序列化器。 当我执行如下readTree时: 它到达了反序列化方法,看起来它只有一个节点,而不是下面

  • JSON文档有一个属性,其中包含一组对象(也可以是使用属性名称作为键的映射)。这是唯一一个在JSON中完全序列化columns对象的地方。在其他任何地方,列都使用唯一的属性进行引用 引用可用于映射键和值 我想将此文档反序列化并: 解析属性中对应对象的引用 使用相同的java对象实例(列类是不可变的),而不是每次都创建一个新的。(我想减少对象的数量) JsonIdtyInfoesnt不适用于地图键。

  • 问题内容: 我正在使用Flickr API 。调用该方法时,默认的JSON结果为: 我想将此响应解析为Java对象: JSON属性应按以下方式映射: 不幸的是,我无法找到一种使用Annotations做到这一点的好方法。到目前为止,我的方法是将JSON字符串读入a 并从中获取值。 但是我认为,这是有史以来最不优雅的方式。有没有简单的方法,可以使用注释还是自定义反序列化器? 这对我来说将是很明显的,

  • 我想通过扩展默认的反序列化器来创建自己的反序列化器,在其后面设置更多的值: 如您所见,我还想将此DTO母类重用于其他DTO。 我没有找到任何这样的例子。我真的是世界上第一个 反序列化的“AsUsual”(p,ctxt)应该是什么 我应该使用什么motherclass?JsonDeserializer/StdDeserializer/UntypedObjectDeserializer 反序列化程序会

  • 我想反序列化表单中的类: 其中文本是加密的,反序列化应该在重建TestFieldEncryptedMessage实例之前取消对值的加密。 我采用的方法非常类似于:https://github.com/codesqueak/jackson-json-crypto 也就是说,我正在构建一个扩展SimpleModule的模块: 如您所见,设置了两个修饰符:EncryptedSerializerModif

  • 我有一个Spring项目,我尝试添加一个自定义反序列化器来反序列化日期属性,具体取决于它们的格式。如果我将其用作Date属性的注释,则效果很好。但是,如果我将反序列化器添加到对象映射器中,当Jackson反序列化日期时,它不会调用。 我尝试这样应用我的自定义反序列化程序: 我不想每次都对Date属性应用注释,我想默认使用此反序列化器。我做错了什么?