본문 바로가기

SW 공부

[Jackson] 상속관계 deserializing

@JsonTypeInfo

인터페이스나 추상클래스를 이용하여 다형성을 구현한 경우, 실제 클래스가 무엇인지 알려주는 설정을 하는 어노테이션

json type 이라는 메타정보를 생성하여 사용하고 이 메타정보는 json 에 추가 프로퍼티로 들어간다. 이 추가 프로퍼티는 직렬화에 관여되지 않는다.

속성으로 ID, AS, property 를 갖는데, 이것은 사용예시를 보면서 하나씩 보자.

 

1. @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)

// @class 라는 추가 프로퍼티가 생기고 패키지.클래스명으로 값이 자동으로 세팅됨
// @JsonSubTypes 을 안줘도 자동으로 서브 클래스를 등록한다.(이건 확인 필요)
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
interface Shape

data class Rectangle(
    val w: Int,
    val h: Int
): Shape

data class Circle(
    val radius: Int
): Shape

data class View(
    val shapes: List<Shape>,
    val shape: Shape
)

Serialize 결과
{"shapes":[{"@class":"models.Rectangle","w":10,"h":20},{"@class":"models.Rectangle","w":1000,"h":2000},{"@class":"models.Circle","radius":7}],"shape":{"@class":"models.Circle","radius":777}}

 

2. @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")

// 사용자가 직접 준 이름을 추가 프로퍼티의 키로 사용
// 값은 클래스이름이 자동으로 세팅
// @class 보다 명확함
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")

Serialize 결과
{"shapes":[{"type":"Rectangle","w":10,"h":20},{"type":"Rectangle","w":1000,"h":2000},{"type":"Circle","radius":7}],"shape":{"type":"Circle","radius":777}}

 

3. 기존의 프로퍼티를 그대로 사용

// EXISTING_PROPERTY 으로 설정하고 property 이름을 주면 기존 필드를 이용할 수 있음
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "subType")
@JsonSubTypes(
    JsonSubTypes.Type(Rectangle::class),
    JsonSubTypes.Type(Circle::class),
)
interface Shape{
    val subType: String
}

data class Rectangle(
    val w: Int,
    val h: Int
): Shape {
    override val subType: String = this::class.java.simpleName
}

data class Circle(
    val radius: Int
): Shape {
    override val subType: String = this::class.java.simpleName
}


결과
{"shapes":[{"w":10,"h":20,"subType":"Rectangle"},{"w":1000,"h":2000,"subType":"Rectangle"},{"radius":7,"subType":"Circle"}],"shape":{"radius":777,"subType":"Circle"}}

 

@JsonSubTypes

1. @JsonTypeInfo 외에 서브클래스를 모두 등록하여야 실제 동작하는데, 서브클래스를 알려주기 위해 사용하는 어노테이션

// 자동으로 클래스 이름이 json type 프로퍼티의 값으로 들어간다.
@JsonSubTypes(
    JsonSubTypes.Type(Rectangle::class),
    JsonSubTypes.Type(Circle::class),
)

 

2. 서브클래스의 매핑값을 직접 줄 수도 있다.

enum class ShapeType{
    RECTANGLE,
    CIRCLE;
    
    object Constants {
        const val RECTANGLE = "R"
        const val CIRCLE = "C"
    }
}

// JsonSubType 의 name에 직접 매핑할 값을 넣어주어도 됨
// 단, 이때는 각각의 클래스에 @JsonTypeName 어노테이션을 주어야 함
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes(
    JsonSubTypes.Type(Rectangle::class, name = ShapeType.Constants.RECTANGLE),
    JsonSubTypes.Type(Circle::class, name = ShapeType.Constants.CIRCLE),
)
interface Shape

@JsonTypeName(ShapeType.Constants.RECTANGLE)
data class Rectangle(
    val w: Int,
    val h: Int
): Shape

@JsonTypeName(ShapeType.Constants.CIRCLE)
data class Circle(
    val radius: Int
): Shape

data class View(
    val shapes: List<Shape>,
    val shape: Shape
)


결과
{"shapes":[{"type":"R","w":10,"h":20},{"type":"R","w":1000,"h":2000},{"type":"C","radius":7}],"shape":{"type":"C","radius":777}}

 

Custom Deserializer 

작성중

 

 

kotlinx.serialization

작성중