새소식

반응형
Java

[Java] 다형성 serialize (jackson, JsonTypeInfo)

  • -
반응형

출처 : https://www.javatpoint.com/jackson

 

코드 내부에서 java의 다형성 특징을 이용해서 abstract, interface를 이용해 기능을 작성하는 경우가 있다.

이러한 경우에 하나의 application 내에서만 호출되는 관계가 아니고, 다른 application에 메시지로 전달되거나 redis와 같은 storage에 저장되는 경우 object를 string이나 byte로 변환하는 과정이 필요하게 된다. 

 

해당 과정에서 abstract, interface를 사용하게 되면 실제로 deserialize 되는 시점에 해당 정보가 어떠한 concrete class에 매핑되는지에 대한 정보가 필요하게 된다. 

 

이러한 구체적인 정보가 없이는 serialize 된 정보가 다시 객체로 변환되기는 불가능하다.

 

@JsonTypeInfo를 사용하면 이러한 문제를 해결할 수 있다.

 

@JsonTypeInfo

해당 애너테이션의 어떠한 정보를 사용해서 serialize, deserialize 할 것인지 다양한 옵션을 제공한다.

이번에 알아볼 옵션은 아래의 3가지이다.

  • use
    • Id.CLASS
    • Id.NAME
    • Id.DEDUCTION

각각의 옵션을 알아보기 전에 테스트를 위한 다형성 코드 

class Test {
    static abstract class Shape {
        public String type;
    }

    @Data
    @NoArgsConstructor
    static class Circle extends Shape {
        String a;
        public Circle(String type) {
            this.type = type;
            this.a = "aa";
        }
    }
}

 

@JsonTypeInfo(use = Id.CLASS)

가장 간단하다.

class명으로 구체 클래스를 구분하겠다고 명시해 주면 된다.

class Test {

    @JsonTypeInfo(use = Id.CLASS)
    static abstract class Shape {
        public String type;
    }
    // ...
}

 

다만, serialize 된 정보에 full class명이 남게 된다.

{"@class":"com.api.test.Test$Circle","type":"c","a":"aa"}

 

 

@JsonTypeInfo(use = Id.NAME)

name이라는 특정 property를 지정해 타입을 구분 

기존 정의된 class에 타입을 구분할 만한 필드가 있는 경우와 그렇지 않은 경우(새로운 필드 추가하여 구분)로 나눌 수 있다.

기존 정의된 class에 타입을 구분할 만한 필드가 있는 경우 (include = As.EXISTING_PROPERTY)

    @JsonTypeInfo(use = Id.NAME, include = As.EXISTING_PROPERTY, property = "type")
    @JsonSubTypes({@Type(name = "cc", value = Circle.class),@Type(name = "rr", value = Rectangle.class)})
    static abstract class Shape {
        public String type;
    }
    
    @Data
    @NoArgsConstructor
    static class Circle extends Shape {
        // .. 
    }
    
    @Test
    void test() {
        Shape s = new Circle("cc");

        ObjectMapper mapper = new ObjectMapper();
        try {
            String result1 = mapper.writeValueAsString(s);
            System.out.println(result1);

            Circle circle = (Circle) mapper.readValue(result1, Shape.class);
            System.out.println(circle);

        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

 

{"type":"rr","b":"bb"}

 

새로운 필드 추가하여 구분

위에서 정의한 include의 값이 default로 As.PROPERTY로 따로 추가하지 않았다.(include = As.PROPERTY)

    @JsonTypeInfo(use = Id.NAME, property = "newType")
    @JsonSubTypes({@Type(name = "cc", value = Circle.class),@Type(name = "rr", value = Rectangle.class)})
    static abstract class Shape {
        public String type;
    }
    
    @Data
    @NoArgsConstructor
    static class Circle extends Shape {
        // .. 
    }

 

{"newType":"cc","type":"cc","a":"aa"}

 

@JsonTypeInfo(use = Id.DEDUCTION)

각각의 concrete class에 서로를 구분할 만한 타입이 존재한다면

jackson에서 추론하여 변환해 주는 설정 

 

Circle, Rectancle의 생성자와 같이 각각을 구분할 수 있을 만한 

타입이 다른 구분자가 존재하여야 동작한다.

    @JsonTypeInfo(use = Id.DEDUCTION)
    @JsonSubTypes({@Type(Circle.class), @Type(Rectangle.class)})
    static abstract class Shape {
        public String type;
    }
    
    @Data
    @NoArgsConstructor
    static class Circle extends Shape {

        String a;

        public Circle(String type, String cc) {
            this.type = type;
            this.a = cc;
        }
    }

    @Data
    @NoArgsConstructor
    static class Rectangle extends Shape {

        int b;

        public Rectangle(String type, int rr) {
            this.type = type;
            this.b = rr;
        }
    }

 

반응형

'Java' 카테고리의 다른 글

[Java] JVM 옵션 정리  (0) 2024.04.21
[Java] ForkJoinPool 에러 발생 in Java11  (1) 2024.03.11
[Java] ForkJoinPool이란 (ParellelStream)  (0) 2023.12.21
[JAVA] Java8 변경 사항 ( java7 to java8)  (0) 2023.11.25
[Java] java8 Time 관련 정리  (0) 2023.08.20
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.