1 package com.fasterxml.jackson.databind.convert; 2 3 import java.io.File; 4 import java.net.InetAddress; 5 import java.net.InetSocketAddress; 6 import java.net.URI; 7 import java.net.URL; 8 import java.nio.charset.Charset; 9 import java.util.Calendar; 10 import java.util.Currency; 11 import java.util.Date; 12 import java.util.GregorianCalendar; 13 import java.util.Locale; 14 import java.util.TimeZone; 15 import java.util.UUID; 16 import java.util.regex.Pattern; 17 18 import com.fasterxml.jackson.databind.BaseMapTest; 19 import com.fasterxml.jackson.databind.JavaType; 20 import com.fasterxml.jackson.databind.ObjectMapper; 21 import com.fasterxml.jackson.databind.cfg.CoercionAction; 22 import com.fasterxml.jackson.databind.cfg.CoercionInputShape; 23 import com.fasterxml.jackson.databind.exc.MismatchedInputException; 24 25 public class CoerceMiscScalarsTest extends BaseMapTest 26 { 27 private final ObjectMapper DEFAULT_MAPPER = sharedMapper(); 28 29 private final ObjectMapper MAPPER_EMPTY_TO_EMPTY; 30 { 31 MAPPER_EMPTY_TO_EMPTY = newJsonMapper(); 32 MAPPER_EMPTY_TO_EMPTY.coercionConfigDefaults() 33 .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsEmpty); 34 } 35 36 private final ObjectMapper MAPPER_EMPTY_TO_TRY_CONVERT; 37 { 38 MAPPER_EMPTY_TO_TRY_CONVERT = newJsonMapper(); 39 MAPPER_EMPTY_TO_TRY_CONVERT.coercionConfigDefaults() 40 .setCoercion(CoercionInputShape.EmptyString, CoercionAction.TryConvert); 41 } 42 43 private final ObjectMapper MAPPER_EMPTY_TO_NULL; 44 { 45 MAPPER_EMPTY_TO_NULL = newJsonMapper(); 46 MAPPER_EMPTY_TO_NULL.coercionConfigDefaults() 47 .setCoercion(CoercionInputShape.EmptyString, CoercionAction.AsNull); 48 } 49 50 private final ObjectMapper MAPPER_EMPTY_TO_FAIL; 51 { 52 MAPPER_EMPTY_TO_FAIL = newJsonMapper(); 53 MAPPER_EMPTY_TO_FAIL.coercionConfigDefaults() 54 .setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail); 55 } 56 57 private final String JSON_EMPTY = quote(""); 58 59 /* 60 /******************************************************** 61 /* Test methods, defaults (legacy) 62 /******************************************************** 63 */ 64 testScalarDefaultsFromEmpty()65 public void testScalarDefaultsFromEmpty() throws Exception 66 { 67 // mostly as null, with some exceptions 68 69 _testScalarEmptyToNull(DEFAULT_MAPPER, File.class); 70 _testScalarEmptyToNull(DEFAULT_MAPPER, URL.class); 71 72 _testScalarEmptyToEmpty(DEFAULT_MAPPER, URI.class, 73 URI.create("")); 74 75 _testScalarEmptyToNull(DEFAULT_MAPPER, Class.class); 76 _testScalarEmptyToNull(DEFAULT_MAPPER, JavaType.class); 77 _testScalarEmptyToNull(DEFAULT_MAPPER, Currency.class); 78 _testScalarEmptyToNull(DEFAULT_MAPPER, Pattern.class); 79 80 _testScalarEmptyToEmpty(DEFAULT_MAPPER, Locale.class, 81 Locale.ROOT); 82 83 _testScalarEmptyToNull(DEFAULT_MAPPER, Charset.class); 84 _testScalarEmptyToNull(DEFAULT_MAPPER, TimeZone.class); 85 _testScalarEmptyToNull(DEFAULT_MAPPER, InetAddress.class); 86 _testScalarEmptyToNull(DEFAULT_MAPPER, InetSocketAddress.class); 87 } 88 89 /* 90 /******************************************************** 91 /* Test methods, successful coercions from empty String 92 /******************************************************** 93 */ 94 testScalarEmptyToNull()95 public void testScalarEmptyToNull() throws Exception 96 { 97 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, File.class); 98 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, URL.class); 99 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, URI.class); 100 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Class.class); 101 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, JavaType.class); 102 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Currency.class); 103 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Pattern.class); 104 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Locale.class); 105 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Charset.class); 106 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, TimeZone.class); 107 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, InetAddress.class); 108 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, InetSocketAddress.class); 109 } 110 testScalarEmptyToEmpty()111 public void testScalarEmptyToEmpty() throws Exception 112 { 113 _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, File.class); 114 _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, URL.class); 115 116 _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, URI.class, 117 URI.create("")); 118 119 _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, Class.class); 120 _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, JavaType.class); 121 _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, Currency.class); 122 _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, Pattern.class); 123 124 _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, Locale.class, 125 Locale.ROOT); 126 127 _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, Charset.class); 128 _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, TimeZone.class); 129 _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, InetAddress.class); 130 _testScalarEmptyToNull(MAPPER_EMPTY_TO_EMPTY, InetSocketAddress.class); 131 } 132 testScalarEmptyToTryConvert()133 public void testScalarEmptyToTryConvert() throws Exception 134 { 135 // Should be same as `AsNull` for most but not all 136 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, File.class); 137 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, URL.class); 138 139 _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_TRY_CONVERT, URI.class, 140 URI.create("")); 141 142 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Class.class); 143 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, JavaType.class); 144 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Currency.class); 145 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Pattern.class); 146 147 _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_TRY_CONVERT, Locale.class, 148 Locale.ROOT); 149 150 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Charset.class); 151 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, TimeZone.class); 152 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, InetAddress.class); 153 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, InetSocketAddress.class); 154 } 155 156 /* 157 /******************************************************** 158 /* Test methods, failed coercions from empty String 159 /******************************************************** 160 */ 161 testScalarsFailFromEmpty()162 public void testScalarsFailFromEmpty() throws Exception 163 { 164 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, File.class); 165 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, URL.class); 166 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, URI.class); 167 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Class.class); 168 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, JavaType.class); 169 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Currency.class); 170 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Pattern.class); 171 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Locale.class); 172 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Charset.class); 173 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, TimeZone.class); 174 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, InetAddress.class); 175 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, InetSocketAddress.class); 176 } 177 178 /* 179 /******************************************************** 180 /* Test methods, (more) special type(s) 181 /******************************************************** 182 */ 183 184 // UUID is quite compatible, but not exactly due to historical reasons; 185 // also uses custom subtype, so test separately 186 testUUIDCoercions()187 public void testUUIDCoercions() throws Exception 188 { 189 // Coerce to `null` both by default, "TryConvert" and explicit 190 _testScalarEmptyToNull(DEFAULT_MAPPER, UUID.class); 191 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, UUID.class); 192 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, UUID.class); 193 194 // but allow separate "empty" value is specifically requested 195 _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, UUID.class, 196 new UUID(0L, 0L)); 197 198 // allow forcing failure, too 199 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, UUID.class); 200 201 // and allow failure with specifically configured per-class override, too 202 ObjectMapper failMapper = newJsonMapper(); 203 failMapper.coercionConfigFor(UUID.class) 204 .setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail); 205 _verifyScalarToFail(failMapper, UUID.class); 206 } 207 208 // StringBuilder is its own special type, since it naturally maps 209 // from String values, hence separate testing testStringBuilderCoercions()210 public void testStringBuilderCoercions() throws Exception 211 { 212 // should result in an "empty" StringBuilder for all valid settings 213 _checkEmptyStringBuilder(DEFAULT_MAPPER.readValue(JSON_EMPTY, StringBuilder.class)); 214 _checkEmptyStringBuilder(MAPPER_EMPTY_TO_EMPTY.readValue(JSON_EMPTY, StringBuilder.class)); 215 _checkEmptyStringBuilder(MAPPER_EMPTY_TO_TRY_CONVERT.readValue(JSON_EMPTY, StringBuilder.class)); 216 _checkEmptyStringBuilder(MAPPER_EMPTY_TO_NULL.readValue(JSON_EMPTY, StringBuilder.class)); 217 // and even alleged failure should not result in that since it's not coercion 218 _checkEmptyStringBuilder(MAPPER_EMPTY_TO_FAIL.readValue(JSON_EMPTY, StringBuilder.class)); 219 } 220 _checkEmptyStringBuilder(StringBuilder sb)221 private void _checkEmptyStringBuilder(StringBuilder sb) { 222 assertNotNull(sb); 223 assertEquals(0, sb.length()); 224 } 225 226 // Date, Calendar also included here for convenience 227 testLegacyDateTimeCoercions()228 public void testLegacyDateTimeCoercions() throws Exception 229 { 230 // Coerce to `null` both by default, "TryConvert" and explicit 231 _testScalarEmptyToNull(DEFAULT_MAPPER, Calendar.class); 232 _testScalarEmptyToNull(DEFAULT_MAPPER, Date.class); 233 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Calendar.class); 234 _testScalarEmptyToNull(MAPPER_EMPTY_TO_NULL, Date.class); 235 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Calendar.class); 236 _testScalarEmptyToNull(MAPPER_EMPTY_TO_TRY_CONVERT, Date.class); 237 238 // but allow separate "empty" value is specifically requested 239 Calendar emptyCal = new GregorianCalendar(); 240 emptyCal.setTimeInMillis(0L); 241 // _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, Calendar.class, emptyCal); 242 _testScalarEmptyToEmpty(MAPPER_EMPTY_TO_EMPTY, Date.class, new Date(0L)); 243 244 // allow forcing failure, too 245 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Calendar.class); 246 _verifyScalarToFail(MAPPER_EMPTY_TO_FAIL, Date.class); 247 } 248 249 /* 250 /******************************************************** 251 /* Second-level test helper methods 252 /******************************************************** 253 */ 254 _testScalarEmptyToNull(ObjectMapper mapper, Class<?> target)255 private void _testScalarEmptyToNull(ObjectMapper mapper, Class<?> target) throws Exception 256 { 257 assertNull(mapper.readerFor(target).readValue(JSON_EMPTY)); 258 } 259 _testScalarEmptyToEmpty(ObjectMapper mapper, Class<?> target, Object emptyValue)260 private void _testScalarEmptyToEmpty(ObjectMapper mapper, 261 Class<?> target, Object emptyValue) throws Exception 262 { 263 Object result = mapper.readerFor(target).readValue(JSON_EMPTY); 264 if (result == null) { 265 fail("Expected empty, non-null value for "+target.getName()+", got null"); 266 } 267 assertEquals(emptyValue, result); 268 } 269 _verifyScalarToFail(ObjectMapper mapper, Class<?> target)270 private void _verifyScalarToFail(ObjectMapper mapper, Class<?> target) throws Exception 271 { 272 try { 273 /*Object result =*/ mapper.readerFor(target) 274 .readValue(JSON_EMPTY); 275 fail("Should not pass"); 276 } catch (MismatchedInputException e) { 277 verifyException(e, "Cannot coerce empty String "); 278 verifyException(e, " to `"+target.getName()); 279 } 280 } 281 } 282