Skip to content
This repository was archived by the owner on Nov 5, 2019. It is now read-only.

Commit d68ed5e

Browse files
committed
Fix #56
1 parent 3380fe1 commit d68ed5e

14 files changed

+313
-60
lines changed

release-notes/VERSION

+5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Project: jackson-module-afterburner
44
=== Releases ===
55
------------------------------------------------------------------------
66

7+
2.6.2 (not yet released)
8+
9+
#56: Afterburner does not respect DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS
10+
(reported by shollander@github)
11+
712
2.6.1 (09-Aug-2015)
813

914
No changes since 2.6.0

src/main/java/com/fasterxml/jackson/module/afterburner/deser/OptimizedSettableBeanProperty.java

+189-27
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import java.lang.annotation.Annotation;
55

66
import com.fasterxml.jackson.core.*;
7-
7+
import com.fasterxml.jackson.core.JsonParser.NumberType;
8+
import com.fasterxml.jackson.core.io.NumberInput;
89
import com.fasterxml.jackson.databind.*;
910
import com.fasterxml.jackson.databind.deser.*;
1011
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
@@ -144,52 +145,213 @@ public int getOptimizedIndex() {
144145
protected final boolean _deserializeBoolean(JsonParser p, DeserializationContext ctxt) throws IOException
145146
{
146147
JsonToken t = p.getCurrentToken();
147-
if (t == JsonToken.VALUE_TRUE) {
148-
return true;
148+
if (t == JsonToken.VALUE_TRUE) return true;
149+
if (t == JsonToken.VALUE_FALSE) return false;
150+
if (t == JsonToken.VALUE_NULL) return false;
151+
152+
// [JACKSON-78]: should accept ints too, (0 == false, otherwise true)
153+
if (t == JsonToken.VALUE_NUMBER_INT) {
154+
// 11-Jan-2012, tatus: May be outside of int...
155+
if (p.getNumberType() == NumberType.INT) {
156+
return (p.getIntValue() != 0);
157+
}
158+
return _deserializeBooleanFromOther(p, ctxt);
149159
}
150-
if (t == JsonToken.VALUE_FALSE) {
151-
return false;
160+
// And finally, let's allow Strings to be converted too
161+
if (t == JsonToken.VALUE_STRING) {
162+
String text = p.getText().trim();
163+
// [#422]: Allow aliases
164+
if ("true".equals(text) || "True".equals(text)) {
165+
return true;
166+
}
167+
if ("false".equals(text) || "False".equals(text) || text.length() == 0) {
168+
return false;
169+
}
170+
if (_hasTextualNull(text)) {
171+
return false;
172+
}
173+
throw ctxt.weirdStringException(text, Boolean.TYPE, "only \"true\" or \"false\" recognized");
152174
}
153-
return p.getValueAsBoolean();
175+
// [databind#381]
176+
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
177+
p.nextToken();
178+
final boolean parsed = _deserializeBooleanFromOther(p, ctxt);
179+
t = p.nextToken();
180+
if (t != JsonToken.END_ARRAY) {
181+
throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
182+
"Attempted to unwrap single value array for single 'boolean' value but there was more than a single value in the array");
183+
}
184+
return parsed;
185+
}
186+
// Otherwise, no can do:
187+
throw ctxt.mappingException(Boolean.TYPE, t);
154188
}
155189

156-
protected final String _deserializeString(JsonParser p, DeserializationContext ctxt) throws IOException
190+
protected final short _deserializeShort(JsonParser jp, DeserializationContext ctxt)
191+
throws IOException
157192
{
158-
String text = p.getValueAsString();
159-
if (text != null) {
160-
return text;
193+
int value = _deserializeInt(jp, ctxt);
194+
// So far so good: but does it fit?
195+
if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
196+
throw ctxt.weirdStringException(String.valueOf(value),
197+
Short.TYPE, "overflow, value can not be represented as 16-bit value");
161198
}
162-
return _convertToString(p, ctxt);
199+
return (short) value;
163200
}
164201

165-
/**
166-
* Helper method for coercing JSON values other than Strings into
167-
* Java String value.
168-
*/
169-
protected final String _convertToString(JsonParser p, DeserializationContext ctxt) throws IOException
202+
protected final int _deserializeInt(JsonParser p, DeserializationContext ctxt)
203+
throws IOException
170204
{
171-
JsonToken curr = p.getCurrentToken();
172-
if (curr == JsonToken.VALUE_NULL) { // should this ever happen?
173-
if (_valueDeserializer == null) {
174-
return null;
205+
if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
206+
return p.getIntValue();
207+
}
208+
JsonToken t = p.getCurrentToken();
209+
if (t == JsonToken.VALUE_STRING) { // let's do implicit re-parse
210+
String text = p.getText().trim();
211+
if (_hasTextualNull(text)) {
212+
return 0;
213+
}
214+
try {
215+
int len = text.length();
216+
if (len > 9) {
217+
long l = Long.parseLong(text);
218+
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
219+
throw ctxt.weirdStringException(text, Integer.TYPE,
220+
"Overflow: numeric value ("+text+") out of range of int ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")");
221+
}
222+
return (int) l;
223+
}
224+
if (len == 0) {
225+
return 0;
226+
}
227+
return NumberInput.parseInt(text);
228+
} catch (IllegalArgumentException iae) {
229+
throw ctxt.weirdStringException(text, Integer.TYPE, "not a valid int value");
175230
}
176-
return (String) _valueDeserializer.getNullValue(ctxt);
177231
}
178-
if (curr == JsonToken.VALUE_EMBEDDED_OBJECT) {
232+
if (t == JsonToken.VALUE_NUMBER_FLOAT) {
233+
if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
234+
_failDoubleToIntCoercion(p, ctxt, "int");
235+
}
236+
return p.getValueAsInt();
237+
}
238+
if (t == JsonToken.VALUE_NULL) {
239+
return 0;
240+
}
241+
if (t == JsonToken.START_ARRAY && ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
242+
p.nextToken();
243+
final int parsed = _deserializeInt(p, ctxt);
244+
t = p.nextToken();
245+
if (t != JsonToken.END_ARRAY) {
246+
throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
247+
"Attempted to unwrap single value array for single 'int' value but there was more than a single value in the array");
248+
}
249+
return parsed;
250+
}
251+
// Otherwise, no can do:
252+
throw ctxt.mappingException(Integer.TYPE, t);
253+
}
254+
255+
protected final long _deserializeLong(JsonParser p, DeserializationContext ctxt)
256+
throws IOException
257+
{
258+
switch (p.getCurrentTokenId()) {
259+
case JsonTokenId.ID_NUMBER_INT:
260+
return p.getLongValue();
261+
case JsonTokenId.ID_NUMBER_FLOAT:
262+
if (!ctxt.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT)) {
263+
_failDoubleToIntCoercion(p, ctxt, "long");
264+
}
265+
return p.getValueAsLong();
266+
case JsonTokenId.ID_STRING:
267+
String text = p.getText().trim();
268+
if (text.length() == 0 || _hasTextualNull(text)) {
269+
return 0L;
270+
}
271+
try {
272+
return NumberInput.parseLong(text);
273+
} catch (IllegalArgumentException iae) { }
274+
throw ctxt.weirdStringException(text, Long.TYPE, "not a valid long value");
275+
case JsonTokenId.ID_NULL:
276+
return 0L;
277+
case JsonTokenId.ID_START_ARRAY:
278+
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
279+
p.nextToken();
280+
final long parsed = _deserializeLong(p, ctxt);
281+
JsonToken t = p.nextToken();
282+
if (t != JsonToken.END_ARRAY) {
283+
throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
284+
"Attempted to unwrap single value array for single 'long' value but there was more than a single value in the array");
285+
}
286+
return parsed;
287+
}
288+
break;
289+
}
290+
throw ctxt.mappingException(Long.TYPE, p.getCurrentToken());
291+
}
292+
293+
protected final String _deserializeString(JsonParser p, DeserializationContext ctxt) throws IOException
294+
{
295+
switch (p.getCurrentTokenId()) {
296+
case JsonTokenId.ID_STRING:
297+
return p.getText();
298+
case JsonTokenId.ID_NULL:
299+
return null;
300+
case JsonTokenId.ID_START_ARRAY:
301+
if (ctxt.isEnabled(DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS)) {
302+
p.nextToken();
303+
final String parsed = _deserializeString(p, ctxt);
304+
if (p.nextToken() != JsonToken.END_ARRAY) {
305+
throw ctxt.wrongTokenException(p, JsonToken.END_ARRAY,
306+
"Attempted to unwrap single value array for single 'String' value but there was more than a single value in the array");
307+
}
308+
return parsed;
309+
}
310+
break;
311+
case JsonTokenId.ID_EMBEDDED_OBJECT:
179312
Object ob = p.getEmbeddedObject();
180313
if (ob == null) {
181314
return null;
182315
}
183316
if (ob instanceof byte[]) {
184317
return Base64Variants.getDefaultVariant().encode((byte[]) ob, false);
185318
}
319+
// otherwise, try conversion using toString()...
186320
return ob.toString();
321+
default:
322+
// allow coercions for other scalar types
323+
String text = p.getValueAsString();
324+
if (text != null) {
325+
return text;
326+
}
187327
}
188-
// Can deserialize any scalar value
189-
if (curr.isScalarValue()) { // should have been handled earlier, but just in case...
190-
return p.getText();
328+
throw ctxt.mappingException(String.class, p.getCurrentToken());
329+
}
330+
331+
protected final boolean _deserializeBooleanFromOther(JsonParser p, DeserializationContext ctxt)
332+
throws IOException
333+
{
334+
if (p.getNumberType() == NumberType.LONG) {
335+
return (p.getLongValue() == 0L) ? Boolean.FALSE : Boolean.TRUE;
191336
}
192-
// but not markers:
193-
throw ctxt.mappingException(String.class);
337+
// no really good logic; let's actually resort to textual comparison
338+
String str = p.getText();
339+
if ("0.0".equals(str) || "0".equals(str)) {
340+
return Boolean.FALSE;
341+
}
342+
return Boolean.TRUE;
343+
}
344+
345+
// // More helper methods from StdDeserializer
346+
347+
protected void _failDoubleToIntCoercion(JsonParser p, DeserializationContext ctxt,
348+
String type) throws IOException
349+
{
350+
throw ctxt.mappingException("Can not coerce a floating-point value ('%s') into %s; enable `DeserializationFeature.ACCEPT_FLOAT_AS_INT` to allow",
351+
p.getValueAsString(), type);
352+
}
353+
354+
protected boolean _hasTextualNull(String value) {
355+
return "null".equals(value);
194356
}
195357
}

