blob: 5608e4d3f7eaff60ae5c521240615c0e7959a9ae [file] [log] [blame]
package com.fasterxml.jackson.databind.deser;
import java.util.*;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.*;
/**
* Unit tests for verifying that {@link JsonAnySetter} annotation
* works as expected.
*/
public class AnySetterTest
extends BaseMapTest
{
static class MapImitator
{
HashMap<String,Object> _map;
public MapImitator() {
_map = new HashMap<String,Object>();
}
@JsonAnySetter
void addEntry(String key, Object value)
{
_map.put(key, value);
}
}
// for [databind#1376]
static class MapImitatorDisabled extends MapImitator
{
@Override
@JsonAnySetter(enabled=false)
void addEntry(String key, Object value) {
throw new RuntimeException("Should not get called");
}
}
/**
* Let's also verify that it is possible to define different
* value: not often useful, but possible.
*/
static class MapImitatorWithValue
{
HashMap<String,int[]> _map;
public MapImitatorWithValue() {
_map = new HashMap<String,int[]>();
}
@JsonAnySetter
void addEntry(String key, int[] value)
{
_map.put(key, value);
}
}
// Bad; 2 "any setters"
static class Broken
{
@JsonAnySetter
void addEntry1(String key, Object value) { }
@JsonAnySetter
void addEntry2(String key, Object value) { }
}
@JsonIgnoreProperties("dummy")
static class Ignored
{
HashMap<String,Object> map = new HashMap<String,Object>();
@JsonIgnore
public String bogus;
@JsonAnySetter
void addEntry(String key, Object value)
{
map.put(key, value);
}
}
static class Bean744
{
protected Map<String,Object> additionalProperties;
@JsonAnySetter
public void addAdditionalProperty(String key, Object value) {
if (additionalProperties == null) additionalProperties = new HashMap<String, Object>();
additionalProperties.put(key,value);
}
public void setAdditionalProperties(Map<String, Object> additionalProperties) {
this.additionalProperties = additionalProperties;
}
@JsonAnyGetter
public Map<String,Object> getAdditionalProperties() { return additionalProperties; }
@JsonIgnore
public String getName() {
return (String) additionalProperties.get("name");
}
}
static class Bean797Base
{
@JsonAnyGetter
public Map<String, JsonNode> getUndefinedProperties() {
throw new IllegalStateException("Should not call parent version!");
}
}
static class Bean797BaseImpl extends Bean797Base
{
@Override
public Map<String, JsonNode> getUndefinedProperties() {
return new HashMap<String, JsonNode>();
}
}
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
static abstract class Base { }
static class Impl extends Base {
public String value;
public Impl() { }
public Impl(String v) { value = v; }
}
static class PolyAnyBean
{
protected Map<String,Base> props = new HashMap<String,Base>();
@JsonAnyGetter
public Map<String,Base> props() {
return props;
}
@JsonAnySetter
public void prop(String name, Base value) {
props.put(name, value);
}
}
static class JsonAnySetterOnMap {
public int id;
@JsonAnySetter
protected HashMap<String, String> other = new HashMap<String, String>();
@JsonAnyGetter
public Map<String, String> any() {
return other;
}
}
static class JsonAnySetterOnNullMap {
public int id;
@JsonAnySetter
protected HashMap<String, String> other;
@JsonAnyGetter
public Map<String, String> any() {
return other;
}
}
static class MyGeneric<T>
{
private String staticallyMappedProperty;
private Map<T, Integer> dynamicallyMappedProperties = new HashMap<T, Integer>();
public String getStaticallyMappedProperty() {
return staticallyMappedProperty;
}
@JsonAnySetter
public void addDynamicallyMappedProperty(T key, int value) {
dynamicallyMappedProperties.put(key, value);
}
public void setStaticallyMappedProperty(String staticallyMappedProperty) {
this.staticallyMappedProperty = staticallyMappedProperty;
}
@JsonAnyGetter
public Map<T, Integer> getDynamicallyMappedProperties() {
return dynamicallyMappedProperties;
}
}
static class MyWrapper
{
private MyGeneric<String> myStringGeneric;
private MyGeneric<Integer> myIntegerGeneric;
public MyGeneric<String> getMyStringGeneric() {
return myStringGeneric;
}
public void setMyStringGeneric(MyGeneric<String> myStringGeneric) {
this.myStringGeneric = myStringGeneric;
}
public MyGeneric<Integer> getMyIntegerGeneric() {
return myIntegerGeneric;
}
public void setMyIntegerGeneric(MyGeneric<Integer> myIntegerGeneric) {
this.myIntegerGeneric = myIntegerGeneric;
}
}
// [databind#349]
static class Bean349
{
public String type;
public int x, y;
private Map<String, Object> props = new HashMap<>();
@JsonAnySetter
public void addProperty(String key, Object value) {
props.put(key, value);
}
@JsonAnyGetter
public Map<String, Object> getProperties() {
return props;
}
@JsonUnwrapped
public IdentityDTO349 identity;
}
static class IdentityDTO349 {
public int x, y;
}
/*
/**********************************************************
/* Test methods
/**********************************************************
*/
private final ObjectMapper MAPPER = new ObjectMapper();
public void testSimpleMapImitation() throws Exception
{
MapImitator mapHolder = MAPPER.readValue
("{ \"a\" : 3, \"b\" : true, \"c\":[1,2,3] }", MapImitator.class);
Map<String,Object> result = mapHolder._map;
assertEquals(3, result.size());
assertEquals(Integer.valueOf(3), result.get("a"));
assertEquals(Boolean.TRUE, result.get("b"));
Object ob = result.get("c");
assertTrue(ob instanceof List<?>);
List<?> l = (List<?>)ob;
assertEquals(3, l.size());
assertEquals(Integer.valueOf(3), l.get(2));
}
public void testAnySetterDisable() throws Exception
{
try {
MAPPER.readValue(aposToQuotes("{'value':3}"),
MapImitatorDisabled.class);
fail("Should not pass");
} catch (JsonMappingException e) {
verifyException(e, "Unrecognized field \"value\"");
}
}
public void testSimpleTyped() throws Exception
{
MapImitatorWithValue mapHolder = MAPPER.readValue
("{ \"a\" : [ 3, -1 ], \"b\" : [ ] }", MapImitatorWithValue.class);
Map<String,int[]> result = mapHolder._map;
assertEquals(2, result.size());
assertEquals(new int[] { 3, -1 }, result.get("a"));
assertEquals(new int[0], result.get("b"));
}
public void testBrokenWithDoubleAnnotations() throws Exception
{
try {
@SuppressWarnings("unused")
Broken b = MAPPER.readValue("{ \"a\" : 3 }", Broken.class);
fail("Should have gotten an exception");
} catch (JsonMappingException e) {
verifyException(e, "Multiple 'any-setter' methods");
}
}
public void testIgnored() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
_testIgnorals(mapper);
}
public void testIgnoredPart2() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
_testIgnorals(mapper);
}
public void testProblem744() throws Exception
{
Bean744 bean = MAPPER.readValue("{\"name\":\"Bob\"}", Bean744.class);
assertNotNull(bean.additionalProperties);
assertEquals(1, bean.additionalProperties.size());
assertEquals("Bob", bean.additionalProperties.get("name"));
}
public void testIssue797() throws Exception
{
String json = MAPPER.writeValueAsString(new Bean797BaseImpl());
assertEquals("{}", json);
}
// [Issue#337]
public void testPolymorphic() throws Exception
{
PolyAnyBean input = new PolyAnyBean();
input.props.put("a", new Impl("xyz"));
String json = MAPPER.writeValueAsString(input);
// System.err.println("JSON: "+json);
PolyAnyBean result = MAPPER.readValue(json, PolyAnyBean.class);
assertEquals(1, result.props.size());
Base ob = result.props.get("a");
assertNotNull(ob);
assertTrue(ob instanceof Impl);
assertEquals("xyz", ((Impl) ob).value);
}
public void testJsonAnySetterOnMap() throws Exception {
JsonAnySetterOnMap result = MAPPER.readValue("{\"id\":2,\"name\":\"Joe\", \"city\":\"New Jersey\"}",
JsonAnySetterOnMap.class);
assertEquals(2, result.id);
assertEquals("Joe", result.other.get("name"));
assertEquals("New Jersey", result.other.get("city"));
}
public void testJsonAnySetterOnNullMap() throws Exception {
JsonAnySetterOnNullMap result = MAPPER.readValue("{\"id\":2,\"name\":\"Joe\", \"city\":\"New Jersey\"}",
JsonAnySetterOnNullMap.class);
assertEquals(2, result.id);
assertNull(result.other);
}
final static String UNWRAPPED_JSON_349 = aposToQuotes(
"{ 'type' : 'IST',\n"
+" 'x' : 3,\n"
//+" 'name' : 'BLAH-New',\n"
//+" 'description' : 'namespace.name: X THIN FIR.DR-WD12-New',\n"
+" 'ZoomLinks': [ 'foofoofoofoo', 'barbarbarbar' ],\n"
+" 'y' : 4, 'z' : 8 }"
);
// [databind#349]
public void testUnwrappedWithAny() throws Exception
{
final ObjectMapper mapper = objectMapper();
Bean349 value = mapper.readValue(UNWRAPPED_JSON_349, Bean349.class);
assertNotNull(value);
assertEquals(3, value.x);
assertEquals(4, value.y);
assertEquals(2, value.props.size());
}
// [databind#349]
public void testUnwrappedWithAnyAsUpdate() throws Exception
{
final ObjectMapper mapper = objectMapper();
Bean349 bean = mapper.readerFor(Bean349.class)
.withValueToUpdate(new Bean349())
.readValue(UNWRAPPED_JSON_349);
assertEquals(3, bean.x);
assertEquals(4, bean.y);
assertEquals(2, bean.props.size());
}
// [databind#1035]
public void testGenericAnySetter() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
Map<String, Integer> stringGenericMap = new HashMap<String, Integer>();
stringGenericMap.put("testStringKey", 5);
Map<Integer, Integer> integerGenericMap = new HashMap<Integer, Integer>();
integerGenericMap.put(111, 6);
MyWrapper deserialized = mapper.readValue(aposToQuotes(
"{'myStringGeneric':{'staticallyMappedProperty':'Test','testStringKey':5},'myIntegerGeneric':{'staticallyMappedProperty':'Test2','111':6}}"
), MyWrapper.class);
MyGeneric<String> stringGeneric = deserialized.getMyStringGeneric();
MyGeneric<Integer> integerGeneric = deserialized.getMyIntegerGeneric();
assertNotNull(stringGeneric);
assertEquals(stringGeneric.getStaticallyMappedProperty(), "Test");
for(Map.Entry<String, Integer> entry : stringGeneric.getDynamicallyMappedProperties().entrySet()) {
assertTrue("A key in MyGeneric<String> is not an String.", entry.getKey() instanceof String);
assertTrue("A value in MyGeneric<Integer> is not an Integer.", entry.getValue() instanceof Integer);
}
assertEquals(stringGeneric.getDynamicallyMappedProperties(), stringGenericMap);
assertNotNull(integerGeneric);
assertEquals(integerGeneric.getStaticallyMappedProperty(), "Test2");
for(Map.Entry<Integer, Integer> entry : integerGeneric.getDynamicallyMappedProperties().entrySet()) {
Object key = entry.getKey();
assertEquals("A key in MyGeneric<Integer> is not an Integer.", Integer.class, key.getClass());
Object value = entry.getValue();
assertEquals("A value in MyGeneric<Integer> is not an Integer.", Integer.class, value.getClass());
}
assertEquals(integerGeneric.getDynamicallyMappedProperties(), integerGenericMap);
}
/*
/**********************************************************
/* Private helper methods
/**********************************************************
*/
private void _testIgnorals(ObjectMapper mapper) throws Exception
{
Ignored bean = mapper.readValue("{\"name\":\"Bob\", \"bogus\": [ 1, 2, 3], \"dummy\" : 13 }", Ignored.class);
// as of 2.0, @JsonIgnoreProperties does block; @JsonIgnore not
assertNull(bean.map.get("dummy"));
assertEquals("[1, 2, 3]", ""+bean.map.get("bogus"));
assertEquals("Bob", bean.map.get("name"));
assertEquals(2, bean.map.size());
}
}