ExternalTypeIdTest.java
package tools.jackson.databind.jsontype.ext;
import java.math.BigDecimal;
import java.util.*;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.*;
import tools.jackson.databind.cfg.DateTimeFeature;
import tools.jackson.databind.testutil.DatabindTestUtil;
import tools.jackson.databind.testutil.NoCheckSubTypeValidator;
import static org.junit.jupiter.api.Assertions.*;
// Tests for External type id, one that exists at same level as typed Object,
// that is, property is not within typed object but a member of its parent.
public class ExternalTypeIdTest extends DatabindTestUtil
{
static class ExternalBean
{
@JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType")
public Object bean;
public ExternalBean() { }
public ExternalBean(int v) {
bean = new ValueBean(v);
}
}
// for [databind#96]
static class ExternalBeanWithDefault
{
@JsonTypeInfo(use=Id.CLASS, include=As.EXTERNAL_PROPERTY, property="extType",
defaultImpl=ValueBean.class)
public Object bean;
public ExternalBeanWithDefault() { }
public ExternalBeanWithDefault(int v) {
bean = new ValueBean(v);
}
}
static class ExternalBean3
{
@JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType1")
public Object value1;
@JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType2")
public Object value2;
public int foo;
@JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType3")
public Object value3;
public ExternalBean3() { }
public ExternalBean3(int v) {
value1 = new ValueBean(v);
value2 = new ValueBean(v+1);
value3 = new ValueBean(v+2);
foo = v;
}
}
static class ExternalBeanWithCreator
{
@JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType")
public Object value;
public int foo;
@JsonCreator
public ExternalBeanWithCreator(@JsonProperty("foo") int f)
{
foo = f;
value = new ValueBean(f);
}
}
@JsonTypeName("vbean")
static class ValueBean {
public int value;
public ValueBean() { }
public ValueBean(int v) { value = v; }
}
@JsonTypeName("funk")
@JsonTypeInfo(use=Id.NAME, include=As.EXTERNAL_PROPERTY, property="extType")
static class FunkyExternalBean {
public int i = 3;
}
// [JACKSON-798]: problems with polymorphic types, external prop
@JsonSubTypes(value= { @JsonSubTypes.Type(value=Derived1.class, name="d1"),
@JsonSubTypes.Type(value=Derived2.class, name="d2") })
interface Base {
String getBaseProperty();
}
static class Derived1 implements Base {
private String derived1Property;
private String baseProperty;
protected Derived1() { throw new IllegalStateException("wrong constructor called"); }
@JsonCreator
public Derived1(@JsonProperty("derived1Property") String d1p,
@JsonProperty("baseProperty") String bp) {
derived1Property = d1p;
baseProperty = bp;
}
@Override
@JsonProperty public String getBaseProperty() {
return baseProperty;
}
@JsonProperty public String getDerived1Property() {
return derived1Property;
}
}
static class Derived2 implements Base {
private String derived2Property;
private String baseProperty;
protected Derived2() { throw new IllegalStateException("wrong constructor called"); }
@JsonCreator
public Derived2(@JsonProperty("derived2Property") String d2p,
@JsonProperty("baseProperty") String bp) {
derived2Property = d2p;
baseProperty = bp;
}
@Override
@JsonProperty public String getBaseProperty() {
return baseProperty;
}
@JsonProperty public String getDerived2Property() {
return derived2Property;
}
}
static class BaseContainer {
protected final Base base;
protected final String baseContainerProperty;
protected BaseContainer() { throw new IllegalStateException("wrong constructor called"); }
@JsonCreator
BaseContainer(@JsonProperty("baseContainerProperty") String bcp, @JsonProperty("base") Base b) {
baseContainerProperty = bcp;
base = b;
}
@JsonProperty
public String getBaseContainerProperty() { return baseContainerProperty; }
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXTERNAL_PROPERTY, property="type")
@JsonProperty
public Base getBase() { return base; }
}
interface Pet {}
static class Dog implements Pet {
public String name;
}
static class House831 {
protected String petType;
@JsonTypeInfo(use = Id.NAME, include = As.EXTERNAL_PROPERTY, property = "petType", visible = true)
@JsonSubTypes({@JsonSubTypes.Type(name = "dog", value = Dog.class)})
public Pet pet;
public String getPetType() {
return petType;
}
public void setPetType(String petType) {
this.petType = petType;
}
}
// for [databind#118]
static class ExternalTypeWithNonPOJO {
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
property = "type",
visible = true,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
defaultImpl = String.class)
@JsonSubTypes({
@JsonSubTypes.Type(value = Date.class, name = "date"),
@JsonSubTypes.Type(value = AsValueThingy.class, name = "thingy")
})
public Object value;
public ExternalTypeWithNonPOJO() { }
public ExternalTypeWithNonPOJO(Object o) { value = o; }
}
// for [databind#119]
static class AsValueThingy {
public long rawDate;
public AsValueThingy(long l) { rawDate = l; }
public AsValueThingy() { }
@JsonValue public Date serialization() {
return new Date(rawDate);
}
}
// [databind#222]
static class Issue222Bean
{
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
property = "type",
include = JsonTypeInfo.As.EXTERNAL_PROPERTY)
public Issue222BeanB value;
public String type = "foo";
public Issue222Bean() { }
public Issue222Bean(int v) {
value = new Issue222BeanB(v);
}
}
@JsonTypeName("222b") // shouldn't actually matter
static class Issue222BeanB
{
public int x;
public Issue222BeanB() { }
public Issue222BeanB(int value) { x = value; }
}
// [databind#928]
static class Envelope928 {
Object _payload;
public Envelope928(@JsonProperty("payload")
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.EXTERNAL_PROPERTY, property="class")
Object payload) {
_payload = payload;
}
}
static class Payload928 {
public String something;
}
enum Type965 { BIG_DECIMAL }
static class Wrapper965 {
protected Type965 typeEnum;
protected Object value;
@JsonGetter("type")
String getTypeString() {
return typeEnum.name();
}
@JsonSetter("type")
void setTypeString(String type) {
this.typeEnum = Type965.valueOf(type);
}
@JsonGetter(value = "objectValue")
Object getValue() {
return value;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type", visible = true)
@JsonSubTypes({ @JsonSubTypes.Type(name = "BIG_DECIMAL", value = BigDecimal.class) })
@JsonSetter(value = "objectValue")
private void setValue(Object value) {
this.value = value;
}
}
/*
/**********************************************************
/* Unit tests, serialization
/**********************************************************
*/
private final ObjectMapper MAPPER = newJsonMapper();
@Test
public void testSimpleSerialization() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.registerSubtypes(ValueBean.class)
.build();
// This may look odd, but one implementation nastiness is the fact
// that we cannot properly serialize type id before the object,
// because call is made after property name (for object) has already
// been written out. So we'll write it after...
// Deserializer will work either way as it cannot rely on ordering
// anyway.
assertEquals("{\"bean\":{\"value\":11},\"extType\":\"vbean\"}",
mapper.writeValueAsString(new ExternalBean(11)));
}
// If trying to use with Class, should just become "PROPERTY" instead:
@Test
public void testImproperExternalIdSerialization() throws Exception
{
ObjectMapper mapper = newJsonMapper();
assertEquals("{\"extType\":\"funk\",\"i\":3}",
mapper.writeValueAsString(new FunkyExternalBean()));
}
// for [databind#942]
@Test
public void testExternalTypeIdWithNull() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.registerSubtypes(ValueBean.class)
.build();
ExternalBean b;
b = mapper.readValue(a2q("{'bean':null,'extType':'vbean'}"),
ExternalBean.class);
assertNotNull(b);
b = mapper.readValue(a2q("{'extType':'vbean','bean':null}"),
ExternalBean.class);
assertNotNull(b);
}
/*
/**********************************************************
/* Unit tests, deserialization
/**********************************************************
*/
@Test
public void testSimpleDeserialization() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.registerSubtypes(ValueBean.class)
.build();
ExternalBean result = mapper.readValue("{\"bean\":{\"value\":11},\"extType\":\"vbean\"}", ExternalBean.class);
assertNotNull(result);
assertNotNull(result.bean);
ValueBean vb = (ValueBean) result.bean;
assertEquals(11, vb.value);
// let's also test with order switched:
result = mapper.readValue("{\"extType\":\"vbean\", \"bean\":{\"value\":13}}", ExternalBean.class);
assertNotNull(result);
assertNotNull(result.bean);
vb = (ValueBean) result.bean;
assertEquals(13, vb.value);
}
// Test for verifying that it's ok to have multiple (say, 3)
// externally typed things, mixed with other stuff...
@Test
public void testMultipleTypeIdsDeserialization() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.registerSubtypes(ValueBean.class)
.build();
String json = mapper.writeValueAsString(new ExternalBean3(3));
ExternalBean3 result = mapper.readValue(json, ExternalBean3.class);
assertNotNull(result);
assertNotNull(result.value1);
assertNotNull(result.value2);
assertNotNull(result.value3);
assertEquals(3, ((ValueBean)result.value1).value);
assertEquals(4, ((ValueBean)result.value2).value);
assertEquals(5, ((ValueBean)result.value3).value);
assertEquals(3, result.foo);
}
// Also, it should be ok to use @JsonCreator as well...
@Test
public void testExternalTypeWithCreator() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.registerSubtypes(ValueBean.class)
.build();
String json = mapper.writeValueAsString(new ExternalBeanWithCreator(7));
ExternalBeanWithCreator result = mapper.readValue(json, ExternalBeanWithCreator.class);
assertNotNull(result);
assertNotNull(result.value);
assertEquals(7, ((ValueBean)result.value).value);
assertEquals(7, result.foo);
}
// If trying to use with Class, should just become "PROPERTY" instead:
@Test
public void testImproperExternalIdDeserialization() throws Exception
{
FunkyExternalBean result = MAPPER.readValue("{\"extType\":\"funk\",\"i\":3}",
FunkyExternalBean.class);
assertNotNull(result);
assertEquals(3, result.i);
result = MAPPER.readValue("{\"i\":4,\"extType\":\"funk\"}",
FunkyExternalBean.class);
assertNotNull(result);
assertEquals(4, result.i);
}
@Test
public void testIssue798() throws Exception
{
Base base = new Derived1("derived1 prop val", "base prop val");
BaseContainer baseContainer = new BaseContainer("bc prop val", base);
String generatedJson = MAPPER.writeValueAsString(baseContainer);
BaseContainer baseContainer2 = MAPPER.readValue(generatedJson, BaseContainer.class);
assertEquals("bc prop val", baseContainer.getBaseContainerProperty());
Base b = baseContainer2.getBase();
assertNotNull(b);
assertInstanceOf(Derived1.class, b);
Derived1 derived1 = (Derived1) b;
assertEquals("base prop val", derived1.getBaseProperty());
assertEquals("derived1 prop val", derived1.getDerived1Property());
}
// There seems to be some problems if type is also visible...
@Test
public void testIssue831() throws Exception
{
final String JSON = "{ \"petType\": \"dog\",\n"
+"\"pet\": { \"name\": \"Pluto\" }\n}";
House831 result = MAPPER.readValue(JSON, House831.class);
assertNotNull(result);
assertNotNull(result.pet);
assertSame(Dog.class, result.pet.getClass());
assertEquals("dog", result.petType);
}
// For [databind#118]
// Note: String works fine, since no type id will used; other scalar types have issues
@Test
public void testWithScalar118() throws Exception
{
ExternalTypeWithNonPOJO input = new ExternalTypeWithNonPOJO(new java.util.Date(123L));
String json = MAPPER.writeValueAsString(input);
assertNotNull(json);
// and back just to be sure:
ExternalTypeWithNonPOJO result = MAPPER.readValue(json, ExternalTypeWithNonPOJO.class);
assertNotNull(result.value);
assertInstanceOf(java.util.Date.class, result.value);
}
// For [databind#118] using "natural" type(s)
@Test
public void testWithNaturalScalar118() throws Exception
{
ExternalTypeWithNonPOJO input = new ExternalTypeWithNonPOJO(Integer.valueOf(13));
String json = MAPPER.writeValueAsString(input);
assertNotNull(json);
// and back just to be sure:
ExternalTypeWithNonPOJO result = MAPPER.readValue(json, ExternalTypeWithNonPOJO.class);
assertNotNull(result.value);
assertInstanceOf(Integer.class, result.value);
// ditto with others types
input = new ExternalTypeWithNonPOJO(Boolean.TRUE);
json = MAPPER.writeValueAsString(input);
assertNotNull(json);
result = MAPPER.readValue(json, ExternalTypeWithNonPOJO.class);
assertNotNull(result.value);
assertInstanceOf(Boolean.class, result.value);
input = new ExternalTypeWithNonPOJO("foobar");
json = MAPPER.writeValueAsString(input);
assertNotNull(json);
result = MAPPER.readValue(json, ExternalTypeWithNonPOJO.class);
assertNotNull(result.value);
assertInstanceOf(String.class, result.value);
assertEquals("foobar", result.value);
}
// For [databind#119]... and bit of [#167] as well
@Test
public void testWithAsValue() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.enable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
.build();
ExternalTypeWithNonPOJO input = new ExternalTypeWithNonPOJO(new AsValueThingy(12345L));
String json = mapper.writeValueAsString(input);
assertNotNull(json);
assertEquals("{\"value\":12345,\"type\":\"thingy\"}", json);
// and get it back too:
ExternalTypeWithNonPOJO result = mapper.readValue(json, ExternalTypeWithNonPOJO.class);
assertNotNull(result);
assertNotNull(result.value);
assertEquals(AsValueThingy.class, result.value.getClass());
assertEquals(12345L, ((AsValueThingy) result.value).rawDate);
}
// for [databind#222]
@Test
public void testExternalTypeWithProp222() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
.build();
Issue222Bean input = new Issue222Bean(13);
String json = mapper.writeValueAsString(input);
assertEquals("{\"type\":\"foo\",\"value\":{\"x\":13}}", json);
}
// [databind#928]
@Test
public void testInverseExternalId928() throws Exception
{
final String CLASS = Payload928.class.getName();
final ObjectMapper mapper = jsonMapperBuilder()
.polymorphicTypeValidator(new NoCheckSubTypeValidator())
.build();
final String successCase = "{\"payload\":{\"something\":\"test\"},\"class\":\""+CLASS+"\"}";
Envelope928 envelope1 = mapper.readValue(successCase, Envelope928.class);
assertNotNull(envelope1);
assertEquals(Payload928.class, envelope1._payload.getClass());
// and then re-ordered case that was problematic
final String failCase = "{\"class\":\""+CLASS+"\",\"payload\":{\"something\":\"test\"}}";
Envelope928 envelope2 = mapper.readValue(failCase, Envelope928.class);
assertNotNull(envelope2);
assertEquals(Payload928.class, envelope2._payload.getClass());
}
// for [databind#965]
@Test
public void testBigDecimal965() throws Exception
{
Wrapper965 w = new Wrapper965();
w.typeEnum = Type965.BIG_DECIMAL;
final String NUM_STR = "-10000000000.0000000001";
w.value = new BigDecimal(NUM_STR);
String json = MAPPER.writeValueAsString(w);
// simple sanity check so serialization is faithful
if (!json.contains(NUM_STR)) {
fail("JSON content should contain value '"+NUM_STR+"', does not appear to: "+json);
}
Wrapper965 w2 = MAPPER.readerFor(Wrapper965.class)
.with(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
.readValue(json);
assertEquals(w.typeEnum, w2.typeEnum);
assertTrue(w.value.equals(w2.value),
String.format("Expected %s = %s; got back %s = %s",
w.value.getClass().getSimpleName(), w.value.toString(), w2.value.getClass().getSimpleName(), w2.value.toString()));
}
@Test
public void testBigDecimal965StringBased() throws Exception
{
Wrapper965 w = new Wrapper965();
w.typeEnum = Type965.BIG_DECIMAL;
final String NUM_STR = "-10000000000.0000000001";
w.value = new BigDecimal(NUM_STR);
String json = "{\"objectValue\":\"-10000000000.0000000001\",\"type\":\"BIG_DECIMAL\"}";
Wrapper965 w2 = MAPPER.readerFor(Wrapper965.class)
.with(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
.readValue(json);
assertEquals(w.typeEnum, w2.typeEnum);
assertTrue(w.value.equals(w2.value),
String.format("Expected %s = %s; got back %s = %s",
w.value.getClass().getSimpleName(), w.value.toString(), w2.value.getClass().getSimpleName(), w2.value.toString()));
}
static class Box3008 {
public String type;
public Fruit3008 fruit;
public Box3008(@JsonProperty("type") String type,
@JsonTypeInfo(use = Id.NAME, include = As.EXTERNAL_PROPERTY, property = "type")
@JsonSubTypes({@JsonSubTypes.Type(value = Orange.class, name = "orange")})
@JsonProperty("fruit")
Fruit3008 fruit) {
this.type = type;
this.fruit = fruit;
}
}
// [databind#3008]: allow
interface Fruit3008 {}
static class Orange implements Fruit3008 {
public String name;
public String color;
public Orange(@JsonProperty("name") String name, @JsonProperty("name") String color) {
this.name = name;
this.color = color;
}
}
// for [databind#3008]
@Test
public void testIssue3008() throws Exception
{
ObjectReader r = MAPPER.readerFor(Box3008.class);
Box3008 deserOrangeBox = r.readValue("{\"type\":null,\"fruit\":null}");
assertNull(deserOrangeBox.fruit);
assertNull(deserOrangeBox.type); // error: "expected null, but was:<null>"
}
// [databind#4185]
static class Parent4185 {
@JsonIgnoreProperties("parent")
public Child4185 child;
}
static class Child4185 {
public Parent4185 parent;
public String childType;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "childType"
)
@JsonSubTypes({
@JsonSubTypes.Type(name = "A", value = SubChildA4185.class),
@JsonSubTypes.Type(name = "B", value = SubChildB4185.class),
})
public SubChild4185 subChild;
}
interface SubChild4185 { }
static class SubChildA4185 implements SubChild4185 { }
static class SubChildB4185 implements SubChild4185 { }
// [databind#2611]
@JsonIgnoreProperties(ignoreUnknown = true)
static class Wrapper2611 {
private String type;
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "type",
defaultImpl = Default2611.class
)
private Default2611 data;
@JsonCreator
public Wrapper2611(
@JsonProperty(value = "type", required = true) String type,
@JsonProperty(value = "data", required = true) Default2611 data
) {
this.type = type;
this.data = data;
}
String getType() {
return type;
}
Default2611 getData() {
return data;
}
}
static class Default2611 {}
@JsonTypeName(value = "decimalValue")
public static class DecimalValue {
private BigDecimal value;
public DecimalValue() { value = new BigDecimal("111.1"); }
@JsonValue
public BigDecimal getValue(){ return value; }
}
@JsonPropertyOrder({"key","value"})
public static class DecimalEntry {
public DecimalEntry() {}
public String getKey() { return "num"; }
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXTERNAL_PROPERTY)
public DecimalValue getValue(){
return new DecimalValue();
}
}
public static class DecimalMetadata {
@JsonProperty("metadata")
public List<DecimalEntry> getMetadata() {
return new ArrayList<DecimalEntry>() { {add(new DecimalEntry());} };
}
}
@JsonTypeName(value = "doubleValue")
public static class DoubleValue {
private Double value;
public DoubleValue() { value = 1234.25; }
@JsonValue
public Double getValue() { return value; }
}
@JsonPropertyOrder({"key","value"})
public static class DoubleEntry {
public DoubleEntry(){}
public String getKey(){ return "num"; }
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXTERNAL_PROPERTY)
public DoubleValue getValue(){ return new DoubleValue(); }
}
public static class DoubleMetadata {
@JsonProperty("metadata")
public List<DoubleEntry> getMetadata() {
return new ArrayList<DoubleEntry>() { {add(new DoubleEntry());} };
}
}
// For [databind#291]
interface F1_291 {}
static class A291 implements F1_291 {
public String a;
}
static class B291 implements F1_291 {
public String b;
}
static interface F2_291 {}
static class C291 implements F2_291 {
public String c;
}
static class D291 implements F2_291 {
public String d;
}
static class Container291 {
@JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY, visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = A291.class, name = "1"),
@JsonSubTypes.Type(value = B291.class, name = "2")})
public F1_291 field1;
@JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY, visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = C291.class, name = "1"),
@JsonSubTypes.Type(value = D291.class, name = "2")})
public F2_291 field2;
}
static class ContainerWithExtra291 extends Container291 {
public String type;
}
// For [databind#96]: should allow use of default impl, if property missing
@Test
public void testWithDefaultAndMissing96() throws Exception
{
ObjectMapper mapper96 = jsonMapperBuilder()
.polymorphicTypeValidator(NoCheckSubTypeValidator.instance)
.build();
ExternalBeanWithDefault input = new ExternalBeanWithDefault(13);
// baseline: include type, verify things work:
String fullJson = mapper96.writeValueAsString(input);
ExternalBeanWithDefault output = mapper96.readValue(fullJson, ExternalBeanWithDefault.class);
assertNotNull(output);
assertNotNull(output.bean);
// and then try without type info...
ExternalBeanWithDefault defaulted = mapper96.readValue("{\"bean\":{\"value\":13}}",
ExternalBeanWithDefault.class);
assertNotNull(defaulted);
assertNotNull(defaulted.bean);
assertSame(ValueBean.class, defaulted.bean.getClass());
}
// [databind#4185]
@Test
public void testExternalPropertyWithJsonIgnore4185() throws Exception
{
Parent4185 parent = MAPPER.readValue(
a2q("{'child': {'childType': 'A', 'subChild':{} } }"),
Parent4185.class);
assertInstanceOf(SubChildA4185.class, parent.child.subChild);
}
// [databind#2611]
@Test
public void testExternalPropertyWithIgnoreUnknown2611() throws Exception
{
final String data = a2q("[{'type': 'test','data': {},'additional': {}}]");
List<Wrapper2611> result = MAPPER.readValue(data, new TypeReference<List<Wrapper2611>>() {});
assertEquals(1, result.size());
Wrapper2611 item = result.get(0);
assertEquals("test", item.getType());
assertNotNull(item.getData());
}
@Test
public void testDoubleMetadata() throws Exception {
DoubleMetadata doub = new DoubleMetadata();
String expected = "{\"metadata\":[{\"key\":\"num\",\"value\":1234.25,\"@type\":\"doubleValue\"}]}";
String json = MAPPER.writeValueAsString(doub);
assertEquals(expected, json, "Serialized json not equivalent");
}
@Test
public void testDecimalMetadata() throws Exception {
DecimalMetadata dec = new DecimalMetadata();
String expected = "{\"metadata\":[{\"key\":\"num\",\"value\":111.1,\"@type\":\"decimalValue\"}]}";
String json = MAPPER.writeValueAsString(dec);
assertEquals(expected, json, "Serialized json not equivalent");
}
// [databind#291]
@Test
public void testMultipleValuesSingleExtId291() throws Exception
{
// first with ext-id before values
_testMultipleValuesSingleExtId291(
"{'type' : '1',\n"
+"'field1' : { 'a' : 'AAA' },\n"
+"'field2' : { 'c' : 'CCC' }\n"
+"}"
);
// then after
_testMultipleValuesSingleExtId291(
"{\n"
+"'field1' : { 'a' : 'AAA' },\n"
+"'field2' : { 'c' : 'CCC' },\n"
+"'type' : '1'\n"
+"}"
);
// and then in-between
_testMultipleValuesSingleExtId291(
"{\n"
+"'field1' : { 'a' : 'AAA' },\n"
+"'type' : '1',\n"
+"'field2' : { 'c' : 'CCC' }\n"
+"}"
);
}
private void _testMultipleValuesSingleExtId291(String json) throws Exception
{
json = a2q(json);
// First, with base class, no type id field separately
{
Container291 c = MAPPER.readValue(json, Container291.class);
assertNotNull(c);
assertInstanceOf(A291.class, c.field1);
assertEquals("AAA", ((A291) c.field1).a);
assertInstanceOf(C291.class, c.field2);
assertEquals("CCC", ((C291) c.field2).c);
}
// then with sub-class that does have similarly named property
{
ContainerWithExtra291 c = MAPPER.readValue(json, ContainerWithExtra291.class);
assertNotNull(c);
assertEquals("1", c.type);
assertInstanceOf(A291.class, c.field1);
assertEquals("AAA", ((A291) c.field1).a);
assertInstanceOf(C291.class, c.field2);
assertEquals("CCC", ((C291) c.field2).c);
}
}
}