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