Sono in grado di serializzare e deserializzare una gerarchia di classi in cui la classe astratta di base è annotata conJackson's @JsonSubTypes è ancora necessario per la deserializzazione polimorfica?
@JsonTypeInfo(
use = JsonTypeInfo.Id.MINIMAL_CLASS,
include = JsonTypeInfo.As.PROPERTY,
property = "@class")
ma non @JsonSubTypes
elencando le sottoclassi, e le sottoclassi stessi sono relativamente non annotate, avendo solo una @JsonCreator
sul costruttore. ObjectMapper è vanilla e non sto usando un mixin.
Jackson documentazione su PolymorphicDeserialization and "type ids" suggerisce (fortemente) Ho bisogno del @JsonSubTypes
annotazioni sulla classe di base astratta, o utilizzarlo su un mixin, o che ho bisogno di register the subtypes with the ObjectMapper. E ci sono un sacco di domande SO e/o post sul blog che sono d'accordo. Eppure funziona. (Questo è Jackson 2.6.0.)
Quindi ... sono io il beneficiario di una caratteristica non ancora privi di documenti o sto contando sul comportamento privi di documenti (che può cambiare) o è qualcos'altro? (Le sto chiedendo perché non voglio che sia tra le ultime due. Ma I gots to know.)
MODIFICA: aggiunta di codice e un commento. Il commento è: avrei dovuto dire che tutte le sottoclassi che sto deserializzando sono nello stesso pacchetto e nello stesso vaso della classe astratta base.
classe astratta di base:
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);
}
Una sottoclasse di esso:
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();
}
}
sottoclassi SubB
e SubC
sono gli stessi, tranne quel campo a
è (non int
) ha dichiarato String
in SubB
e boolean
(non int
) in SubC
(e il metodo getA
è modifi ed di conseguenza).
prova:
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);
}
}
Questo test viene superato e se si rompe la seconda riga try {
è possibile esaminare actual1
e vedere:
{"pb1":{"@class":".SubA","a":5},
"pb2":{"@class":".SubB","a":"foobar"},
"pb3":{"@class":".SubC","a":true}}
Così i tre sottoclassi ricevuti correttamente serializzati (ognuno con il loro nome di classe come id) e quindi deserializzato e il risultato è paragonato (ogni sottoclasse ha un "tipo di valore" equals()
).
Buona domanda, buon esempio. Mostra una buona ricerca. Ne hai bisogno di più. –