1 package com.fasterxml.jackson.databind.convert; 2 3 import java.math.BigDecimal; 4 import java.math.BigInteger; 5 import java.net.URI; 6 import java.net.URL; 7 import java.util.*; 8 9 import com.fasterxml.jackson.core.type.TypeReference; 10 import com.fasterxml.jackson.databind.*; 11 import com.fasterxml.jackson.databind.cfg.CoercionAction; 12 import com.fasterxml.jackson.databind.cfg.CoercionInputShape; 13 import com.fasterxml.jackson.databind.exc.MismatchedInputException; 14 import com.fasterxml.jackson.databind.type.LogicalType; 15 16 /** 17 * Tests to verify implementation of [databind#540]; also for 18 * follow up work of: 19 * 20 * - [databind#994] 21 */ 22 public class CoerceEmptyArrayTest extends BaseMapTest 23 { 24 private final ObjectMapper DEFAULT_MAPPER = sharedMapper(); 25 private final ObjectReader DEFAULT_READER = DEFAULT_MAPPER.reader(); 26 private final ObjectReader READER_WITH_ARRAYS = DEFAULT_READER 27 .with(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT); 28 29 private final ObjectMapper MAPPER_TO_EMPTY; 30 { 31 MAPPER_TO_EMPTY = newJsonMapper(); 32 MAPPER_TO_EMPTY.coercionConfigDefaults() 33 .setCoercion(CoercionInputShape.EmptyArray, CoercionAction.AsEmpty); 34 } 35 36 private final ObjectMapper MAPPER_TRY_CONVERT; 37 { 38 MAPPER_TRY_CONVERT = newJsonMapper(); 39 MAPPER_TRY_CONVERT.coercionConfigDefaults() 40 .setCoercion(CoercionInputShape.EmptyArray, CoercionAction.TryConvert); 41 } 42 43 private final ObjectMapper MAPPER_TO_NULL; 44 { 45 MAPPER_TO_NULL = newJsonMapper(); 46 MAPPER_TO_NULL.coercionConfigDefaults() 47 .setCoercion(CoercionInputShape.EmptyArray, CoercionAction.AsNull); 48 } 49 50 private final ObjectMapper MAPPER_TO_FAIL; 51 { 52 MAPPER_TO_FAIL = newJsonMapper(); 53 MAPPER_TO_FAIL.coercionConfigDefaults() 54 .setCoercion(CoercionInputShape.EmptyArray, CoercionAction.Fail); 55 } 56 57 static class Bean { 58 public String a = "foo"; 59 60 @Override equals(Object o)61 public boolean equals(Object o) { 62 return (o instanceof Bean) 63 && a.equals(((Bean) o).a); 64 } 65 } 66 67 final static String EMPTY_ARRAY = " [\n]"; 68 69 /* 70 /********************************************************** 71 /* Test methods, settings 72 /********************************************************** 73 */ 74 testSettings()75 public void testSettings() { 76 assertFalse(DEFAULT_MAPPER.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)); 77 assertFalse(DEFAULT_READER.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)); 78 assertTrue(READER_WITH_ARRAYS.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT)); 79 } 80 81 /* 82 /********************************************************** 83 /* Test methods, POJOs 84 /********************************************************** 85 */ 86 87 // [databind#540] testPOJOFromEmptyArray()88 public void testPOJOFromEmptyArray() throws Exception 89 { 90 final Class<?> targetType = Bean.class; 91 92 _verifyFailForEmptyArray(DEFAULT_READER, targetType); 93 _verifyFailForEmptyArray(MAPPER_TO_FAIL, targetType); 94 95 // Nulls for explicit, "TryConvert" 96 _verifyToNullCoercion(MAPPER_TO_NULL, targetType); 97 _verifyToNullCoercion(MAPPER_TRY_CONVERT, targetType); 98 99 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, targetType, new Bean()); 100 101 // But let's also check precedence: legacy setting allow, but mask for type 102 ObjectMapper mapper = jsonMapperBuilder() 103 .enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) 104 .build(); 105 mapper.coercionConfigFor(targetType) 106 .setCoercion(CoercionInputShape.EmptyArray, CoercionAction.Fail); 107 _verifyFailForEmptyArray(mapper, targetType); 108 109 // and conversely 110 mapper = jsonMapperBuilder() 111 .disable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) 112 .build(); 113 mapper.coercionConfigFor(LogicalType.POJO) 114 .setCoercion(CoercionInputShape.EmptyArray, CoercionAction.AsEmpty); 115 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, targetType, new Bean()); 116 } 117 118 /* 119 /********************************************************** 120 /* Test methods, Maps 121 /********************************************************** 122 */ 123 testMapFromEmptyArray()124 public void testMapFromEmptyArray() throws Exception 125 { 126 final Class<?> targetType = Map.class; 127 128 _verifyFailForEmptyArray(DEFAULT_READER, targetType); 129 _verifyFailForEmptyArray(MAPPER_TO_FAIL, targetType); 130 131 // Nulls for explicit, "TryConvert" 132 _verifyToNullCoercion(MAPPER_TO_NULL, targetType); 133 _verifyToNullCoercion(MAPPER_TRY_CONVERT, targetType); 134 135 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, targetType, new LinkedHashMap<>()); 136 137 // assume overrides work ok since POJOs test it 138 } 139 testEnumMapFromEmptyArray()140 public void testEnumMapFromEmptyArray() throws Exception 141 { 142 final JavaType targetType = DEFAULT_READER.getTypeFactory() 143 .constructType(new TypeReference<EnumMap<ABC,String>>() { }); 144 145 assertNull(MAPPER_TO_NULL.readerFor(targetType).readValue(EMPTY_ARRAY)); 146 147 EnumMap<?,?> result = MAPPER_TO_EMPTY.readerFor(targetType).readValue(EMPTY_ARRAY); 148 assertNotNull(result); 149 assertTrue(result.isEmpty()); 150 } 151 152 /* 153 /********************************************************** 154 /* Test methods, scalars 155 /********************************************************** 156 */ 157 testNumbersFromEmptyArray()158 public void testNumbersFromEmptyArray() throws Exception 159 { 160 for (Class<?> targetType : new Class<?>[] { 161 Boolean.class, Character.class, 162 Byte.class, Short.class, Integer.class, Long.class, 163 Float.class, Double.class, 164 BigInteger.class, BigDecimal.class 165 }) { 166 // Default, fail; explicit fail 167 _verifyFailForEmptyArray(DEFAULT_READER, targetType); 168 _verifyFailForEmptyArray(MAPPER_TO_FAIL, targetType); 169 170 // Nulls for explicit, "TryConvert" 171 _verifyToNullCoercion(MAPPER_TO_NULL, targetType); 172 _verifyToNullCoercion(MAPPER_TRY_CONVERT, targetType); 173 } 174 175 // But as-empty needs separate 176 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, Boolean.class, Boolean.FALSE); 177 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, Character.class, Character.valueOf('\0')); 178 179 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, Byte.class, Byte.valueOf((byte) 0)); 180 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, Short.class, Short.valueOf((short) 0)); 181 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, Integer.class, Integer.valueOf(0)); 182 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, Long.class, Long.valueOf(0L)); 183 184 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, Float.class, Float.valueOf(0f)); 185 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, Double.class, Double.valueOf(0d)); 186 187 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, BigInteger.class, BigInteger.ZERO); 188 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, BigDecimal.class, new BigDecimal(BigInteger.ZERO)); 189 } 190 testOtherScalarsFromEmptyArray()191 public void testOtherScalarsFromEmptyArray() throws Exception 192 { 193 for (Class<?> targetType : new Class<?>[] { 194 String.class, StringBuilder.class, 195 UUID.class, URL.class, URI.class, 196 Date.class, Calendar.class 197 }) { 198 _verifyFailForEmptyArray(DEFAULT_READER, targetType); 199 _verifyFailForEmptyArray(MAPPER_TO_FAIL, targetType); 200 201 // Nulls for explicit, "TryConvert" 202 _verifyToNullCoercion(MAPPER_TO_NULL, targetType); 203 _verifyToNullCoercion(MAPPER_TRY_CONVERT, targetType); 204 } 205 206 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, String.class, ""); 207 StringBuilder sb = MAPPER_TO_EMPTY.readerFor(StringBuilder.class) 208 .readValue(EMPTY_ARRAY); 209 assertEquals(0, sb.length()); 210 211 _verifyToEmptyCoercion(MAPPER_TO_EMPTY, UUID.class, new UUID(0L, 0L)); 212 } 213 214 /* 215 /********************************************************** 216 /* Helper methods 217 /********************************************************** 218 */ 219 _verifyToNullCoercion(ObjectMapper mapper, Class<?> cls)220 private void _verifyToNullCoercion(ObjectMapper mapper, Class<?> cls) throws Exception { 221 _verifyToNullCoercion(mapper.reader(), cls); 222 } 223 _verifyToNullCoercion(ObjectReader r, Class<?> cls)224 private void _verifyToNullCoercion(ObjectReader r, Class<?> cls) throws Exception { 225 Object result = r.forType(cls).readValue(EMPTY_ARRAY); 226 if (result != null) { 227 fail("Expect null for "+cls.getName()+", got: "+result); 228 } 229 } 230 _verifyToEmptyCoercion(ObjectMapper mapper, Class<?> cls, Object exp)231 private void _verifyToEmptyCoercion(ObjectMapper mapper, Class<?> cls, Object exp) throws Exception { 232 _verifyToEmptyCoercion(mapper.reader(), cls, exp); 233 } 234 _verifyToEmptyCoercion(ObjectReader r, Class<?> cls, Object exp)235 private void _verifyToEmptyCoercion(ObjectReader r, Class<?> cls, Object exp) throws Exception { 236 Object result = r.forType(cls).readValue(EMPTY_ARRAY); 237 if (!exp.equals(result)) { 238 fail("Expect value ["+exp+"] for "+cls.getName()+", got: "+result); 239 } 240 } 241 _verifyFailForEmptyArray(ObjectMapper mapper, Class<?> targetType)242 private void _verifyFailForEmptyArray(ObjectMapper mapper, Class<?> targetType) throws Exception { 243 _verifyFailForEmptyArray(mapper.readerFor(targetType), targetType); 244 } 245 _verifyFailForEmptyArray(ObjectReader r, Class<?> targetType)246 private void _verifyFailForEmptyArray(ObjectReader r, Class<?> targetType) throws Exception 247 { 248 try { 249 r.forType(targetType).readValue(EMPTY_ARRAY); 250 fail("Should not accept Empty Array for "+targetType.getName()+" by default"); 251 } catch (MismatchedInputException e) { 252 verifyException(e, "from Array value (token `JsonToken.START_ARRAY`)"); 253 } 254 } 255 } 256