ExternalTypeIdOnContainer1127Test.java
package tools.jackson.databind.jsontype.ext;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.exc.InvalidDefinitionException;
import tools.jackson.databind.testutil.DatabindTestUtil;
import static org.junit.jupiter.api.Assertions.assertThrows;
// [databind#1127]: `@JsonTypeInfo(As.EXTERNAL_PROPERTY)` cannot work on a container-typed
// (Collection/array/Map) or reference-typed (Optional) property: there is no place to attach
// the external type-id sibling property. Verify we fail eagerly with a clear definition error
// (on both read and write sides) instead of a confusing low-level error during processing.
public class ExternalTypeIdOnContainer1127Test extends DatabindTestUtil
{
static class Foo { public String msg; }
static class FooA extends Foo { public String hey; }
static class FooB extends Foo { public String whoa; }
static class ListHolder {
public String footype;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "footype")
@JsonSubTypes({ @JsonSubTypes.Type(value = FooA.class, name = "a"),
@JsonSubTypes.Type(value = FooB.class, name = "b") })
public List<Foo> foo;
}
static class ArrayHolder {
public String footype;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "footype")
@JsonSubTypes({ @JsonSubTypes.Type(value = FooA.class, name = "a") })
public Foo[] foo;
}
static class MapHolder {
public String footype;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "footype")
@JsonSubTypes({ @JsonSubTypes.Type(value = FooA.class, name = "a") })
public Map<String, Foo> foo;
}
static class OptionalHolder {
public String footype;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "footype")
@JsonSubTypes({ @JsonSubTypes.Type(value = FooA.class, name = "a") })
public Optional<Foo> foo;
}
private final ObjectMapper MAPPER = newJsonMapper();
@Test
public void testExternalPropertyOnListFailsEagerly() throws Exception {
_verifyReadFailure(ListHolder.class,
"{'footype':'a','foo':[{'msg':'Hello','hey':'there'}]}");
}
@Test
public void testExternalPropertyOnArrayFailsEagerly() throws Exception {
_verifyReadFailure(ArrayHolder.class,
"{'footype':'a','foo':[{'msg':'Hello','hey':'there'}]}");
}
@Test
public void testExternalPropertyOnMapFailsEagerly() throws Exception {
_verifyReadFailure(MapHolder.class,
"{'footype':'a','foo':{'x':{'msg':'Hello','hey':'there'}}}");
}
@Test
public void testExternalPropertyOnReferenceFailsEagerly() throws Exception {
_verifyReadFailure(OptionalHolder.class,
"{'footype':'a','foo':{'msg':'Hello','hey':'there'}}");
}
// [databind#1127]: same eager failure must apply on serialization side
@Test
public void testExternalPropertyOnListFailsEagerlyOnWrite() throws Exception {
ListHolder h = new ListHolder();
h.foo = new ArrayList<>(List.of(_fooA()));
_verifyWriteFailure(h);
}
@Test
public void testExternalPropertyOnArrayFailsEagerlyOnWrite() throws Exception {
ArrayHolder h = new ArrayHolder();
h.foo = new Foo[] { _fooA() };
_verifyWriteFailure(h);
}
@Test
public void testExternalPropertyOnMapFailsEagerlyOnWrite() throws Exception {
MapHolder h = new MapHolder();
h.foo = new LinkedHashMap<>();
h.foo.put("x", _fooA());
_verifyWriteFailure(h);
}
@Test
public void testExternalPropertyOnReferenceFailsEagerlyOnWrite() throws Exception {
OptionalHolder h = new OptionalHolder();
h.foo = Optional.of(_fooA());
_verifyWriteFailure(h);
}
private FooA _fooA() {
FooA f = new FooA();
f.msg = "Hello";
f.hey = "there";
return f;
}
private void _verifyReadFailure(Class<?> type, String json) {
InvalidDefinitionException e = assertThrows(InvalidDefinitionException.class,
() -> MAPPER.readValue(a2q(json), type));
verifyException(e, "EXTERNAL_PROPERTY");
verifyException(e, "container-typed property");
}
private void _verifyWriteFailure(Object value) {
InvalidDefinitionException e = assertThrows(InvalidDefinitionException.class,
() -> MAPPER.writeValueAsString(value));
verifyException(e, "EXTERNAL_PROPERTY");
verifyException(e, "container-typed property");
}
}