1 package com.fasterxml.jackson.databind.convert; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.IOException; 5 import java.io.StringReader; 6 import java.math.BigDecimal; 7 import java.math.BigInteger; 8 import java.util.concurrent.atomic.AtomicBoolean; 9 10 import com.fasterxml.jackson.core.JsonParser; 11 import com.fasterxml.jackson.core.JsonToken; 12 import com.fasterxml.jackson.databind.*; 13 import com.fasterxml.jackson.databind.exc.MismatchedInputException; 14 15 // Tests for "old" coercions (pre-2.12), with `MapperFeature.ALLOW_COERCION_OF_SCALARS` 16 public class CoerceJDKScalarsTest extends BaseMapTest 17 { 18 static class BooleanPOJO { 19 public Boolean value; 20 } 21 22 private final ObjectMapper COERCING_MAPPER = jsonMapperBuilder() 23 .enable(MapperFeature.ALLOW_COERCION_OF_SCALARS) 24 .build(); 25 26 private final ObjectMapper NOT_COERCING_MAPPER = jsonMapperBuilder() 27 .disable(MapperFeature.ALLOW_COERCION_OF_SCALARS) 28 .build(); 29 30 /* 31 /********************************************************** 32 /* Unit tests: coercion from empty String 33 /********************************************************** 34 */ 35 testNullValueFromEmpty()36 public void testNullValueFromEmpty() throws Exception 37 { 38 // wrappers accept `null` fine 39 _verifyNullOkFromEmpty(Boolean.class, null); 40 // but primitives require non-null 41 _verifyNullOkFromEmpty(Boolean.TYPE, Boolean.FALSE); 42 43 _verifyNullOkFromEmpty(Byte.class, null); 44 _verifyNullOkFromEmpty(Byte.TYPE, Byte.valueOf((byte) 0)); 45 _verifyNullOkFromEmpty(Short.class, null); 46 _verifyNullOkFromEmpty(Short.TYPE, Short.valueOf((short) 0)); 47 _verifyNullOkFromEmpty(Character.class, null); 48 _verifyNullOkFromEmpty(Character.TYPE, Character.valueOf((char) 0)); 49 _verifyNullOkFromEmpty(Integer.class, null); 50 _verifyNullOkFromEmpty(Integer.TYPE, Integer.valueOf(0)); 51 _verifyNullOkFromEmpty(Long.class, null); 52 _verifyNullOkFromEmpty(Long.TYPE, Long.valueOf(0L)); 53 _verifyNullOkFromEmpty(Float.class, null); 54 _verifyNullOkFromEmpty(Float.TYPE, Float.valueOf(0.0f)); 55 _verifyNullOkFromEmpty(Double.class, null); 56 _verifyNullOkFromEmpty(Double.TYPE, Double.valueOf(0.0)); 57 58 _verifyNullOkFromEmpty(BigInteger.class, null); 59 _verifyNullOkFromEmpty(BigDecimal.class, null); 60 _verifyNullOkFromEmpty(AtomicBoolean.class, null); 61 } 62 _verifyNullOkFromEmpty(Class<?> type, Object exp)63 private void _verifyNullOkFromEmpty(Class<?> type, Object exp) throws IOException 64 { 65 Object result = COERCING_MAPPER.readerFor(type) 66 .with(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT) 67 .readValue("\"\""); 68 if (exp == null) { 69 assertNull(result); 70 } else { 71 assertEquals(exp, result); 72 } 73 } 74 testNullFailFromEmpty()75 public void testNullFailFromEmpty() throws Exception 76 { 77 _verifyNullFail(Boolean.class); 78 _verifyNullFail(Boolean.TYPE); 79 80 _verifyNullFail(Byte.class); 81 _verifyNullFail(Byte.TYPE); 82 _verifyNullFail(Short.class); 83 _verifyNullFail(Short.TYPE); 84 _verifyNullFail(Character.class); 85 _verifyNullFail(Character.TYPE); 86 _verifyNullFail(Integer.class); 87 _verifyNullFail(Integer.TYPE); 88 _verifyNullFail(Long.class); 89 _verifyNullFail(Long.TYPE); 90 _verifyNullFail(Float.class); 91 _verifyNullFail(Float.TYPE); 92 _verifyNullFail(Double.class); 93 _verifyNullFail(Double.TYPE); 94 95 _verifyNullFail(BigInteger.class); 96 _verifyNullFail(BigDecimal.class); 97 _verifyNullFail(AtomicBoolean.class); 98 } 99 _verifyNullFail(Class<?> type)100 private void _verifyNullFail(Class<?> type) throws IOException 101 { 102 try { 103 NOT_COERCING_MAPPER.readerFor(type).readValue("\"\""); 104 fail("Should have failed for "+type); 105 } catch (MismatchedInputException e) { 106 verifyException(e, "Cannot coerce empty String"); 107 } 108 } 109 110 /* 111 /********************************************************** 112 /* Unit tests: coercion from secondary representations 113 /********************************************************** 114 */ 115 testStringCoercionOkBoolean()116 public void testStringCoercionOkBoolean() throws Exception 117 { 118 // first successful coercions. Boolean has a ton... 119 _verifyCoerceSuccess("1", Boolean.TYPE, Boolean.TRUE); 120 _verifyCoerceSuccess("1", Boolean.class, Boolean.TRUE); 121 _verifyCoerceSuccess(quote("true"), Boolean.TYPE, Boolean.TRUE); 122 _verifyCoerceSuccess(quote("true"), Boolean.class, Boolean.TRUE); 123 _verifyCoerceSuccess(quote("True"), Boolean.TYPE, Boolean.TRUE); 124 _verifyCoerceSuccess(quote("True"), Boolean.class, Boolean.TRUE); 125 _verifyCoerceSuccess(quote("TRUE"), Boolean.TYPE, Boolean.TRUE); 126 _verifyCoerceSuccess(quote("TRUE"), Boolean.class, Boolean.TRUE); 127 _verifyCoerceSuccess("0", Boolean.TYPE, Boolean.FALSE); 128 _verifyCoerceSuccess("0", Boolean.class, Boolean.FALSE); 129 _verifyCoerceSuccess(quote("false"), Boolean.TYPE, Boolean.FALSE); 130 _verifyCoerceSuccess(quote("false"), Boolean.class, Boolean.FALSE); 131 _verifyCoerceSuccess(quote("False"), Boolean.TYPE, Boolean.FALSE); 132 _verifyCoerceSuccess(quote("False"), Boolean.class, Boolean.FALSE); 133 _verifyCoerceSuccess(quote("FALSE"), Boolean.TYPE, Boolean.FALSE); 134 _verifyCoerceSuccess(quote("FALSE"), Boolean.class, Boolean.FALSE); 135 } 136 testStringCoercionOkNumbers()137 public void testStringCoercionOkNumbers() throws Exception 138 { 139 _verifyCoerceSuccess(quote("123"), Byte.TYPE, Byte.valueOf((byte) 123)); 140 _verifyCoerceSuccess(quote("123"), Byte.class, Byte.valueOf((byte) 123)); 141 _verifyCoerceSuccess(quote("123"), Short.TYPE, Short.valueOf((short) 123)); 142 _verifyCoerceSuccess(quote("123"), Short.class, Short.valueOf((short) 123)); 143 _verifyCoerceSuccess(quote("123"), Integer.TYPE, Integer.valueOf(123)); 144 _verifyCoerceSuccess(quote("123"), Integer.class, Integer.valueOf(123)); 145 _verifyCoerceSuccess(quote("123"), Long.TYPE, Long.valueOf(123)); 146 _verifyCoerceSuccess(quote("123"), Long.class, Long.valueOf(123)); 147 _verifyCoerceSuccess(quote("123.5"), Float.TYPE, Float.valueOf(123.5f)); 148 _verifyCoerceSuccess(quote("123.5"), Float.class, Float.valueOf(123.5f)); 149 _verifyCoerceSuccess(quote("123.5"), Double.TYPE, Double.valueOf(123.5)); 150 _verifyCoerceSuccess(quote("123.5"), Double.class, Double.valueOf(123.5)); 151 152 _verifyCoerceSuccess(quote("123"), BigInteger.class, BigInteger.valueOf(123)); 153 _verifyCoerceSuccess(quote("123.0"), BigDecimal.class, new BigDecimal("123.0")); 154 155 AtomicBoolean ab = COERCING_MAPPER.readValue(quote("true"), AtomicBoolean.class); 156 assertNotNull(ab); 157 assertTrue(ab.get()); 158 } 159 testStringCoercionFailBoolean()160 public void testStringCoercionFailBoolean() throws Exception 161 { 162 _verifyRootStringCoerceFail("true", Boolean.TYPE); 163 _verifyRootStringCoerceFail("true", Boolean.class); 164 _verifyRootStringCoerceFail("True", Boolean.TYPE); 165 _verifyRootStringCoerceFail("True", Boolean.class); 166 _verifyRootStringCoerceFail("TRUE", Boolean.TYPE); 167 _verifyRootStringCoerceFail("TRUE", Boolean.class); 168 169 _verifyRootStringCoerceFail("false", Boolean.TYPE); 170 _verifyRootStringCoerceFail("false", Boolean.class); 171 } 172 testStringCoercionFailInteger()173 public void testStringCoercionFailInteger() throws Exception 174 { 175 _verifyRootStringCoerceFail("123", Byte.TYPE); 176 _verifyRootStringCoerceFail("123", Byte.class); 177 _verifyRootStringCoerceFail("123", Short.TYPE); 178 _verifyRootStringCoerceFail("123", Short.class); 179 _verifyRootStringCoerceFail("123", Integer.TYPE); 180 _verifyRootStringCoerceFail("123", Integer.class); 181 _verifyRootStringCoerceFail("123", Long.TYPE); 182 _verifyRootStringCoerceFail("123", Long.class); 183 } 184 testStringCoercionFailFloat()185 public void testStringCoercionFailFloat() throws Exception 186 { 187 _verifyRootStringCoerceFail("123.5", Float.TYPE); 188 _verifyRootStringCoerceFail("123.5", Float.class); 189 _verifyRootStringCoerceFail("123.5", Double.TYPE); 190 _verifyRootStringCoerceFail("123.5", Double.class); 191 192 _verifyRootStringCoerceFail("123", BigInteger.class); 193 _verifyRootStringCoerceFail("123.0", BigDecimal.class); 194 } 195 196 // [databind#2635], [databind#2770] testToBooleanCoercionFailBytes()197 public void testToBooleanCoercionFailBytes() throws Exception 198 { 199 final String beanDoc = aposToQuotes("{'value':1}"); 200 _verifyBooleanCoerceFail("1", true, JsonToken.VALUE_NUMBER_INT, "1", Boolean.TYPE); 201 _verifyBooleanCoerceFail("1", true, JsonToken.VALUE_NUMBER_INT, "1", Boolean.class); 202 _verifyBooleanCoerceFail(beanDoc, true, JsonToken.VALUE_NUMBER_INT, "1", BooleanPOJO.class); 203 204 _verifyBooleanCoerceFail("1.25", true, JsonToken.VALUE_NUMBER_FLOAT, "1.25", Boolean.TYPE); 205 _verifyBooleanCoerceFail("1.25", true, JsonToken.VALUE_NUMBER_FLOAT, "1.25", Boolean.class); 206 } 207 208 // [databind#2635], [databind#2770] testToBooleanCoercionFailChars()209 public void testToBooleanCoercionFailChars() throws Exception 210 { 211 final String beanDoc = aposToQuotes("{'value':1}"); 212 _verifyBooleanCoerceFail("1", false, JsonToken.VALUE_NUMBER_INT, "1", Boolean.TYPE); 213 _verifyBooleanCoerceFail("1", false, JsonToken.VALUE_NUMBER_INT, "1", Boolean.class); 214 _verifyBooleanCoerceFail(beanDoc, false, JsonToken.VALUE_NUMBER_INT, "1", BooleanPOJO.class); 215 216 _verifyBooleanCoerceFail("1.25", false, JsonToken.VALUE_NUMBER_FLOAT, "1.25", Boolean.TYPE); 217 _verifyBooleanCoerceFail("1.25", false, JsonToken.VALUE_NUMBER_FLOAT, "1.25", Boolean.class); 218 } 219 testMiscCoercionFail()220 public void testMiscCoercionFail() throws Exception 221 { 222 // And then we have coercions from more esoteric types too 223 224 _verifyCoerceFail("65", Character.class, 225 "Cannot coerce Integer value (65) to `java.lang.Character` value"); 226 _verifyCoerceFail("65", Character.TYPE, 227 "Cannot coerce Integer value (65) to `char` value"); 228 } 229 230 /* 231 /********************************************************** 232 /* Helper methods 233 /********************************************************** 234 */ 235 _verifyCoerceSuccess(String input, Class<?> type, Object exp)236 private void _verifyCoerceSuccess(String input, Class<?> type, Object exp) throws IOException 237 { 238 Object result = COERCING_MAPPER.readerFor(type) 239 .readValue(input); 240 assertEquals(exp, result); 241 } 242 _verifyCoerceFail(String input, Class<?> type, String... expMatches)243 private void _verifyCoerceFail(String input, Class<?> type, 244 String... expMatches) throws IOException 245 { 246 try { 247 NOT_COERCING_MAPPER.readerFor(type) 248 .readValue(input); 249 fail("Should not have allowed coercion"); 250 } catch (MismatchedInputException e) { 251 verifyException(e, expMatches); 252 } 253 } 254 _verifyRootStringCoerceFail(String unquotedValue, Class<?> type)255 private void _verifyRootStringCoerceFail(String unquotedValue, Class<?> type) throws IOException 256 { 257 // Test failure for root value: for both byte- and char-backed sources: 258 259 final String input = quote(unquotedValue); 260 try (JsonParser p = NOT_COERCING_MAPPER.createParser(new StringReader(input))) { 261 _verifyStringCoerceFail(p, unquotedValue, type); 262 } 263 final byte[] inputBytes = utf8Bytes(input); 264 try (JsonParser p = NOT_COERCING_MAPPER.createParser(new ByteArrayInputStream(inputBytes))) { 265 _verifyStringCoerceFail(p, unquotedValue, type); 266 } 267 } 268 _verifyStringCoerceFail(JsonParser p, String unquotedValue, Class<?> type)269 private void _verifyStringCoerceFail(JsonParser p, 270 String unquotedValue, Class<?> type) throws IOException 271 { 272 try { 273 NOT_COERCING_MAPPER.readerFor(type) 274 .readValue(p); 275 fail("Should not have allowed coercion"); 276 } catch (MismatchedInputException e) { 277 verifyException(e, "Cannot coerce "); 278 verifyException(e, " to `"); 279 verifyException(e, "` value"); 280 281 assertNotNull(e.getProcessor()); 282 assertSame(p, e.getProcessor()); 283 284 assertToken(JsonToken.VALUE_STRING, p.currentToken()); 285 assertEquals(unquotedValue, p.getText()); 286 } 287 } 288 _verifyBooleanCoerceFail(String doc, boolean useBytes, JsonToken tokenType, String tokenValue, Class<?> targetType)289 private void _verifyBooleanCoerceFail(String doc, boolean useBytes, 290 JsonToken tokenType, String tokenValue, Class<?> targetType) throws IOException 291 { 292 // Test failure for root value: for both byte- and char-backed sources. 293 294 // [databind#2635]: important, need to use `readValue()` that takes content and NOT 295 // JsonParser, as this forces closing of underlying parser and exposes more issues. 296 297 final ObjectReader r = NOT_COERCING_MAPPER.readerFor(targetType); 298 try { 299 if (useBytes) { 300 r.readValue(utf8Bytes(doc)); 301 } else { 302 r.readValue(doc); 303 } 304 fail("Should not have allowed coercion"); 305 } catch (MismatchedInputException e) { 306 _verifyBooleanCoerceFailReason(e, tokenType, tokenValue); 307 } 308 } 309 310 @SuppressWarnings("resource") _verifyBooleanCoerceFailReason(MismatchedInputException e, JsonToken tokenType, String tokenValue)311 private void _verifyBooleanCoerceFailReason(MismatchedInputException e, 312 JsonToken tokenType, String tokenValue) throws IOException 313 { 314 // 2 different possibilities here 315 verifyException(e, "Cannot coerce Integer value", "Cannot deserialize value of type `"); 316 317 JsonParser p = (JsonParser) e.getProcessor(); 318 319 assertToken(tokenType, p.currentToken()); 320 321 final String text = p.getText(); 322 323 if (!tokenValue.equals(text)) { 324 String textDesc = (text == null) ? "NULL" : quote(text); 325 fail("Token text ("+textDesc+") via parser of type "+p.getClass().getName() 326 +" not as expected ("+quote(tokenValue)+"); exception message: '"+e.getMessage()+"'"); 327 } 328 } 329 } 330