source

Jackson의 @JsonSubTypes는 다형성 디시리얼라이제이션에 여전히 필요한가요?

bestscript 2023. 3. 21. 22:15

Jackson의 @JsonSubTypes는 다형성 디시리얼라이제이션에 여전히 필요한가요?

추상 기본 클래스가 주석으로 표시되는 클래스 계층을 직렬화 및 역직렬화할 수 있다.

@JsonTypeInfo(
    use = JsonTypeInfo.Id.MINIMAL_CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class")

하지만 아니다@JsonSubTypes서브클래스를 나열하고, 서브클래스 자체는 비교적 주석 없이, 오직 1개의 서브클래스만을 가지고 있다.@JsonCreator컨스트럭터 위에 있습니다.Object Mapper는 바닐라로 믹스인을 사용하지 않습니다.

PolymorphicDeserialization 및 "typeids"에 대한 Jackson 문서에서는 (강력하게) 다음 명령어가 필요합니다.@JsonSubTypes오브젝트 맵퍼에 서브타입을 등록해야 하는 경우 등입니다.SO에 관한 질문이나 블로그 투고에도 동의하는 글이 많이 있습니다.그래도 작동한다.(이것이 잭슨 2.6.0)

그렇다면... 저는 아직 문서화되지 않은 기능의 수혜자일까요? 아니면 문서화되지 않은 행동에 의존하고 있을까요? (변화할 수 있는) 아니면 다른 일이 벌어지고 있을까요? (저는 정말로 후자의 두 가지 중 하나가 되지 않기를 바라기 때문에 물어봅니다.)하지만알아야겠어.)

EDIT: 코드 추가 - 코멘트 1개.코멘트:디시리얼라이즈하는 모든 서브클래스는 기본 추상 클래스와 동일한 패키지와 동일한 항아리에 포함되어 있음을 언급했어야 합니다.

추상 기본 클래스:

package so;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
    use = JsonTypeInfo.Id.MINIMAL_CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class")
public abstract class PolyBase
{
    public PolyBase() { }

    @Override
    public abstract boolean equals(Object obj);
}

서브클래스:

package so;
import org.apache.commons.lang3.builder.EqualsBuilder;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public final class SubA extends PolyBase
{
    private final int a;

    @JsonCreator
    public SubA(@JsonProperty("a") int a) { this.a = a; }

    public int getA() { return a; }

    @Override
    public boolean equals(Object obj) {
        if (null == obj) return false;
        if (this == obj) return true;
        if (this.getClass() != obj.getClass()) return false;

        SubA rhs = (SubA) obj;
        return new EqualsBuilder().append(this.a, rhs.a).isEquals();
    }
}

서브클래스SubB그리고.SubC그 필드 이외에는 동일하다.a선언되어 있다String(없음)int)의 경우SubB그리고.boolean(없음)int)의 경우SubC(및 방법)getA에 따라 변경됩니다).

테스트 클래스:

package so;    
import java.io.IOException;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.testng.annotations.Test;
import static org.assertj.core.api.Assertions.*;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;

public class TestPoly
{
    public static class TestClass
    {
        public PolyBase pb1, pb2, pb3;

        @JsonCreator
        public TestClass(@JsonProperty("pb1") PolyBase pb1,
                         @JsonProperty("pb2") PolyBase pb2,
                         @JsonProperty("pb3") PolyBase pb3)
        {
            this.pb1 = pb1;
            this.pb2 = pb2;
            this.pb3 = pb3;
        }

        @Override
        public boolean equals(Object obj) {
            if (null == obj) return false;
            if (this == obj) return true;
            if (this.getClass() != obj.getClass()) return false;

            TestClass rhs = (TestClass) obj;
            return new EqualsBuilder().append(pb1, rhs.pb1)
                                      .append(pb2, rhs.pb2)
                                      .append(pb3, rhs.pb3)
                                      .isEquals();
        }
    }

