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 { boolean _v; void setV(boolean v) { _v = 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 ObjectMapper mapper = new ObjectMapper(); mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); BooleanBean result = mapper.readValue(new StringReader("{\"v\":[true]}"), BooleanBean.class); assertTrue(result._v); _verifyMultiValueArrayFail("[{\"v\":[true,true]}]", BooleanBean.class); result = mapper.readValue("{\"v\":[null]}", BooleanBean.class); assertNotNull(result); assertFalse(result._v); result = mapper.readValue("[{\"v\":[null]}]", BooleanBean.class); assertNotNull(result); assertFalse(result._v); boolean[] array = mapper.readValue(new StringReader("[ [ null ] ]"), boolean[].class); assertNotNull(array); assertEquals(1, array.length); assertFalse(array[0]); } /* /********************************************************** /* Single-element as array tests, numbers /********************************************************** */ // [databind#381] 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 ObjectMapper mapper = new ObjectMapper(); mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); final int intValue = mapper.readValue(asArray(intTest), Integer.TYPE); assertEquals(intTest, intValue); final Integer integerWrapperValue = mapper.readValue(asArray(Integer.valueOf(intTest)), Integer.class); assertEquals(Integer.valueOf(intTest), integerWrapperValue); final double doubleValue = mapper.readValue(asArray(doubleTest), Double.class); assertEquals(doubleTest, doubleValue); final Double doubleWrapperValue = mapper.readValue(asArray(Double.valueOf(doubleTest)), Double.class); assertEquals(Double.valueOf(doubleTest), doubleWrapperValue); final long longValue = mapper.readValue(asArray(longTest), Long.TYPE); assertEquals(longTest, longValue); final Long longWrapperValue = mapper.readValue(asArray(Long.valueOf(longTest)), Long.class); assertEquals(Long.valueOf(longTest), longWrapperValue); final short shortValue = mapper.readValue(asArray(shortTest), Short.TYPE); assertEquals(shortTest, shortValue); final Short shortWrapperValue = mapper.readValue(asArray(Short.valueOf(shortTest)), Short.class); assertEquals(Short.valueOf(shortTest), shortWrapperValue); final float floatValue = mapper.readValue(asArray(floatTest), Float.TYPE); assertEquals(floatTest, floatValue); final Float floatWrapperValue = mapper.readValue(asArray(Float.valueOf(floatTest)), Float.class); assertEquals(Float.valueOf(floatTest), floatWrapperValue); final byte byteValue = mapper.readValue(asArray(byteTest), Byte.TYPE); assertEquals(byteTest, byteValue); final Byte byteWrapperValue = mapper.readValue(asArray(Byte.valueOf(byteTest)), Byte.class); assertEquals(Byte.valueOf(byteTest), byteWrapperValue); final char charValue = mapper.readValue(asArray(quote(String.valueOf(charTest))), Character.TYPE); assertEquals(charTest, charValue); final Character charWrapperValue = mapper.readValue(asArray(quote(String.valueOf(charTest))), Character.class); assertEquals(Character.valueOf(charTest), charWrapperValue); final boolean booleanTrueValue = mapper.readValue(asArray(true), Boolean.TYPE); assertTrue(booleanTrueValue); final boolean booleanFalseValue = mapper.readValue(asArray(false), Boolean.TYPE); assertFalse(booleanFalseValue); final Boolean booleanWrapperTrueValue = mapper.readValue(asArray(Boolean.valueOf(true)), Boolean.class); assertEquals(Boolean.TRUE, booleanWrapperTrueValue); } public void testSingleElementArrayDisabled() throws Exception { final ObjectMapper mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); try { mapper.readValue("[42]", Integer.class); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("[42]", Integer.TYPE); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("[42342342342342]", Long.class); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("[42342342342342342]", Long.TYPE); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("[42]", Short.class); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("[42]", Short.TYPE); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("[327.2323]", Float.class); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("[82.81902]", Float.TYPE); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("[22]", Byte.class); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("[22]", Byte.TYPE); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("['d']", Character.class); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("['d']", Character.TYPE); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("[true]", Boolean.class); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException exp) { //Exception was thrown correctly } try { mapper.readValue("[true]", Boolean.TYPE); fail("Single value array didn't throw an exception when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } 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 testSingleString() throws Exception { String value = "FOO!"; String result = MAPPER.readValue("\""+value+"\"", String.class); assertEquals(value, result); } public void testSingleStringWrapped() throws Exception { final ObjectMapper mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); String value = "FOO!"; try { mapper.readValue("[\""+value+"\"]", String.class); fail("Exception not thrown when attempting to unwrap a single value 'String' array into a simple String"); } catch (MismatchedInputException exp) { verifyException(exp, "Can not deserialize"); verifyException(exp, "out of START_ARRAY"); } mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); try { mapper.readValue("[\""+value+"\",\""+value+"\"]", String.class); 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 = mapper.readValue("[\""+value+"\"]", String.class); assertEquals(value, result); } public void testBigDecimal() throws Exception { final ObjectMapper mapper = objectMapper(); mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); BigDecimal value = new BigDecimal("0.001"); BigDecimal result = mapper.readValue(value.toString(), BigDecimal.class); assertEquals(value, result); try { mapper.readValue("[" + value.toString() + "]", BigDecimal.class); 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) { verifyException(exp, "Can not deserialize"); verifyException(exp, "out of START_ARRAY"); } mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); result = mapper.readValue("[" + value.toString() + "]", BigDecimal.class); assertEquals(value, result); try { mapper.readValue("[" + value.toString() + "," + value.toString() + "]", BigDecimal.class); 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 { final ObjectMapper mapper = objectMapper(); mapper.disable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); BigInteger value = new BigInteger("-1234567890123456789012345567809"); BigInteger result = mapper.readValue(value.toString(), BigInteger.class); assertEquals(value, result); try { mapper.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) { verifyException(exp, "Can not deserialize"); verifyException(exp, "out of START_ARRAY"); } mapper.enable(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS); result = mapper.readValue("[" + value.toString() + "]", BigInteger.class); assertEquals(value, result); try { mapper.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 = MAPPER .readerFor(Class.class) .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) .readValue(quote(String.class.getName())); assertEquals(String.class, result); try { MAPPER.readerFor(Class.class) .without(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) .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) { verifyException(e, "out of START_ARRAY token"); } _verifyMultiValueArrayFail("[" + quote(Object.class.getName()) + "," + quote(Object.class.getName()) +"]", Class.class); result = MAPPER.readerFor(Class.class) .with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) .readValue("[" + quote(String.class.getName()) + "]"); assertEquals(String.class, result); } public void testURIAsArray() throws Exception { final ObjectReader reader = MAPPER.readerFor(URI.class); final URI value = new URI("http://foo.com"); try { reader.without(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) .readValue("[\""+value.toString()+"\"]"); fail("Did not throw exception for single value array when UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException e) { verifyException(e, "out of START_ARRAY token"); } _verifyMultiValueArrayFail("[\""+value.toString()+"\",\""+value.toString()+"\"]", URI.class); } public void testUUIDAsArray() throws Exception { final ObjectReader reader = MAPPER.readerFor(UUID.class); 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 when UNWRAP_SINGLE_VALUE_ARRAYS is disabled and attempted to read a single value array as a single element"); } catch (MismatchedInputException e) { verifyException(e, "out of START_ARRAY token"); } assertEquals(uuid, reader.with(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS) .readValue("[" + quote(uuidStr) + "]")); _verifyMultiValueArrayFail("[" + quote(uuidStr) + "," + quote(uuidStr) + "]", UUID.class); } /* /********************************************************** /* Helper methods /********************************************************** */ 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 when DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS is disabled"); } catch (MismatchedInputException e) { verifyException(e, "Attempted to unwrap"); } } private static String asArray(Object value) { return "["+value+"]"; } }