Ist Jacksons @JsonSubTypes noch für die polymorphe Deserialisierung notwendig?

Ich kann eine Klassenhierarchie serialisieren und deserialisieren, in der die abstrakte Basisklasse mit @ beschriftet is

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

aber nein@JsonSubTypes Auflistung der Unterklassen, und die Unterklassen selbst sind relativ unkommentiert und haben nur ein@JsonCreator auf dem Konstruktor. Der ObjectMapper ist Vanille und ich verwende kein Mixin.

Jackson Dokumentation aufPolymorphicDeserialization und "Typ-IDs" schlägt vor (stark) ich brauche das@JsonSubTypes Annotation auf der abstrakten Basisklasse, oder verwenden Sie es auf einem Mixin, oder das muss ich Registrieren Sie die Untertypen mit dem ObjectMapper. Und es gibt viele SO-Fragen und / oder Blog-Posts, die übereinstimmen. Trotzdem funktioniert es. Das ist Jackson 2.6.0.)

So ... bin ich der Nutznießer eines noch nicht dokumentierten Features oder verlasse ich mich auf nicht dokumentiertes Verhalten (das sich möglicherweise ändert) oder ist etwas anderes im Gange? (Ich frage, weil ich wirklich nicht möchte, dass es einer der beiden letzteren ist. AberIch muss wissen.)

EDIT: Code hinzufügen - und einen Kommentar. Der Kommentar lautet: Ich hätte erwähnen sollen, dass sich alle Unterklassen, die ich deserialisiere, im selben Paket und im selben Jar wie die abstrakte Basisklasse befinden.

Abstrakte Basisklasse:

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);
}

Eine Unterklasse davon:

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();
    }
}

SubclassesSubB undSubC sind bis auf das Feld @ gleia wird zu @ erkläString (nichtint) imSubB undboolean (nichtint) imSubC (und die MethodegetA wird entsprechend geändert).

Testklasse:

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);
    }
}

Dieser Test besteht und wenn Sie bei der zweiten Pausetry { Linie, die Sie inspizieren könnenactual1 und sehen

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

So wurden die drei Unterklassen richtig serialisiert (jede mit ihrem Klassennamen als id) und dann deserialisiert und das Ergebnis gleich verglichen (jede Unterklasse hat einen "Werttyp"equals()).

Antworten auf die Frage(2)

Ihre Antwort auf die Frage