src/main/java/com/fasterxml/jackson/module/afterburner/deser/SettableBooleanFieldProperty.java

+22-4
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,17 @@ public SettableBooleanFieldProperty withMutator(BeanPropertyMutator mut) {
4949
*/
5050

5151
@Override
52-
public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, Object bean) throws IOException {
53-
_propertyMutator.booleanField(bean, _deserializeBoolean(jp, ctxt));
52+
public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object bean) throws IOException {
53+
boolean b;
54+
JsonToken t = p.getCurrentToken();
55+
if (t == JsonToken.VALUE_TRUE) {
56+
b = true;
57+
} else if (t == JsonToken.VALUE_FALSE) {
58+
b = false;
59+
} else {
60+
b = _deserializeBoolean(p, ctxt);
61+
}
62+
_propertyMutator.booleanField(bean, b);
5463
}
5564

5665
@Override
@@ -60,9 +69,18 @@ public void set(Object bean, Object value) throws IOException {
6069
}
6170

6271
@Override
63-
public Object deserializeSetAndReturn(JsonParser jp,
72+
public Object deserializeSetAndReturn(JsonParser p,
6473
DeserializationContext ctxt, Object instance) throws IOException
6574
{
66-
return setAndReturn(instance, _deserializeBoolean(jp, ctxt));
75+
boolean b;
76+
JsonToken t = p.getCurrentToken();
77+
if (t == JsonToken.VALUE_TRUE) {
78+
b = true;
79+
} else if (t == JsonToken.VALUE_FALSE) {
80+
b = false;
81+
} else {
82+
b = _deserializeBoolean(p, ctxt);
83+
}
84+
return setAndReturn(instance, b);
6785
}
6886
}

