BeanDeserializerTest.java
package tools.jackson.databind.deser.bean;
import java.util.*;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import tools.jackson.core.*;
import tools.jackson.databind.*;
import tools.jackson.databind.deser.BeanDeserializerBuilder;
import tools.jackson.databind.deser.SettableBeanProperty;
import tools.jackson.databind.deser.ValueDeserializerModifier;
import tools.jackson.databind.deser.std.StdDeserializer;
import tools.jackson.databind.deser.std.StdScalarDeserializer;
import tools.jackson.databind.exc.InvalidDefinitionException;
import tools.jackson.databind.module.SimpleModule;
import tools.jackson.databind.type.ArrayType;
import tools.jackson.databind.type.CollectionType;
import tools.jackson.databind.type.MapType;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static tools.jackson.databind.testutil.DatabindTestUtil.*;
@SuppressWarnings("serial")
public class BeanDeserializerTest
{
static abstract class Abstract {
public int x;
}
static class Bean {
public String b = "b";
public String a = "a";
public Bean() { }
protected Bean(String a, String b) {
this.a = a;
this.b = b;
}
}
static class ModuleImpl extends SimpleModule
{
protected ValueDeserializerModifier modifier;
public ModuleImpl(ValueDeserializerModifier modifier)
{
super("test", Version.unknownVersion());
this.modifier = modifier;
}
@Override
public void setupModule(SetupContext context)
{
super.setupModule(context);
if (modifier != null) {
context.addDeserializerModifier(modifier);
}
}
}
static class RemovingModifier extends ValueDeserializerModifier
{
private final String _removedProperty;
public RemovingModifier(String remove) { _removedProperty = remove; }
@Override
public BeanDeserializerBuilder updateBuilder(DeserializationConfig config,
BeanDescription.Supplier beanDescRef, BeanDeserializerBuilder builder) {
builder.addIgnorable(_removedProperty);
return builder;
}
}
static class ReplacingModifier extends ValueDeserializerModifier
{
private final ValueDeserializer<?> _deserializer;
public ReplacingModifier(ValueDeserializer<?> s) { _deserializer = s; }
@Override
public ValueDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription.Supplier beanDescRef, ValueDeserializer<?> deserializer) {
return _deserializer;
}
}
static class BogusBeanDeserializer extends ValueDeserializer<Object>
{
private final String a, b;
public BogusBeanDeserializer(String a, String b) {
this.a = a;
this.b = b;
}
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt)
{
p.skipChildren();
return new Bean(a, b);
}
}
static class Issue476Bean {
public Issue476Type value1, value2;
}
static class Issue476Type {
public String name, value;
}
static class Issue476Deserializer extends BeanDeserializer
{
protected static int propCount;
public Issue476Deserializer(BeanDeserializer src) {
super(src);
}
@Override
public ValueDeserializer<?> createContextual(DeserializationContext ctxt,
BeanProperty property)
{
super.createContextual(ctxt, property);
propCount++;
return this;
}
}
public class Issue476DeserializerModifier extends ValueDeserializerModifier {
@Override
public ValueDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription.Supplier beanDescRef, ValueDeserializer<?> deserializer) {
if (Issue476Type.class == beanDescRef.getBeanClass()) {
return new Issue476Deserializer((BeanDeserializer)deserializer);
}
return super.modifyDeserializer(config, beanDescRef, deserializer);
}
}
public class Issue476Module extends SimpleModule
{
public Issue476Module() {
super("Issue476Module", Version.unknownVersion());
}
@Override
public void setupModule(SetupContext context) {
context.addDeserializerModifier(new Issue476DeserializerModifier());
}
}
public static class Issue1912Bean {
public Issue1912SubBean subBean;
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES) // This is need to populate _propertyBasedCreator on BeanDeserializerBase
public Issue1912Bean(@JsonProperty("subBean") Issue1912SubBean subBean) {
this.subBean = subBean;
}
}
public static class Issue1912SubBean {
public String a;
public Issue1912SubBean() { }
public Issue1912SubBean(String a) {
this.a = a;
}
}
public static class Issue1912CustomBeanDeserializer
extends ValueDeserializer<Issue1912Bean>
{
private BeanDeserializer defaultDeserializer;
public Issue1912CustomBeanDeserializer(BeanDeserializer defaultDeserializer) {
this.defaultDeserializer = defaultDeserializer;
}
@Override
public Issue1912Bean deserialize(JsonParser p, DeserializationContext ctxt)
{
p.nextName(); // read subBean
if (p.nextToken() != JsonToken.START_OBJECT) {
throw new IllegalArgumentException("Unexpected token "+p.currentToken());
}
Issue1912SubBean subBean = (Issue1912SubBean) defaultDeserializer.findProperty("subBean").deserialize(p, ctxt);
// Must also read trailing END_OBJECT
if (p.nextToken() != JsonToken.END_OBJECT) {
throw new IllegalArgumentException("Unexpected token "+p.currentToken());
}
return new Issue1912Bean(subBean);
}
@Override
public ValueDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
{
return new Issue1912CustomBeanDeserializer(
(BeanDeserializer) defaultDeserializer.createContextual(ctxt, property));
}
}
public static class Issue1912CustomPropertyDeserializer extends ValueDeserializer<Issue1912SubBean>
{
@Override
public Issue1912SubBean deserialize(JsonParser p, DeserializationContext ctxt)
{
p.nextName(); // read "a"
Issue1912SubBean object = new Issue1912SubBean(p.nextStringValue() + "_custom");
p.nextToken();
return object;
}
}
public static class Issue1912UseAddOrReplacePropertyDeserializerModifier extends ValueDeserializerModifier
{
@Override
public ValueDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription.Supplier beanDescRef, ValueDeserializer<?> deserializer) {
if (beanDescRef.getBeanClass() == Issue1912Bean.class) {
return new Issue1912CustomBeanDeserializer((BeanDeserializer) deserializer);
}
return super.modifyDeserializer(config, beanDescRef, deserializer);
}
@Override
public BeanDeserializerBuilder updateBuilder(DeserializationConfig config,
BeanDescription.Supplier beanDescRef, BeanDeserializerBuilder builder) {
if (beanDescRef.getBeanClass() == Issue1912Bean.class) {
Iterator<SettableBeanProperty> props = builder.getProperties();
while (props.hasNext()) {
SettableBeanProperty prop = props.next();
SettableBeanProperty propWithCustomDeserializer = prop.withValueDeserializer(new Issue1912CustomPropertyDeserializer());
builder.addOrReplaceProperty(propWithCustomDeserializer, true);
}
}
return builder;
}
}
public class Issue1912Module extends SimpleModule {
public Issue1912Module() {
super("Issue1912Module", Version.unknownVersion());
}
@Override
public void setupModule(SetupContext context) {
context.addDeserializerModifier(new Issue1912UseAddOrReplacePropertyDeserializerModifier());
}
}
// [Issue#121], arrays, collections, maps
enum EnumABC { A, B, C; }
static class ArrayDeserializerModifier extends ValueDeserializerModifier {
@Override
public ValueDeserializer<?> modifyArrayDeserializer(DeserializationConfig config,
ArrayType valueType,
BeanDescription.Supplier beanDescRef, ValueDeserializer<?> deserializer) {
return (ValueDeserializer<?>) new StdDeserializer<Object>(Object.class) {
@Override public Object deserialize(JsonParser p,
DeserializationContext ctxt) {
p.skipChildren();
return new String[] { "foo" };
}
};
}
}
static class CollectionDeserializerModifier extends ValueDeserializerModifier {
@Override
public ValueDeserializer<?> modifyCollectionDeserializer(DeserializationConfig config,
CollectionType valueType,
BeanDescription.Supplier beanDescRef, ValueDeserializer<?> deserializer) {
return (ValueDeserializer<?>) new StdDeserializer<Object>(Object.class) {
@Override public Object deserialize(JsonParser p,
DeserializationContext ctxt) {
p.skipChildren();
ArrayList<String> list = new ArrayList<String>();
list.add("foo");
return list;
}
};
}
}
static class MapDeserializerModifier extends ValueDeserializerModifier {
@Override
public ValueDeserializer<?> modifyMapDeserializer(DeserializationConfig config,
MapType valueType,
BeanDescription.Supplier beanDescRef, ValueDeserializer<?> deserializer) {
return (ValueDeserializer<?>) new StdDeserializer<Object>(Object.class) {
@Override public Object deserialize(JsonParser p,
DeserializationContext ctxt) {
p.skipChildren();
HashMap<String,String> map = new HashMap<String,String>();
map.put("a", "foo");
return map;
}
};
}
}
static class EnumDeserializerModifier extends ValueDeserializerModifier {
@Override
public ValueDeserializer<?> modifyEnumDeserializer(DeserializationConfig config,
JavaType valueType,
BeanDescription.Supplier beanDescRef, ValueDeserializer<?> deserializer) {
return (ValueDeserializer<?>) new StdDeserializer<Object>(Object.class) {
@Override public Object deserialize(JsonParser jp,
DeserializationContext ctxt) {
return "foo";
}
};
}
}
static class KeyDeserializerModifier extends ValueDeserializerModifier {
@Override
public KeyDeserializer modifyKeyDeserializer(DeserializationConfig config, JavaType valueType,
KeyDeserializer kd) {
return new KeyDeserializer() {
@Override
public Object deserializeKey(String key,
DeserializationContext ctxt) {
return "foo";
}
};
}
}
static class UCStringDeserializer extends StdScalarDeserializer<String>
{
private final ValueDeserializer<?> _deser;
public UCStringDeserializer(ValueDeserializer<?> orig) {
super(String.class);
_deser = orig;
}
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) {
Object ob = _deser.deserialize(p, ctxt);
return String.valueOf(ob).toUpperCase();
}
}
/*
/********************************************************
/* Test methods
/********************************************************
*/
private final ObjectMapper MAPPER = newJsonMapper();
/**
* Test to verify details of how trying to deserialize into
* abstract type should fail (if there is no way to determine
* actual type information for the concrete type to use)
*/
@Test
public void testAbstractFailure() throws Exception
{
try {
MAPPER.readValue("{ \"x\" : 3 }", Abstract.class);
fail("Should fail on trying to deserialize abstract type");
} catch (InvalidDefinitionException e) {
verifyException(e, "cannot construct");
}
}
@Test
public void testPropertyRemoval() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.addModule(new ModuleImpl(new RemovingModifier("a")))
.build();
Bean bean = mapper.readValue("{\"b\":\"2\"}", Bean.class);
assertEquals("2", bean.b);
// and 'a' has its default value:
assertEquals("a", bean.a);
}
@Test
public void testDeserializerReplacement() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.addModule(new ModuleImpl(new ReplacingModifier(new BogusBeanDeserializer("foo", "bar"))))
.build();
Bean bean = mapper.readValue("{\"a\":\"xyz\"}", Bean.class);
// custom deserializer always produces instance like this:
assertEquals("foo", bean.a);
assertEquals("bar", bean.b);
}
@Test
public void testIssue476() throws Exception
{
final String JSON = "{\"value1\" : {\"name\" : \"fruit\", \"value\" : \"apple\"}, \"value2\" : {\"name\" : \"color\", \"value\" : \"red\"}}";
ObjectMapper mapper = jsonMapperBuilder()
.addModule(new Issue476Module())
.build();
mapper.readValue(JSON, Issue476Bean.class);
// there are 2 properties
assertEquals(2, Issue476Deserializer.propCount);
}
// [databind#120]
@Test
public void testModifyArrayDeserializer() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.addModule(new SimpleModule("test")
.setDeserializerModifier(new ArrayDeserializerModifier()))
.build();
Object[] result = mapper.readValue("[1,2]", Object[].class);
assertEquals(1, result.length);
assertEquals("foo", result[0]);
}
@Test
public void testModifyCollectionDeserializer() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.addModule(new SimpleModule("test")
.setDeserializerModifier(new CollectionDeserializerModifier()))
.build();
List<?> result = mapper.readValue("[1,2]", List.class);
assertEquals(1, result.size());
assertEquals("foo", result.get(0));
}
@Test
public void testModifyMapDeserializer() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.addModule(new SimpleModule("test")
.setDeserializerModifier(new MapDeserializerModifier()))
.build();
Map<?,?> result = mapper.readValue("{\"a\":1,\"b\":2}", Map.class);
assertEquals(1, result.size());
assertEquals("foo", result.get("a"));
}
@Test
public void testModifyEnumDeserializer() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.addModule(new SimpleModule("test")
.setDeserializerModifier(new EnumDeserializerModifier()))
.build();
Object result = mapper.readValue(q("B"), EnumABC.class);
assertEquals("foo", result);
}
@Test
public void testModifyKeyDeserializer() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.addModule(new SimpleModule("test")
.setDeserializerModifier(new KeyDeserializerModifier()))
.build();
Map<?,?> result = mapper.readValue("{\"a\":1}", Map.class);
assertEquals(1, result.size());
assertEquals("foo", result.entrySet().iterator().next().getKey());
}
/**
* Test to verify that even standard deserializers will result in `modifyDeserializer`
* getting appropriately called.
*/
@Test
public void testModifyStdScalarDeserializer() throws Exception
{
ObjectMapper mapper = jsonMapperBuilder()
.addModule(new SimpleModule("test")
.setDeserializerModifier(new ValueDeserializerModifier() {
@Override
public ValueDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription.Supplier beanDescRef, ValueDeserializer<?> deser) {
if (beanDescRef.getBeanClass() == String.class) {
return new UCStringDeserializer(deser);
}
return deser;
}
}))
.build();
Object result = mapper.readValue(q("abcDEF"), String.class);
assertEquals("ABCDEF", result);
}
@Test
public void testAddOrReplacePropertyIsUsedOnDeserialization() throws Exception {
ObjectMapper mapper = jsonMapperBuilder()
.addModule(new Issue1912Module())
.build();
Issue1912Bean result = mapper.readValue("{\"subBean\": {\"a\":\"foo\"}}", Issue1912Bean.class);
assertEquals("foo_custom", result.subBean.a);
}
}