Could not read JSON:Cannot construct instance of org.springframework.data.domain.PageImpl (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

2024. 8. 25. 10:59·Backend/Redis

두둥.. 에러가 발생했습니다. 발생한 이유는 org.springframework.data.domain.PageImpl 객체를 JSON으로 직렬화하거나 역직렬화하는 과정에서 발생하는 문제였습니다. 특히, Spring에서 캐싱된 Page 객체를 다시 사용할 때 나타나는 문제로, PageImpl 클래스가 기본 생성자나 적절한 생성자를 제공하지 않아 Jackson이 객체를 역직렬화할 수 없기 때문이었습니다.

 

 

Page 인터페이스를 구현한 PageImpl 클래스는 기본 생성자가 없으며, Jackson이나 다른 JSON 라이브러리가 객체를 역직렬화할 때 기본 생성자가 필요했고, 이 경우 캐시에서 꺼낸 데이터가 역직렬화되지 않으면서 예외가 발생한 것이었습니다. PageImpl의 기본 생성자도 신경써줘야 하다니 !! 문제를 해결하기 위해서, 두 가지 방법을 찾았습니다. 하나는 Page 객체가 아닌 DTO로 변환하여 캐시 데이터를 저장하는 것. 나머지는 PageImpl 객체를 JSON으로 직렬화/역직렬화할 수 있도록 커스텀 설정을 추가하는 방법이 있었고, 기존에 제공하고 있는 API를 변경할 수가 없었기 때문에 후자를 선택했습니다.

 

 

@JsonIgnoreProperties 어노테이션은 com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "empty” 와 같은 에러를 해결하기 위해 작성하였습니다.

자세한 내용은 아래 글을 확인해보세요!

https://stackoverflow.com/questions/4486787/jackson-with-json-unrecognized-field-not-marked-as-ignorable

 

Jackson with JSON: Unrecognized field, not marked as ignorable

I need to convert a certain JSON string to a Java object. I am using Jackson for JSON handling. I have no control over the input JSON (I read from a web service). This is my input JSON: {"wrapper"...

stackoverflow.com

 

 

CustomPageImpl

@JsonIgnoreProperties(ignoreUnknown = true)
public class CustomPageImpl<T> extends PageImpl<T> {

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

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

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

 

 

SearchRepositoryImpl

public class SearchRepositoryImpl implements SearchRepositoryCustom {

    private final JPAQueryFactory queryFactory;

    public SearchRepositoryImpl(EntityManager em) {
        this.queryFactory = new JPAQueryFactory(em);
    }

    @Override
    public Page<Instance> search(Progress progressCond, String titleCond, Pageable pageable) {
        BooleanBuilder builder = new BooleanBuilder();

        if (progressCond != null) {
            builder.and(instance.progress.eq(progressCond));
        }
        if (titleCond != null) {
            builder.and(instance.title.contains(titleCond));
        }

        List<Instance> content = queryFactory
                .selectFrom(instance)
                .where(builder)
                .orderBy(instance.startedDate.desc())
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .fetch();

        JPAQuery<Long> countQuery = queryFactory
                .select(instance.count())
                .from(instance)
                .where(builder);

        return new CustomPageImpl<>(content, pageable, countQuery.fetchOne());

    }
}

 

 

새로 적용한 CustomPageImpl에서 직렬화, 역직렬화가 정상적으로 작동되는 지 아래와 같이 테스트해보았습니다.

public class CustomPageImplTest {

	private final ObjectMapper objectMapper = new ObjectMapper();

	@Test
	public void testCustomPageImplSerialization() throws JsonProcessingException {
		List<String> data = List.of("item1", "item2", "item3");
		PageRequest pageRequest = PageRequest.of(0, 10);

		CustomPageImpl<String> customPage = new CustomPageImpl<>(data, pageRequest, 3L);

		// CustomPageImpl 객체를 JSON으로 직렬화
		String json = objectMapper.writeValueAsString(customPage);

		System.out.println("Serialized JSON: " + json);

		// JSON 문자열을 다시 CustomPageImpl 객체로 역직렬화
		CustomPageImpl deserializedPage = objectMapper.readValue(json, CustomPageImpl.class);

		assertNotNull(deserializedPage);
		assertEquals(customPage.getContent(), deserializedPage.getContent());
		assertEquals(customPage.getTotalElements(), deserializedPage.getTotalElements());
		assertEquals(customPage.getPageable().getPageNumber(), deserializedPage.getPageable().getPageNumber());
	}
}

 

 

 

성공 ! 레디스 캐시를 적용하면서 많은 이슈들을 마주하고 있는데요. 참 재밌습니다 하하하하 !

 

 

 

 

 

 


참고 자료 

  • https://stackoverflow.com/questions/55965523/error-during-deserialization-of-pageimpl-cannot-construct-instance-of-org-spr
  • https://stackoverflow.com/questions/52490399/spring-boot-page-deserialization-pageimpl-no-constructor

 

 

 

 

 

 

 

 

'Backend/Redis' 카테고리의 다른 글
  • com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type java.time.LocalDateTime not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
  • Docker Redis 컨테이너 실행 시 redis.conf 파일 생성 오류
kimdozzi
kimdozzi
끝까지 포기하지 않으면, 내가 다 이겨!
  • kimdozzi
    도브로
    kimdozzi
  • 전체
    오늘
    어제
    • 분류 전체보기 (132)
      • Problem Solving (49)
        • Baekjoon (29)
        • Programmers (0)
        • LeetCode (17)
        • 삼성 유형 (2)
      • Computer Science (27)
        • Operating System (2)
        • Algorithms (13)
        • Network (6)
        • DataBase (6)
      • Backend (33)
        • JavaScript (0)
        • TypeScript (6)
        • Java (7)
        • Spring Boot (7)
        • Spring Security (6)
        • JPA (2)
        • Mybatis (1)
        • Junit5 (1)
        • Redis (3)
      • DevOps (14)
        • Git, Github (5)
        • docker (4)
        • AWS (3)
        • nginx (2)
      • etc (6)
        • IntelliJ (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 티스토리
    • 설정
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    알고리즘
    컨테이너
    imos법
    타입스크립트
    인터페이스
    PrefixSum
    오블완
    S3
    백준
    오프라인 쿼리
    점 업데이트
    구간합
    TypeScript
    python
    온라인 쿼리
    docker
    파이썬
    Bucket
    누적합
    도커
    AWS
    구간 업데이트
    세그먼트 트리
    docker image
    interface
    segment tree
    인덱서블 타입
    삼성기출
    CORS
    인덱스 시그니처
    티스토리챌린지
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
kimdozzi
Could not read JSON:Cannot construct instance of org.springframework.data.domain.PageImpl (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
상단으로

티스토리툴바