    @Test
    public void jackson_should_or_should_not_deserialize_without_JsonSubTypes() {

        // Arrange
        PolyBase pb1 = new SubA(5), pb2 = new SubB("foobar"), pb3 = new SubC(true);
        TestClass sut = new TestClass(pb1, pb2, pb3);

        ObjectMapper mapper = new ObjectMapper();

        // Act
        String actual1 = null;
        TestClass actual2 = null;

        try {
            actual1 = mapper.writeValueAsString(sut);
        } catch (IOException e) {
            fail("didn't serialize", e);
        }

        try {
            actual2 = mapper.readValue(actual1, TestClass.class);
        } catch (IOException e) {
            fail("didn't deserialize", e);
        }

        // Assert
        assertThat(actual2).isEqualTo(sut);
    }
}

이 테스트는 통과하고 두 번째에 고장나면try {검사할 수 있는 라인actual1이하를 참조해 주세요.

{"pb1":{"@class":".SubA","a":5},
 "pb2":{"@class":".SubB","a":"foobar"},
 "pb3":{"@class":".SubC","a":true}}

따라서 3개의 서브클래스가 적절히 시리얼화(각 클래스명이 ID로 지정)되었다가 역시리얼화되어 결과가 동등하게 비교되었습니다(각 서브클래스는 「값 타입」이 있습니다).equals()).

잭슨과의 연재화와 역직렬화에는 두 가지 방법이 있다.섹션 1에 정의되어 있습니다. 게시한 링크의 사용 현황입니다.

고객님의 코드

@JsonTypeInfo(
    use = JsonTypeInfo.Id.MINIMAL_CLASS,
    include = JsonTypeInfo.As.PROPERTY,
    property = "@class")

는 두 번째 접근법의 예입니다.가장 먼저 주목해야 할 것은 말이다

주석 유형 및 하위 유형의 모든 인스턴스는 이러한 설정을 사용합니다(다른 주석으로 재정의되지 않는 한).

따라서 이 설정값은 모든 서브타입으로 전파됩니다.그런 다음 Java 유형을 JSON 문자열의 텍스트 값에 매핑하는 유형 식별자가 필요합니다.이 예에서는 다음과 같이 표시됩니다.

최소 경로를 가진 Java 클래스 이름이 유형 식별자로 사용됨을 의미합니다.

따라서 시리얼화 시 타깃인스턴스에서 최소한의 클래스 이름이 생성되어 JSON 콘텐츠에 기록됩니다.또는 최소 클래스 이름을 사용하여 역직렬화 대상 유형을 결정합니다.

또한 어떤 것을 사용할 수도 있었습니다.

논리 타입명이 타입 정보로 사용되는 것을 의미합니다.이름을 실제 콘크리트 타입으로 개별적으로 해결해야 합니다.(Class를 참조해 주세요.

이러한 논리적 유형 이름을 제공하려면

서서 JsonTypeInfo시리얼 가능한 다형성 타입의 서브 타입을 나타내, JSON 컨텐츠내에서 사용되는 논리명을 관련짓습니다(물리 Java 클래스명을 사용하는 것보다 이식성이 우수합니다).

이것은 같은 결과를 얻기 위한 또 다른 방법일 뿐이다.당신이 묻고 있는 문서는

Java 클래스 이름을 기반으로 하는 유형 ID는 매우 간단합니다. 클래스 이름일 뿐이며, 일부 간단한 접두사 제거("minimal" 배리언트일 수 있습니다.그러나 유형 이름은 다릅니다. 논리 이름과 실제 클래스 간의 매핑이 필요합니다.

여러 가지...JsonTypeInfo.Id클래스 이름을 처리하는 값은 자동으로 생성될 수 있기 때문에 간단합니다.그러나 유형 이름의 경우 매핑 값을 명시적으로 지정해야 합니다.

언급URL : https://stackoverflow.com/questions/31665620/is-jacksons-jsonsubtypes-still-necessary-for-polymorphic-deserialization