src/main/java/com/fasterxml/jackson/module/afterburner/deser/SettableBooleanMethodProperty.java

+22-4
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,17 @@ public SettableBooleanMethodProperty withMutator(BeanPropertyMutator mut) {
4747
*/
4848

4949
@Override
50-
public void deserializeAndSet(JsonParser jp, DeserializationContext ctxt, Object bean) throws IOException {
51-
_propertyMutator.booleanSetter(bean, _deserializeBoolean(jp, ctxt));
50+
public void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object bean) throws IOException {
51+
boolean b;
52+
JsonToken t = p.getCurrentToken();
53+
if (t == JsonToken.VALUE_TRUE) {
54+
b = true;
55+
} else if (t == JsonToken.VALUE_FALSE) {
56+
b = false;
57+
} else {
58+
b = _deserializeBoolean(p, ctxt);
59+
}
60+
_propertyMutator.booleanSetter(bean, b);
5261
}
5362

5463
@Override
@@ -58,9 +67,18 @@ public void set(Object bean, Object value) throws IOException {
5867
}
5968

6069
@Override
61-
public Object deserializeSetAndReturn(JsonParser jp,
70+
public Object deserializeSetAndReturn(JsonParser p,
6271
DeserializationContext ctxt, Object instance) throws IOException
6372
{
64-
return setAndReturn(instance, _deserializeBoolean(jp, ctxt));
73+
boolean b;
74+
JsonToken t = p.getCurrentToken();
75+
if (t == JsonToken.VALUE_TRUE) {
76+
b = true;
77+
} else if (t == JsonToken.VALUE_FALSE) {
78+
b = false;
79+
} else {
80+
b = _deserializeBoolean(p, ctxt);
81+
}
82+
return setAndReturn(instance, b);
6583
}
6684
}

src/main/java/com/fasterxml/jackson/module/afterburner/deser/SettableIntFieldProperty.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ public SettableIntFieldProperty withMutator(BeanPropertyMutator mut) {
5252
public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
5353
Object bean) throws IOException
5454
{
55-
_propertyMutator.intField(bean, p.getValueAsInt());
55+
int v = p.hasToken(JsonToken.VALUE_NUMBER_INT) ? p.getIntValue() : _deserializeInt(p, ctxt);
56+
_propertyMutator.intField(bean, v);
5657
}
5758

5859
@Override
@@ -65,6 +66,7 @@ public void set(Object bean, Object value) throws IOException {
6566
public Object deserializeSetAndReturn(JsonParser p,
6667
DeserializationContext ctxt, Object instance) throws IOException
6768
{
68-
return setAndReturn(instance, p.getValueAsInt());
69+
int v = p.hasToken(JsonToken.VALUE_NUMBER_INT) ? p.getIntValue() : _deserializeInt(p, ctxt);
70+
return setAndReturn(instance, v);
6971
}
7072
}

src/main/java/com/fasterxml/jackson/module/afterburner/deser/SettableIntMethodProperty.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public SettableIntMethodProperty withMutator(BeanPropertyMutator mut) {
5050
public void deserializeAndSet(JsonParser p, DeserializationContext ctxt,
5151
Object bean) throws IOException
5252
{
53-
_propertyMutator.intSetter(bean, p.getValueAsInt());
53+
int v = p.hasToken(JsonToken.VALUE_NUMBER_INT) ? p.getIntValue() : _deserializeInt(p, ctxt);
54+
_propertyMutator.intSetter(bean, v);
5455
}
5556

5657
@Override
@@ -64,6 +65,7 @@ public Object deserializeSetAndReturn(JsonParser p,
6465
DeserializationContext ctxt, Object instance)
6566
throws IOException
6667
{
67-
return setAndReturn(instance, p.getValueAsInt());
68+
int v = p.hasToken(JsonToken.VALUE_NUMBER_INT) ? p.getIntValue() : _deserializeInt(p, ctxt);
69+
return setAndReturn(instance, v);
6870
}
6971
}

0 commit comments

Comments
 (0)