package com.fasterxml.jackson.databind.deser;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
@SuppressWarnings("serial")
public class TestCollectionDeserialization
extends BaseMapTest
{
enum Key {
KEY1, KEY2, WHATEVER;
}
@JsonDeserialize(using=ListDeserializer.class)
static class CustomList extends LinkedList<String> { }
static class ListDeserializer extends StdDeserializer<CustomList>
{
public ListDeserializer() { super(CustomList.class); }
@Override
public CustomList deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException
{
CustomList result = new CustomList();
result.add(jp.getText());
return result;
}
}
static class XBean {
public int x;
public XBean() { }
public XBean(int x) { this.x = x; }
}
// [Issue#199]
static class ListAsIterable {
public Iterable<String> values;
}
static class ListAsIterableX {
public Iterable<XBean> nums;
}
static class KeyListBean {
public List<Key> keys;
}
// [Issue#828]
@JsonDeserialize(using=SomeObjectDeserializer.class)
static class SomeObject {}
static class SomeObjectDeserializer extends StdDeserializer<SomeObject> {
public SomeObjectDeserializer() { super(SomeObject.class); }
@Override
public SomeObject deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
throw new RuntimeException("I want to catch this exception");
}
}
/*
/**********************************************************
/* Test methods
/**********************************************************
*/
private final static ObjectMapper MAPPER = new ObjectMapper();
public void testUntypedList() throws Exception
{
// to get "untyped" default List, pass Object.class
String JSON = "[ \"text!\", true, null, 23 ]";
// Not a guaranteed cast theoretically, but will work:
// (since we know that Jackson will construct an ArrayList here...)
Object value = MAPPER.readValue(JSON, Object.class);
assertNotNull(value);
assertTrue(value instanceof ArrayList<?>);
List<?> result = (List<?>) value;
assertEquals(4, result.size());
assertEquals("text!", result.get(0));
assertEquals(Boolean.TRUE, result.get(1));
assertNull(result.get(2));
assertEquals(Integer.valueOf(23), result.get(3));
}
public void testExactStringCollection() throws Exception
{
// to get typing, must use type reference
String JSON = "[ \"a\", \"b\" ]";
List<String> result = MAPPER.readValue(JSON, new TypeReference<ArrayList<String>>() { });
assertNotNull(result);
assertEquals(ArrayList.class, result.getClass());
assertEquals(2, result.size());
assertEquals("a", result.get(0));
assertEquals("b", result.get(1));
}
public void testHashSet() throws Exception
{
String JSON = "[ \"KEY1\", \"KEY2\" ]";
EnumSet<Key> result = MAPPER.readValue(JSON, new TypeReference<EnumSet<Key>>() { });
assertNotNull(result);
assertTrue(EnumSet.class.isAssignableFrom(result.getClass()));
assertEquals(2, result.size());
assertTrue(result.contains(Key.KEY1));
assertTrue(result.contains(Key.KEY2));
assertFalse(result.contains(Key.WHATEVER));
}
/// Test to verify that @JsonDeserialize.using works as expected
public void testCustomDeserializer() throws IOException
{
CustomList result = MAPPER.readValue(quote("abc"), CustomList.class);
assertEquals(1, result.size());
assertEquals("abc", result.get(0));
}
// Testing "implicit JSON array" for single-element arrays,
// mostly produced by Jettison, Badgerfish conversions (from XML)
@SuppressWarnings("unchecked")
public void testImplicitArrays() throws Exception
{
// can't share mapper, custom configs (could create ObjectWriter tho)
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
// first with simple scalar types (numbers), with collections
List<Integer> ints = mapper.readValue("4", List.class);
assertEquals(1, ints.size());
assertEquals(Integer.valueOf(4), ints.get(0));
List<String> strings = mapper.readValue(quote("abc"), new TypeReference<ArrayList<String>>() { });
assertEquals(1, strings.size());
assertEquals("abc", strings.get(0));
// and arrays:
int[] intArray = mapper.readValue("-7", int[].class);
assertEquals(1, intArray.length);
assertEquals(-7, intArray[0]);
String[] stringArray = mapper.readValue(quote("xyz"), String[].class);
assertEquals(1, stringArray.length);
assertEquals("xyz", stringArray[0]);
// and then with Beans:
List<XBean> xbeanList = mapper.readValue("{\"x\":4}", new TypeReference<List<XBean>>() { });
assertEquals(1, xbeanList.size());
assertEquals(XBean.class, xbeanList.get(0).getClass());
Object ob = mapper.readValue("{\"x\":29}", XBean[].class);
XBean[] xbeanArray = (XBean[]) ob;
assertEquals(1, xbeanArray.length);
assertEquals(XBean.class, xbeanArray[0].getClass());
}
// [JACKSON-620]: allow "" to mean 'null' for Maps
public void testFromEmptyString() throws Exception
{
ObjectReader r = MAPPER.reader(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
List<?> result = r.forType(List.class).readValue(quote(""));
assertNull(result);
}
// [databind#161]
public void testArrayBlockingQueue() throws Exception
{
// ok to skip polymorphic type to get Object
ArrayBlockingQueue<?> q = MAPPER.readValue("[1, 2, 3]", ArrayBlockingQueue.class);
assertNotNull(q);
assertEquals(3, q.size());
assertEquals(Integer.valueOf(1), q.take());
assertEquals(Integer.valueOf(2), q.take());
assertEquals(Integer.valueOf(3), q.take());
}
// [databind#199]
public void testIterableWithStrings() throws Exception
{
String JSON = "{ \"values\":[\"a\",\"b\"]}";
ListAsIterable w = MAPPER.readValue(JSON, ListAsIterable.class);
assertNotNull(w);
assertNotNull(w.values);
Iterator<String> it = w.values.iterator();
assertTrue(it.hasNext());
assertEquals("a", it.next());
assertEquals("b", it.next());
assertFalse(it.hasNext());
}
public void testIterableWithBeans() throws Exception
{
String JSON = "{ \"nums\":[{\"x\":1},{\"x\":2}]}";
ListAsIterableX w = MAPPER.readValue(JSON, ListAsIterableX.class);
assertNotNull(w);
assertNotNull(w.nums);
Iterator<XBean> it = w.nums.iterator();
assertTrue(it.hasNext());
XBean xb = it.next();
assertNotNull(xb);
assertEquals(1, xb.x);
xb = it.next();
assertEquals(2, xb.x);
assertFalse(it.hasNext());
}
// for [databind#506]
public void testArrayIndexForExceptions() throws Exception
{
final String OBJECTS_JSON = "[ \"KEY2\", false ]";
try {
MAPPER.readValue(OBJECTS_JSON, Key[].class);
fail("Should not pass");
} catch (JsonMappingException e) {
verifyException(e, "Can not deserialize");
List<JsonMappingException.Reference> refs = e.getPath();
assertEquals(1, refs.size());
assertEquals(1, refs.get(0).getIndex());
}
try {
MAPPER.readValue("[ \"xyz\", { } ]", String[].class);
fail("Should not pass");
} catch (JsonMappingException e) {
verifyException(e, "Can not deserialize");
List<JsonMappingException.Reference> refs = e.getPath();
assertEquals(1, refs.size());
assertEquals(1, refs.get(0).getIndex());
}
try {
MAPPER.readValue("{\"keys\":"+OBJECTS_JSON+"}", KeyListBean.class);
fail("Should not pass");
} catch (JsonMappingException e) {
verifyException(e, "Can not deserialize");
List<JsonMappingException.Reference> refs = e.getPath();
assertEquals(2, refs.size());
// Bean has no index, but has name:
assertEquals(-1, refs.get(0).getIndex());
assertEquals("keys", refs.get(0).getFieldName());
// and for List, reverse:
assertEquals(1, refs.get(1).getIndex());
assertNull(refs.get(1).getFieldName());
}
}
// for [databind#828]
public void testWrapExceptions() throws Exception
{
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.WRAP_EXCEPTIONS);
try {
mapper.readValue("[{}]", new TypeReference<List<SomeObject>>() {});
} catch (JsonMappingException exc) {
assertEquals("I want to catch this exception", exc.getOriginalMessage());
} catch (RuntimeException exc) {
fail("The RuntimeException should have been wrapped with a JsonMappingException.");
}
ObjectMapper mapperNoWrap = new ObjectMapper();
mapperNoWrap.disable(DeserializationFeature.WRAP_EXCEPTIONS);
try {
mapperNoWrap.readValue("[{}]", new TypeReference<List<SomeObject>>() {});
} catch (JsonMappingException exc) {
fail("It should not have wrapped the RuntimeException.");
} catch (RuntimeException exc) {
assertEquals("I want to catch this exception", exc.getMessage());
}
}
// And then a round-trip test for singleton collections
public void testSingletonCollections() throws Exception
{
final TypeReference<?> xbeanListType = new TypeReference<List<XBean>>() { };
String json = MAPPER.writeValueAsString(Collections.singleton(new XBean(3)));
Collection<XBean> result = MAPPER.readValue(json, xbeanListType);
assertNotNull(result);
assertEquals(1, result.size());
assertEquals(3, result.iterator().next().x);
json = MAPPER.writeValueAsString(Collections.singletonList(new XBean(28)));
result = MAPPER.readValue(json, xbeanListType);
assertNotNull(result);
assertEquals(1, result.size());
assertEquals(28, result.iterator().next().x);
}
}