1 package com.fasterxml.jackson.databind.deser.std;
2
3 import java.io.IOException;
4
5 import com.fasterxml.jackson.annotation.JsonFormat;
6 import com.fasterxml.jackson.core.*;
7 import com.fasterxml.jackson.databind.*;
8 import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
9 import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
10 import com.fasterxml.jackson.databind.deser.NullValueProvider;
11 import com.fasterxml.jackson.databind.deser.impl.NullsConstantProvider;
12 import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
13 import com.fasterxml.jackson.databind.util.AccessPattern;
14 import com.fasterxml.jackson.databind.util.ObjectBuffer;
15
16
21 @JacksonStdImpl
22 public final class StringArrayDeserializer
23
24
25
26 extends StdDeserializer<String[]>
27 implements ContextualDeserializer
28 {
29 private static final long serialVersionUID = 2L;
30
31 private final static String[] NO_STRINGS = new String[0];
32
33 public final static StringArrayDeserializer instance = new StringArrayDeserializer();
34
35
38 protected JsonDeserializer<String> _elementDeserializer;
39
40
45 protected final NullValueProvider _nullProvider;
46
47
54 protected final Boolean _unwrapSingle;
55
56
62 protected final boolean _skipNullValues;
63
64 public StringArrayDeserializer() {
65 this(null, null, null);
66 }
67
68 @SuppressWarnings("unchecked")
69 protected StringArrayDeserializer(JsonDeserializer<?> deser,
70 NullValueProvider nuller, Boolean unwrapSingle) {
71 super(String[].class);
72 _elementDeserializer = (JsonDeserializer<String>) deser;
73 _nullProvider = nuller;
74 _unwrapSingle = unwrapSingle;
75 _skipNullValues = NullsConstantProvider.isSkipper(nuller);
76 }
77
78 @Override
79 public Boolean supportsUpdate(DeserializationConfig config) {
80 return Boolean.TRUE;
81 }
82
83 @Override
84 public AccessPattern getEmptyAccessPattern() {
85
86 return AccessPattern.CONSTANT;
87 }
88
89 @Override
90 public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
91 return NO_STRINGS;
92 }
93
94
98 @Override
99 public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
100 throws JsonMappingException
101 {
102 JsonDeserializer<?> deser = _elementDeserializer;
103
104 deser = findConvertingContentDeserializer(ctxt, property, deser);
105 JavaType type = ctxt.constructType(String.class);
106 if (deser == null) {
107 deser = ctxt.findContextualValueDeserializer(type, property);
108 } else {
109 deser = ctxt.handleSecondaryContextualization(deser, property, type);
110 }
111
112 Boolean unwrapSingle = findFormatFeature(ctxt, property, String[].class,
113 JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
114 NullValueProvider nuller = findContentNullProvider(ctxt, property, deser);
115
116 if ((deser != null) && isDefaultDeserializer(deser)) {
117 deser = null;
118 }
119 if ((_elementDeserializer == deser)
120 && (_unwrapSingle == unwrapSingle)
121 && (_nullProvider == nuller)) {
122 return this;
123 }
124 return new StringArrayDeserializer(deser, nuller, unwrapSingle);
125 }
126
127 @Override
128 public String[] deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
129 {
130
131 if (!p.isExpectedStartArrayToken()) {
132 return handleNonArray(p, ctxt);
133 }
134 if (_elementDeserializer != null) {
135 return _deserializeCustom(p, ctxt, null);
136 }
137
138 final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
139 Object[] chunk = buffer.resetAndStart();
140
141 int ix = 0;
142
143 try {
144 while (true) {
145 String value = p.nextTextValue();
146 if (value == null) {
147 JsonToken t = p.getCurrentToken();
148 if (t == JsonToken.END_ARRAY) {
149 break;
150 }
151 if (t == JsonToken.VALUE_NULL) {
152 if (_skipNullValues) {
153 continue;
154 }
155 value = (String) _nullProvider.getNullValue(ctxt);
156 } else {
157 value = _parseString(p, ctxt);
158 }
159 }
160 if (ix >= chunk.length) {
161 chunk = buffer.appendCompletedChunk(chunk);
162 ix = 0;
163 }
164 chunk[ix++] = value;
165 }
166 } catch (Exception e) {
167 throw JsonMappingException.wrapWithPath(e, chunk, buffer.bufferedSize() + ix);
168 }
169 String[] result = buffer.completeAndClearBuffer(chunk, ix, String.class);
170 ctxt.returnObjectBuffer(buffer);
171 return result;
172 }
173
174
177 protected final String[] _deserializeCustom(JsonParser p, DeserializationContext ctxt,
178 String[] old) throws IOException
179 {
180 final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
181 int ix;
182 Object[] chunk;
183
184 if (old == null) {
185 ix = 0;
186 chunk = buffer.resetAndStart();
187 } else {
188 ix = old.length;
189 chunk = buffer.resetAndStart(old, ix);
190 }
191
192 final JsonDeserializer<String> deser = _elementDeserializer;
193
194 try {
195 while (true) {
196
201 String value;
202 if (p.nextTextValue() == null) {
203 JsonToken t = p.getCurrentToken();
204 if (t == JsonToken.END_ARRAY) {
205 break;
206 }
207
208 if (t == JsonToken.VALUE_NULL) {
209 if (_skipNullValues) {
210 continue;
211 }
212 value = (String) _nullProvider.getNullValue(ctxt);
213 } else {
214 value = deser.deserialize(p, ctxt);
215 }
216 } else {
217 value = deser.deserialize(p, ctxt);
218 }
219 if (ix >= chunk.length) {
220 chunk = buffer.appendCompletedChunk(chunk);
221 ix = 0;
222 }
223 chunk[ix++] = value;
224 }
225 } catch (Exception e) {
226
227 throw JsonMappingException.wrapWithPath(e, String.class, ix);
228 }
229 String[] result = buffer.completeAndClearBuffer(chunk, ix, String.class);
230 ctxt.returnObjectBuffer(buffer);
231 return result;
232 }
233
234 @Override
235 public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
236 return typeDeserializer.deserializeTypedFromArray(p, ctxt);
237 }
238
239 @Override
240 public String[] deserialize(JsonParser p, DeserializationContext ctxt,
241 String[] intoValue) throws IOException
242 {
243
244 if (!p.isExpectedStartArrayToken()) {
245 String[] arr = handleNonArray(p, ctxt);
246 if (arr == null) {
247 return intoValue;
248 }
249 final int offset = intoValue.length;
250 String[] result = new String[offset + arr.length];
251 System.arraycopy(intoValue, 0, result, 0, offset);
252 System.arraycopy(arr, 0, result, offset, arr.length);
253 return result;
254 }
255
256 if (_elementDeserializer != null) {
257 return _deserializeCustom(p, ctxt, intoValue);
258 }
259 final ObjectBuffer buffer = ctxt.leaseObjectBuffer();
260 int ix = intoValue.length;
261 Object[] chunk = buffer.resetAndStart(intoValue, ix);
262
263 try {
264 while (true) {
265 String value = p.nextTextValue();
266 if (value == null) {
267 JsonToken t = p.getCurrentToken();
268 if (t == JsonToken.END_ARRAY) {
269 break;
270 }
271 if (t == JsonToken.VALUE_NULL) {
272
273 if (_skipNullValues) {
274 return NO_STRINGS;
275 }
276 value = (String) _nullProvider.getNullValue(ctxt);
277 } else {
278 value = _parseString(p, ctxt);
279 }
280 }
281 if (ix >= chunk.length) {
282 chunk = buffer.appendCompletedChunk(chunk);
283 ix = 0;
284 }
285 chunk[ix++] = value;
286 }
287 } catch (Exception e) {
288 throw JsonMappingException.wrapWithPath(e, chunk, buffer.bufferedSize() + ix);
289 }
290 String[] result = buffer.completeAndClearBuffer(chunk, ix, String.class);
291 ctxt.returnObjectBuffer(buffer);
292 return result;
293 }
294
295 private final String[] handleNonArray(JsonParser p, DeserializationContext ctxt) throws IOException
296 {
297
298 boolean canWrap = (_unwrapSingle == Boolean.TRUE) ||
299 ((_unwrapSingle == null) &&
300 ctxt.isEnabled(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY));
301 if (canWrap) {
302 String value = p.hasToken(JsonToken.VALUE_NULL)
303 ? (String) _nullProvider.getNullValue(ctxt)
304 : _parseString(p, ctxt);
305 return new String[] { value };
306 }
307 if (p.hasToken(JsonToken.VALUE_STRING)
308 && ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
309 String str = p.getText();
310 if (str.length() == 0) {
311 return null;
312 }
313 }
314 return (String[]) ctxt.handleUnexpectedToken(_valueClass, p);
315 }
316 }
317