package com.fasterxml.jackson.databind.struct; import java.io.IOException; import java.io.StringReader; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.util.UUID; import com.fasterxml.jackson.databind.BaseMapTest; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.exc.MismatchedInputException; public class UnwrapSingleArrayScalarsTest extends BaseMapTest { static class BooleanBean { public boolean v; } static class IntBean { public int v; } static class LongBean { public long v; } static class DoubleBean { public double v; } private final ObjectMapper MAPPER = new ObjectMapper(); private final ObjectReader NO_UNWRAPPING_READER = MAPPER.reader(); private final ObjectReader UNWRAPPING_READER = MAPPER.reader() .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); /* /********************************************************** /* Tests for boolean /********************************************************** */ public void testBooleanPrimitiveArrayUnwrap() throws Exception { // [databind#381] final ObjectReader r = UNWRAPPING_READER.forType(BooleanBean.class); BooleanBean result = r.readValue(new StringReader("{\"v\":[true]}")); assertTrue(result.v); _verifyMultiValueArrayFail("[{\"v\":[true,true]}]", BooleanBean.class); result = r.readValue("{\"v\":[null]}"); assertNotNull(result); assertFalse(result.v); result = r.readValue("[{\"v\":[null]}]"); assertNotNull(result); assertFalse(result.v); boolean[] array = UNWRAPPING_READER.forType(boolean[].class) .readValue(new StringReader("[ [ null ] ]")); assertNotNull(array); assertEquals(1, array.length); assertFalse(array[0]); } /* /********************************************************** /* Single-element as array tests, numbers /********************************************************** */ public void testIntPrimitiveArrayUnwrap() throws Exception { try { NO_UNWRAPPING_READER.forType(IntBean.class) .readValue("{\"v\":[3]}"); fail("Did not throw exception when reading a value from a single value array with the UNWRAP_SINGLE_VALUE_ARRAYS feature disabled"); } catch (MismatchedInputException e) { verifyException(e, "Cannot deserialize value of type `int`"); } ObjectReader r = UNWRAPPING_READER.forType(IntBean.class); IntBean result = r.readValue("{\"v\":[3]}"); assertEquals(3, result.v); result = r.readValue("[{\"v\":[3]}]"); assertEquals(3, result.v); try { r.readValue("[{\"v\":[3,3]}]"); fail("Did not throw exception while reading a value from a multi value array with UNWRAP_SINGLE_VALUE_ARRAY feature enabled"); } catch (MismatchedInputException e) { verifyException(e, "more than one value"); } result = r.readValue("{\"v\":[null]}"); assertNotNull(result); assertEquals(0, result.v); int[] array = UNWRAPPING_READER.forType(int[].class).readValue("[ [ null ] ]"); assertNotNull(array); assertEquals(1, array.length); assertEquals(0, array[0]); } public void testLongPrimitiveArrayUnwrap() throws Exception { final ObjectReader unwrapR = UNWRAPPING_READER.forType(LongBean.class); final ObjectReader noUnwrapR = NO_UNWRAPPING_READER.forType(LongBean.class); try { noUnwrapR.readValue("{\"v\":[3]}"); fail("Did not throw exception when reading a value from a single value array"); } catch (MismatchedInputException e) { verifyException(e, "Cannot deserialize value of type `long`"); } LongBean result = unwrapR.readValue("{\"v\":[3]}"); assertEquals(3, result.v); result = unwrapR.readValue("[{\"v\":[3]}]"); assertEquals(3, result.v); try { unwrapR.readValue("[{\"v\":[3,3]}]"); fail("Did not throw exception while reading a value from a multi value array"); } catch (MismatchedInputException e) { verifyException(e, "more than one value"); } result = unwrapR.readValue("{\"v\":[null]}"); assertNotNull(result); assertEquals(0, result.v); long[] array = unwrapR.forType(long[].class) .readValue("[ [ null ] ]"); assertNotNull(array); assertEquals(1, array.length); assertEquals(0, array[0]); } public void testDoubleAsArray() throws Exception { final ObjectReader unwrapR = UNWRAPPING_READER.forType(DoubleBean.class); final ObjectReader noUnwrapR = NO_UNWRAPPING_READER.forType(DoubleBean.class); final double value = 0.016; try { noUnwrapR.readValue("{\"v\":[" + value + "]}"); fail("Did not throw exception when reading a value from a single value array"); } catch (MismatchedInputException e) { verifyException(e, "Cannot deserialize value of type `double`"); } DoubleBean result = unwrapR.readValue("{\"v\":[" + value + "]}"); assertEquals(value, result.v); result = unwrapR.readValue("[{\"v\":[" + value + "]}]"); assertEquals(value, result.v); try { unwrapR.readValue("[{\"v\":[" + value + "," + value + "]}]"); fail("Did not throw exception while reading a value from a multi value array"); } catch (MismatchedInputException e) { verifyException(e, "more than one value"); } result = unwrapR.readValue("{\"v\":[null]}"); assertNotNull(result); assertEquals(0d, result.v); double[] array = unwrapR.forType(double[].class) .readValue("[ [ null ] ]"); assertNotNull(array); assertEquals(1, array.length); assertEquals(0d, array[0]); } public void testSingleElementScalarArrays() throws Exception { final int intTest = 932832; final double doubleTest = 32.3234; final long longTest = 2374237428374293423L; final short shortTest = (short) intTest; final float floatTest = 84.3743f; final byte byteTest = (byte) 43; final char charTest = 'c'; final int intValue = UNWRAPPING_READER.readValue(asArray(intTest), Integer.TYPE); assertEquals(intTest, intValue); final Integer integerWrapperValue = UNWRAPPING_READER.readValue(asArray(Integer.valueOf(intTest)), Integer.class); assertEquals(Integer.valueOf(intTest), integerWrapperValue); final double doubleValue = UNWRAPPING_READER.readValue(asArray(doubleTest), Double.class); assertEquals(doubleTest, doubleValue); final Double doubleWrapperValue = UNWRAPPING_READER.readValue(asArray(Double.valueOf(doubleTest)), Double.class); assertEquals(Double.valueOf(doubleTest), doubleWrapperValue); final long longValue = UNWRAPPING_READER.readValue(asArray(longTest), Long.TYPE); assertEquals(longTest, longValue); final Long longWrapperValue = UNWRAPPING_READER.readValue(asArray(Long.valueOf(longTest)), Long.class); assertEquals(Long.valueOf(longTest), longWrapperValue); final short shortValue = UNWRAPPING_READER.readValue(asArray(shortTest), Short.TYPE); assertEquals(shortTest, shortValue); final Short shortWrapperValue = UNWRAPPING_READER.readValue(asArray(Short.valueOf(shortTest)), Short.class); assertEquals(Short.valueOf(shortTest), shortWrapperValue); final float floatValue = UNWRAPPING_READER.readValue(asArray(floatTest), Float.TYPE); assertEquals(floatTest, floatValue); final Float floatWrapperValue = UNWRAPPING_READER.readValue(asArray(Float.valueOf(floatTest)), Float.class); assertEquals(Float.valueOf(floatTest), floatWrapperValue); final byte byteValue = UNWRAPPING_READER.readValue(asArray(byteTest), Byte.TYPE); assertEquals(byteTest, byteValue); final Byte byteWrapperValue = UNWRAPPING_READER.readValue(asArray(Byte.valueOf(byteTest)), Byte.class); assertEquals(Byte.valueOf(byteTest), byteWrapperValue); final char charValue = UNWRAPPING_READER.readValue(asArray(quote(String.valueOf(charTest))), Character.TYPE); assertEquals(charTest, charValue); final Character charWrapperValue = UNWRAPPING_READER.readValue(asArray(quote(String.valueOf(charTest))), Character.class); assertEquals(Character.valueOf(charTest), charWrapperValue); final boolean booleanTrueValue = UNWRAPPING_READER.readValue(asArray(true), Boolean.TYPE); assertTrue(booleanTrueValue); final boolean booleanFalseValue = UNWRAPPING_READER.readValue(asArray(false), Boolean.TYPE); assertFalse(booleanFalseValue); final Boolean booleanWrapperTrueValue = UNWRAPPING_READER.readValue(asArray(Boolean.valueOf(true)), Boolean.class); assertEquals(Boolean.TRUE, booleanWrapperTrueValue); } public void testSingleElementArrayDisabled() throws Exception { try { NO_UNWRAPPING_READER.readValue("[42]", Integer.class); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("[42]", Integer.TYPE); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("[42342342342342]", Long.class); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("[42342342342342342]", Long.TYPE); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("[42]", Short.class); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("[42]", Short.TYPE); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("[327.2323]", Float.class); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("[82.81902]", Float.TYPE); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("[22]", Byte.class); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("[22]", Byte.TYPE); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("['d']", Character.class); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("['d']", Character.TYPE); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("[true]", Boolean.class); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { NO_UNWRAPPING_READER.readValue("[true]", Boolean.TYPE); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } } public void testMultiValueArrayException() throws IOException { _verifyMultiValueArrayFail("[42,42]", Integer.class); _verifyMultiValueArrayFail("[42,42]", Integer.TYPE); _verifyMultiValueArrayFail("[42342342342342,42342342342342]", Long.class); _verifyMultiValueArrayFail("[42342342342342342,42342342342342]", Long.TYPE); _verifyMultiValueArrayFail("[42,42]", Short.class); _verifyMultiValueArrayFail("[42,42]", Short.TYPE); _verifyMultiValueArrayFail("[22,23]", Byte.class); _verifyMultiValueArrayFail("[22,23]", Byte.TYPE); _verifyMultiValueArrayFail("[327.2323,327.2323]", Float.class); _verifyMultiValueArrayFail("[82.81902,327.2323]", Float.TYPE); _verifyMultiValueArrayFail("[42.273,42.273]", Double.class); _verifyMultiValueArrayFail("[42.2723,42.273]", Double.TYPE); _verifyMultiValueArrayFail(asArray(quote("c") + "," + quote("d")), Character.class); _verifyMultiValueArrayFail(asArray(quote("c") + "," + quote("d")), Character.TYPE); _verifyMultiValueArrayFail("[true,false]", Boolean.class); _verifyMultiValueArrayFail("[true,false]", Boolean.TYPE); } /* /********************************************************** /* Simple non-primitive types /********************************************************** */ public void testSingleStringWrapped() throws Exception { String value = "FOO!"; try { NO_UNWRAPPING_READER.readValue("[\""+value+"\"]", String.class); fail("Exception not thrown when attempting to unwrap a single value 'String' array into a simple String"); } catch (MismatchedInputException exp) { _verifyNoDeserFromArray(exp); } try { UNWRAPPING_READER.forType(String.class) .readValue("[\""+value+"\",\""+value+"\"]"); fail("Exception not thrown when attempting to unwrap a single value 'String' array that contained more than one value into a simple String"); } catch (MismatchedInputException exp) { verifyException(exp, "Attempted to unwrap"); } String result = UNWRAPPING_READER.forType(String.class) .readValue("[\""+value+"\"]"); assertEquals(value, result); } public void testBigDecimal() throws Exception { BigDecimal value = new BigDecimal("0.001"); ObjectReader r = NO_UNWRAPPING_READER.forType(BigDecimal.class); BigDecimal result = r.readValue(value.toString()); assertEquals(value, result); try { r.readValue("[" + value.toString() + "]"); fail("Exception was not thrown when attempting to read a single value array of BigDecimal when UNWRAP_SINGLE_VALUE_ARRAYS feature is disabled"); } catch (MismatchedInputException exp) { _verifyNoDeserFromArray(exp); } r = UNWRAPPING_READER.forType(BigDecimal.class); result = r.readValue("[" + value.toString() + "]"); assertEquals(value, result); try { r.readValue("[" + value.toString() + "," + value.toString() + "]"); fail("Exception was not thrown when attempting to read a muti value array of BigDecimal when UNWRAP_SINGLE_VALUE_ARRAYS feature is enabled"); } catch (MismatchedInputException exp) { verifyException(exp, "Attempted to unwrap"); } } public void testBigInteger() throws Exception { BigInteger value = new BigInteger("-1234567890123456789012345567809"); BigInteger result = NO_UNWRAPPING_READER.readValue(value.toString(), BigInteger.class); assertEquals(value, result); try { NO_UNWRAPPING_READER.readValue("[" + value.toString() + "]", BigInteger.class); fail("Exception was not thrown when attempting to read a single value array of BigInteger when UNWRAP_SINGLE_VALUE_ARRAYS feature is disabled"); } catch (MismatchedInputException exp) { _verifyNoDeserFromArray(exp); } result = UNWRAPPING_READER.readValue("[" + value.toString() + "]", BigInteger.class); assertEquals(value, result); try { UNWRAPPING_READER.readValue("[" + value.toString() + "," + value.toString() + "]", BigInteger.class); fail("Exception was not thrown when attempting to read a multi-value array of BigInteger when UNWRAP_SINGLE_VALUE_ARRAYS feature is enabled"); } catch (MismatchedInputException exp) { verifyException(exp, "Attempted to unwrap"); } } public void testClassAsArray() throws Exception { Class result = UNWRAPPING_READER .forType(Class.class) .readValue(quote(String.class.getName())); assertEquals(String.class, result); try { NO_UNWRAPPING_READER.forType(Class.class) .readValue("[" + quote(String.class.getName()) + "]"); fail("Did not throw exception when UNWRAP_SINGLE_VALUE_ARRAYS feature was disabled and attempted to read a Class array containing one element"); } catch (MismatchedInputException e) { _verifyNoDeserFromArray(e); } _verifyMultiValueArrayFail("[" + quote(Object.class.getName()) + "," + quote(Object.class.getName()) +"]", Class.class); result = UNWRAPPING_READER.forType(Class.class) .readValue("[" + quote(String.class.getName()) + "]"); assertEquals(String.class, result); } public void testURIAsArray() throws Exception { final URI value = new URI("http://foo.com"); try { NO_UNWRAPPING_READER.forType(URI.class) .readValue("[\""+value.toString()+"\"]"); fail("Did not throw exception for single value array when UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException e) { _verifyNoDeserFromArray(e); } _verifyMultiValueArrayFail("[\""+value.toString()+"\",\""+value.toString()+"\"]", URI.class); } public void testUUIDAsArray() throws Exception { final String uuidStr = "76e6d183-5f68-4afa-b94a-922c1fdb83f8"; UUID uuid = UUID.fromString(uuidStr); try { NO_UNWRAPPING_READER.forType(UUID.class) .readValue("[" + quote(uuidStr) + "]"); fail("Exception was not thrown as expected"); } catch (MismatchedInputException e) { _verifyNoDeserFromArray(e); } assertEquals(uuid, UNWRAPPING_READER.forType(UUID.class) .readValue("[" + quote(uuidStr) + "]")); _verifyMultiValueArrayFail("[" + quote(uuidStr) + "," + quote(uuidStr) + "]", UUID.class); } /* /********************************************************** /* Helper methods /********************************************************** */ private void _verifyNoDeserFromArray(Exception e) { verifyException(e, "Cannot deserialize"); verifyException(e, "from Array value"); verifyException(e, "JsonToken.START_ARRAY"); } private void _verifyMultiValueArrayFail(String input, Class type) throws IOException { try { UNWRAPPING_READER.forType(type).readValue(input); fail("Single value array didn't throw an exception"); } catch (MismatchedInputException e) { verifyException(e, "Attempted to unwrap"); } } private static String asArray(Object value) { return "["+value+"]"; } }