ObjectIdWithTypeInfo4014Test.java
package tools.jackson.databind.objectid;
import java.util.List;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.*;
import tools.jackson.databind.*;
import tools.jackson.databind.testutil.DatabindTestUtil;
import static org.junit.jupiter.api.Assertions.*;
/**
* Test for [databind#4014]: combining {@code @JsonTypeInfo(include = As.PROPERTY)}
* with {@code @JsonIdentityInfo(generator = PropertyGenerator.class)} on an interface
* should work for deserialization.
*/
public class ObjectIdWithTypeInfo4014Test extends DatabindTestUtil
{
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@c")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "@id")
public interface BaseEntity {
@JsonProperty("@id")
Integer getId();
}
// Immutable classes: constructor-only @id, no setter
static class Foo implements BaseEntity {
private final Integer id;
private Bar bar;
@JsonCreator
public Foo(@JsonProperty("@id") Integer id) {
this.id = id;
}
@Override @JsonProperty("@id") public Integer getId() { return id; }
public Bar getBar() { return bar; }
public void setBar(Bar bar) { this.bar = bar; }
}
static class Bar implements BaseEntity {
private final Integer id;
private Foo foo;
@JsonCreator
public Bar(@JsonProperty("@id") Integer id) {
this.id = id;
}
@Override @JsonProperty("@id") public Integer getId() { return id; }
public Foo getFoo() { return foo; }
public void setFoo(Foo foo) { this.foo = foo; }
}
// Mutable classes: default constructor + setters
static class FooWithSetter implements BaseEntity {
private Integer id;
private BarWithSetter bar;
public FooWithSetter() { }
@Override @JsonProperty("@id") public Integer getId() { return id; }
@JsonProperty("@id") public void setId(Integer id) { this.id = id; }
public BarWithSetter getBar() { return bar; }
public void setBar(BarWithSetter bar) { this.bar = bar; }
}
static class BarWithSetter implements BaseEntity {
private Integer id;
private FooWithSetter foo;
public BarWithSetter() { }
@Override @JsonProperty("@id") public Integer getId() { return id; }
@JsonProperty("@id") public void setId(Integer id) { this.id = id; }
public FooWithSetter getFoo() { return foo; }
public void setFoo(FooWithSetter foo) { this.foo = foo; }
}
// Wrapper-array variants: exercises AsArrayTypeDeserializer fix
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY, property = "@c")
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "@id")
public interface WrapperArrayEntity {
@JsonProperty("@id")
Integer getId();
}
static class WrapFoo implements WrapperArrayEntity {
private final Integer id;
private WrapBar bar;
@JsonCreator
public WrapFoo(@JsonProperty("@id") Integer id) { this.id = id; }
@Override @JsonProperty("@id") public Integer getId() { return id; }
public WrapBar getBar() { return bar; }
public void setBar(WrapBar bar) { this.bar = bar; }
}
static class WrapBar implements WrapperArrayEntity {
private final Integer id;
private WrapFoo foo;
@JsonCreator
public WrapBar(@JsonProperty("@id") Integer id) { this.id = id; }
@Override @JsonProperty("@id") public Integer getId() { return id; }
public WrapFoo getFoo() { return foo; }
public void setFoo(WrapFoo foo) { this.foo = foo; }
}
// // // [databind#5872]: ObjectId with type info, interface/class base type, +/- builder
static class Container5872 {
@JsonProperty
public List<Base5872> list;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonSubTypes({@JsonSubTypes.Type(name = "Derived", value = Derived5872.class)})
@JsonIdentityInfo(generator = ObjectIdGenerators.StringIdGenerator.class)
interface Base5872 {
}
static class Derived5872 implements Base5872 {
@JsonProperty
public String a;
}
static class ContainerWithClass5872 {
@JsonProperty
public List<BaseClass5872> list;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonSubTypes({@JsonSubTypes.Type(name = "DerivedFromClass", value = DerivedFromClass5872.class)})
@JsonIdentityInfo(generator = ObjectIdGenerators.StringIdGenerator.class)
static class BaseClass5872 {
}
static class DerivedFromClass5872 extends BaseClass5872 {
@JsonProperty
public String a;
}
static class ContainerInterfaceWithBuilder5872 {
@JsonProperty
public List<BaseWithBuilder5872> list;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonSubTypes({@JsonSubTypes.Type(name = "DerivedWithBuilder", value = DerivedWithBuilder5872.class)})
@JsonIdentityInfo(generator = ObjectIdGenerators.StringIdGenerator.class,
resolver = SimpleObjectIdResolver.class)
interface BaseWithBuilder5872 {
}
@tools.jackson.databind.annotation.JsonDeserialize(builder = DerivedWithBuilder5872.DerivedBuilder.class)
static class DerivedWithBuilder5872 implements BaseWithBuilder5872 {
@JsonProperty
public String a;
DerivedWithBuilder5872(String a) {
this.a = a;
}
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
public static class DerivedBuilder {
private String a;
@JsonProperty
public DerivedBuilder a(String a) {
this.a = a;
return this;
}
public DerivedWithBuilder5872 build() {
return new DerivedWithBuilder5872(this.a);
}
}
}
static class ContainerClassWithBuilder5872 {
@JsonProperty
public List<BaseClassWithBuilder5872> list;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME)
@JsonSubTypes({@JsonSubTypes.Type(name = "DerivedClassBuilder", value = DerivedClassWithBuilder5872.class)})
@JsonIdentityInfo(generator = ObjectIdGenerators.StringIdGenerator.class,
resolver = SimpleObjectIdResolver.class)
static class BaseClassWithBuilder5872 {
}
@tools.jackson.databind.annotation.JsonDeserialize(builder = DerivedClassWithBuilder5872.DerivedBuilder.class)
static class DerivedClassWithBuilder5872 extends BaseClassWithBuilder5872 {
@JsonProperty
public String a;
DerivedClassWithBuilder5872(String a) {
this.a = a;
}
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
public static class DerivedBuilder {
private String a;
@JsonProperty
public DerivedBuilder a(String a) {
this.a = a;
return this;
}
public DerivedClassWithBuilder5872 build() {
return new DerivedClassWithBuilder5872(this.a);
}
}
}
private final ObjectMapper MAPPER = newJsonMapper();
// [databind#4014]: simple round-trip via interface (setter-based)
@Test
public void testSimpleDeserializationViaInterface() throws Exception
{
FooWithSetter foo = new FooWithSetter();
foo.setId(1);
String json = MAPPER.writeValueAsString(foo);
BaseEntity result = MAPPER.readValue(json, BaseEntity.class);
assertNotNull(result);
assertInstanceOf(FooWithSetter.class, result);
assertEquals(1, ((FooWithSetter) result).getId());
}
// [databind#4014]: simple round-trip via interface (constructor-based)
@Test
public void testSimpleDeserializationWithCreator() throws Exception
{
Foo foo = new Foo(1);
String json = MAPPER.writeValueAsString(foo);
BaseEntity result = MAPPER.readValue(json, BaseEntity.class);
assertNotNull(result);
assertInstanceOf(Foo.class, result);
assertEquals(1, ((Foo) result).getId());
}
// [databind#4014]: circular references with setter-based classes
@Test
public void testCircularReferenceWithSetters() throws Exception
{
FooWithSetter foo = new FooWithSetter();
foo.setId(1);
BarWithSetter bar = new BarWithSetter();
bar.setId(2);
foo.setBar(bar);
bar.setFoo(foo);
String json = MAPPER.writeValueAsString(foo);
BaseEntity result = MAPPER.readValue(json, BaseEntity.class);
assertNotNull(result);
assertInstanceOf(FooWithSetter.class, result);
FooWithSetter resultFoo = (FooWithSetter) result;
assertEquals(1, resultFoo.getId());
assertNotNull(resultFoo.getBar());
assertEquals(2, resultFoo.getBar().getId());
assertSame(resultFoo, resultFoo.getBar().getFoo());
}
// [databind#4014]: circular references with constructor-based classes
@Test
public void testCircularReferenceWithCreator() throws Exception
{
Foo foo = new Foo(1);
Bar bar = new Bar(2);
foo.setBar(bar);
bar.setFoo(foo);
String json = MAPPER.writeValueAsString(foo);
BaseEntity result = MAPPER.readValue(json, BaseEntity.class);
assertNotNull(result);
assertInstanceOf(Foo.class, result);
Foo resultFoo = (Foo) result;
assertEquals(1, resultFoo.getId());
assertNotNull(resultFoo.getBar());
assertEquals(2, resultFoo.getBar().getId());
assertSame(resultFoo, resultFoo.getBar().getFoo());
}
// [databind#4014]: simple round-trip with WRAPPER_ARRAY
@Test
public void testSimpleDeserializationWrapperArray() throws Exception
{
WrapFoo foo = new WrapFoo(1);
String json = MAPPER.writeValueAsString(foo);
WrapperArrayEntity result = MAPPER.readValue(json, WrapperArrayEntity.class);
assertNotNull(result);
assertInstanceOf(WrapFoo.class, result);
assertEquals(1, ((WrapFoo) result).getId());
}
// [databind#4014]: circular references with WRAPPER_ARRAY
@Test
public void testCircularReferenceWrapperArray() throws Exception
{
WrapFoo foo = new WrapFoo(1);
WrapBar bar = new WrapBar(2);
foo.setBar(bar);
bar.setFoo(foo);
String json = MAPPER.writeValueAsString(foo);
WrapperArrayEntity result = MAPPER.readValue(json, WrapperArrayEntity.class);
assertNotNull(result);
assertInstanceOf(WrapFoo.class, result);
WrapFoo resultFoo = (WrapFoo) result;
assertEquals(1, resultFoo.getId());
assertNotNull(resultFoo.getBar());
assertEquals(2, resultFoo.getBar().getId());
assertSame(resultFoo, resultFoo.getBar().getFoo());
}
/*
/**********************************************************
/* Unit tests, ObjectId + type info + interface/class base [databind#5872]
/**********************************************************
*/
// [databind#5872]: ObjectId with interface base type (no builder)
@Test
public void testObjectIdWithInterfaceBaseType5872() throws Exception
{
String json = a2q("{'list':["
+ "{'@type':'Derived','@id':'id1','a':'foo'},"
+ "'id1'"
+ "]}");
Container5872 container = MAPPER.readValue(json, Container5872.class);
assertNotNull(container);
assertNotNull(container.list);
assertEquals(2, container.list.size());
Base5872 first = container.list.get(0);
assertEquals(Derived5872.class, first.getClass());
assertEquals("foo", ((Derived5872) first).a);
Base5872 second = container.list.get(1);
assertSame(first, second);
}
// [databind#5872]: ObjectId with class base type (no builder)
@Test
public void testObjectIdWithClassBaseType5872() throws Exception
{
String json = a2q("{'list':["
+ "{'@type':'DerivedFromClass','@id':'id1','a':'foo'},"
+ "'id1'"
+ "]}");
ContainerWithClass5872 container = MAPPER.readValue(json, ContainerWithClass5872.class);
assertNotNull(container);
assertNotNull(container.list);
assertEquals(2, container.list.size());
BaseClass5872 first = container.list.get(0);
assertEquals(DerivedFromClass5872.class, first.getClass());
assertEquals("foo", ((DerivedFromClass5872) first).a);
BaseClass5872 second = container.list.get(1);
assertSame(first, second);
}
// [databind#5872]: ObjectId with interface base type + builder
@Test
public void testObjectIdWithInterfaceAndBuilder5872() throws Exception
{
String json = a2q("{'list':["
+ "{'@type':'DerivedWithBuilder','@id':'id1','a':'foo'},"
+ "'id1'"
+ "]}");
ContainerInterfaceWithBuilder5872 container = MAPPER.readValue(json, ContainerInterfaceWithBuilder5872.class);
assertNotNull(container);
assertNotNull(container.list);
assertEquals(2, container.list.size());
BaseWithBuilder5872 first = container.list.get(0);
assertEquals(DerivedWithBuilder5872.class, first.getClass());
assertEquals("foo", ((DerivedWithBuilder5872) first).a);
BaseWithBuilder5872 second = container.list.get(1);
assertSame(first, second);
}
// [databind#5872]: ObjectId with class base type + builder
@Test
public void testObjectIdWithClassAndBuilder5872() throws Exception
{
String json = a2q("{'list':["
+ "{'@type':'DerivedClassBuilder','@id':'id1','a':'foo'},"
+ "'id1'"
+ "]}");
ContainerClassWithBuilder5872 container = MAPPER.readValue(json, ContainerClassWithBuilder5872.class);
assertNotNull(container);
assertNotNull(container.list);
assertEquals(2, container.list.size());
BaseClassWithBuilder5872 first = container.list.get(0);
assertEquals(DerivedClassWithBuilder5872.class, first.getClass());
assertEquals("foo", ((DerivedClassWithBuilder5872) first).a);
BaseClassWithBuilder5872 second = container.list.get(1);
assertSame(first, second);
}
}