• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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