blob: da6ceabd5aff0c299bf3dab83e43ebdb7e158b66 [file] [log] [blame]
package com.fasterxml.jackson.databind.deser;
import java.io.*;
import java.util.*;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
/**
* Unit tests for verifying "raw" (or "untyped") data binding from JSON to JDK objects;
* one that only uses core JDK types; wrappers, Maps and Lists.
*/
@SuppressWarnings("serial")
public class TestUntypedDeserialization
extends BaseMapTest
{
static class UCStringDeserializer
extends StdScalarDeserializer<String>
{
public UCStringDeserializer() { super(String.class); }
@Override
public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
return jp.getText().toUpperCase();
}
}
static class CustomNumberDeserializer
extends StdScalarDeserializer<Number>
{
protected final Integer value;
public CustomNumberDeserializer(int nr) {
super(Number.class);
value = nr;
}
@Override
public Number deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
return value;
}
}
// Let's make this Contextual, to tease out cyclic resolution issues, if any
static class ListDeserializer extends StdDeserializer<List<Object>>
implements ContextualDeserializer
{
public ListDeserializer() { super(List.class); }
@Override
public List<Object> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException
{
ArrayList<Object> list = new ArrayList<Object>();
while (jp.nextValue() != JsonToken.END_ARRAY) {
list.add("X"+jp.getText());
}
return list;
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
BeanProperty property) throws JsonMappingException
{
// For now, we just need to access "untyped" deserializer; not use it.
/*JsonDeserializer<Object> ob = */
ctxt.findContextualValueDeserializer(ctxt.constructType(Object.class), property);
return this;
}
}
static class MapDeserializer extends StdDeserializer<Map<String,Object>>
{
public MapDeserializer() { super(Map.class); }
@Override
public Map<String,Object> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException
{
Map<String,Object> map = new LinkedHashMap<String,Object>();
while (jp.nextValue() != JsonToken.END_OBJECT) {
map.put(jp.getCurrentName(), "Y"+jp.getText());
}
return map;
}
}
/*
/**********************************************************
/* Test methods
/**********************************************************
*/
@SuppressWarnings("unchecked")
public void testSampleDoc() throws Exception
{
final String JSON = SAMPLE_DOC_JSON_SPEC;
/* To get "untyped" Mapping (to Maps, Lists, instead of beans etc),
* we'll specify plain old Object.class as the target.
*/
Object root = new ObjectMapper().readValue(JSON, Object.class);
assertType(root, Map.class);
Map<?,?> rootMap = (Map<?,?>) root;
assertEquals(1, rootMap.size());
Map.Entry<?,?> rootEntry = rootMap.entrySet().iterator().next();
assertEquals("Image", rootEntry.getKey());
Object image = rootEntry.getValue();
assertType(image, Map.class);
Map<?,?> imageMap = (Map<?,?>) image;
assertEquals(5, imageMap.size());
Object value = imageMap.get("Width");
assertType(value, Integer.class);
assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_WIDTH), value);
value = imageMap.get("Height");
assertType(value, Integer.class);
assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_HEIGHT), value);
assertEquals(SAMPLE_SPEC_VALUE_TITLE, imageMap.get("Title"));
// Another Object, "thumbnail"
value = imageMap.get("Thumbnail");
assertType(value, Map.class);
Map<?,?> tnMap = (Map<?,?>) value;
assertEquals(3, tnMap.size());
assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_TN_HEIGHT), tnMap.get("Height"));
// for some reason, width is textual, not numeric...
assertEquals(SAMPLE_SPEC_VALUE_TN_WIDTH, tnMap.get("Width"));
assertEquals(SAMPLE_SPEC_VALUE_TN_URL, tnMap.get("Url"));
// And then number list, "IDs"
value = imageMap.get("IDs");
assertType(value, List.class);
List<Object> ids = (List<Object>) value;
assertEquals(4, ids.size());
assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_TN_ID1), ids.get(0));
assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_TN_ID2), ids.get(1));
assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_TN_ID3), ids.get(2));
assertEquals(Integer.valueOf(SAMPLE_SPEC_VALUE_TN_ID4), ids.get(3));
// and that's all folks!
}
public void testNestedUntypes() throws IOException
{
// 05-Apr-2014, tatu: Odd failures if using shared mapper; so work around:
final ObjectMapper mapper = new ObjectMapper();
Object root = mapper.readValue(aposToQuotes("{'a':3,'b':[1,2]}"),
Object.class);
assertTrue(root instanceof Map<?,?>);
Map<?,?> map = (Map<?,?>) root;
assertEquals(2, map.size());
assertEquals(Integer.valueOf(3), map.get("a"));
Object ob = map.get("b");
assertTrue(ob instanceof List<?>);
List<?> l = (List<?>) ob;
assertEquals(2, l.size());
assertEquals(Integer.valueOf(2), l.get(1));
}
// [JACKSON-839]: allow 'upgrade' of big integers into Long, BigInteger
public void testObjectSerializeWithLong() throws IOException
{
final ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(DefaultTyping.JAVA_LANG_OBJECT, As.PROPERTY);
final long VALUE = 1337800584532L;
String serialized = "{\"timestamp\":"+VALUE+"}";
// works fine as node
JsonNode deserialized = mapper.readTree(serialized);
assertEquals(VALUE, deserialized.get("timestamp").asLong());
// and actually should work in Maps too
Map<?,?> deserMap = mapper.readValue(serialized, Map.class);
Number n = (Number) deserMap.get("timestamp");
assertNotNull(n);
assertSame(Long.class, n.getClass());
assertEquals(Long.valueOf(VALUE), n);
}
public void testUntypedWithCustomScalarDesers() throws IOException
{
SimpleModule m = new SimpleModule("test-module");
m.addDeserializer(String.class, new UCStringDeserializer());
m.addDeserializer(Number.class, new CustomNumberDeserializer(13));
final ObjectMapper mapper = new ObjectMapper()
.registerModule(m);
Object ob = mapper.readValue("{\"a\":\"b\", \"nr\":1 }", Object.class);
assertTrue(ob instanceof Map);
Object value = ((Map<?,?>) ob).get("a");
assertNotNull(value);
assertTrue(value instanceof String);
assertEquals("B", value);
value = ((Map<?,?>) ob).get("nr");
assertNotNull(value);
assertTrue(value instanceof Number);
assertEquals(Integer.valueOf(13), value);
}
public void testUntypedWithListDeser() throws IOException
{
SimpleModule m = new SimpleModule("test-module");
m.addDeserializer(List.class, new ListDeserializer());
final ObjectMapper mapper = new ObjectMapper()
.registerModule(m);
// And then list...
Object ob = mapper.readValue("[1, 2, true]", Object.class);
assertTrue(ob instanceof List<?>);
List<?> l = (List<?>) ob;
assertEquals(3, l.size());
assertEquals("X1", l.get(0));
assertEquals("X2", l.get(1));
assertEquals("Xtrue", l.get(2));
}
public void testUntypedWithMapDeser() throws IOException
{
SimpleModule m = new SimpleModule("test-module");
m.addDeserializer(Map.class, new MapDeserializer());
final ObjectMapper mapper = new ObjectMapper()
.registerModule(m);
// And then list...
Object ob = mapper.readValue("{\"a\":true}", Object.class);
assertTrue(ob instanceof Map<?,?>);
Map<?,?> map = (Map<?,?>) ob;
assertEquals(1, map.size());
assertEquals("Ytrue", map.get("a"));
}
}