Skip to content

Commit 63dd152

Browse files
committed
Merge branch '2.x' into 3.x
2 parents f58cfbc + c3b7946 commit 63dd152

19 files changed

+414
-286
lines changed

release-notes/VERSION-2.x

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Project: jackson-databind
1212
(contributed by Giulio L)
1313
#4136: Drop deprecated (in 2.12) `PropertyNamingStrategy` implementations
1414
from 2.20
15+
#4656: `DeserializationProblemHandler.handleUnexpectedToken()` cast Object to String
16+
(reported by @yacine-pc)
1517
#5103: Use `writeStartObject(Object forValue, int size)` for `ObjectNode`
1618
serialization
1719
#5151: Add new exception type, `MissingInjectValueException`, to be used

src/main/java/tools/jackson/databind/DeserializationContext.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,17 +1101,22 @@ public Calendar constructCalendar(Date d) {
11011101
* Method to call in case incoming shape is Object Value (and parser thereby
11021102
* points to {@link tools.jackson.core.JsonToken#START_OBJECT} token),
11031103
* but a Scalar value (potentially coercible from String value) is expected.
1104-
* This would typically be used to deserializer a Number, Boolean value or some other
1104+
* This would typically be used to deserialize a Number, Boolean value or some other
11051105
* "simple" unstructured value type.
1106+
*<p>
1107+
* Note that expected behavior in case of extraction not succeeding changed in
1108+
* Jackson 2.20: now {@code null} is expected to be returned in that case (in 2.19
1109+
* and before, exception was to be thrown, but this prevented fallback handling
1110+
* via {@link DeserializationProblemHandler#handleUnexpectedToken}.
11061111
*
11071112
* @param p Actual parser to read content from
11081113
* @param deser Deserializer that needs extracted String value
11091114
* @param scalarType Immediate type of scalar to extract; usually type deserializer
11101115
* handles but not always (for example, deserializer for {@code int[]} would pass
11111116
* scalar type of {@code int})
11121117
*
1113-
* @return String value found; not {@code null} (exception should be thrown if no suitable
1114-
* value found)
1118+
* @return String value found, if any; {@code null} if none (note: changed in 2.20;
1119+
* before that in 2.19 and before exception throwing was expected)
11151120
*
11161121
* @throws JacksonException If there are problems either reading content (underlying parser
11171122
* problem) or finding expected scalar value
@@ -1120,7 +1125,10 @@ public String extractScalarFromObject(JsonParser p, ValueDeserializer<?> deser,
11201125
Class<?> scalarType)
11211126
throws JacksonException
11221127
{
1123-
return (String) handleUnexpectedToken(constructType(scalarType), p);
1128+
// 17-May-2025, tatu: [databind#4656] must NOT call `handleUnexpectedToken`
1129+
// since that can return value other than {@code String}
1130+
// return (String) handleUnexpectedToken(constructType(scalarType), p);
1131+
return null;
11241132
}
11251133

11261134
/*

src/main/java/tools/jackson/databind/deser/jdk/EnumDeserializer.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,12 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt)
195195
}
196196
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
197197
if (p.isExpectedStartObjectToken()) {
198-
return _fromString(p, ctxt,
199-
ctxt.extractScalarFromObject(p, this, _valueClass));
198+
String str = ctxt.extractScalarFromObject(p, this, _valueClass);
199+
// 17-May-2025, tatu: [databind#4656] need to check for `null`
200+
if (str == null) {
201+
return ctxt.handleUnexpectedToken(_enumClass(), p);
202+
}
203+
return _fromString(p, ctxt, str);
200204
}
201205
return _deserializeOther(p, ctxt);
202206
}

src/main/java/tools/jackson/databind/deser/jdk/FactoryBasedEnumDeserializer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt)
164164
// https://github.com/FasterXML/jackson-databind/issues/4807
165165
if (t == JsonToken.START_OBJECT) {
166166
value = ctxt.extractScalarFromObject(p, this, _valueClass);
167+
// 17-May-2025, tatu: [databind#4656] need to check for `null`
168+
if (value == null) {
169+
return ctxt.handleUnexpectedToken(_valueClass, p);
170+
}
167171
} else if ((t == null) || !t.isScalarValue()) {
168172
// Could argue we should throw an exception but...
169173
// 01-Jun-2023, tatu: And now we will finally do it!

src/main/java/tools/jackson/databind/deser/jdk/NumberDeserializers.java

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,11 @@ protected Byte _parseByte(JsonParser p, DeserializationContext ctxt)
288288
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
289289
case JsonTokenId.ID_START_OBJECT:
290290
text = ctxt.extractScalarFromObject(p, this, _valueClass);
291-
break;
291+
// 17-May-2025, tatu: [databind#4656] need to check for `null`
292+
if (text != null) {
293+
break;
294+
}
295+
// fall through
292296
default:
293297
return (Byte) ctxt.handleUnexpectedToken(getValueType(ctxt), p);
294298
}
@@ -369,12 +373,16 @@ protected Short _parseShort(JsonParser p, DeserializationContext ctxt)
369373
return (Short) getNullValue(ctxt);
370374
case JsonTokenId.ID_NUMBER_INT:
371375
return p.getShortValue();
376+
case JsonTokenId.ID_START_ARRAY:
377+
return (Short)_deserializeFromArray(p, ctxt);
372378
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
373379
case JsonTokenId.ID_START_OBJECT:
380+
// 17-May-2025, tatu: [databind#4656] need to check for `null`
374381
text = ctxt.extractScalarFromObject(p, this, _valueClass);
375-
break;
376-
case JsonTokenId.ID_START_ARRAY:
377-
return (Short)_deserializeFromArray(p, ctxt);
382+
if (text != null) {
383+
break;
384+
}
385+
// fall through
378386
default:
379387
return (Short) ctxt.handleUnexpectedToken(getValueType(ctxt), p);
380388
}
@@ -457,12 +465,16 @@ public Character deserialize(JsonParser p, DeserializationContext ctxt)
457465
_verifyNullForPrimitive(ctxt);
458466
}
459467
return (Character) getNullValue(ctxt);
468+
case JsonTokenId.ID_START_ARRAY:
469+
return _deserializeFromArray(p, ctxt);
460470
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
461471
case JsonTokenId.ID_START_OBJECT:
462472
text = ctxt.extractScalarFromObject(p, this, _valueClass);
463-
break;
464-
case JsonTokenId.ID_START_ARRAY:
465-
return _deserializeFromArray(p, ctxt);
473+
// 17-May-2025, tatu: [databind#4656] need to check for `null`
474+
if (text != null) {
475+
break;
476+
}
477+
// fall through
466478
default:
467479
return (Character) ctxt.handleUnexpectedToken(getValueType(ctxt), p);
468480
}
@@ -597,12 +609,16 @@ protected final Float _parseFloat(JsonParser p, DeserializationContext ctxt)
597609
// fall through to coerce
598610
case JsonTokenId.ID_NUMBER_FLOAT:
599611
return p.getFloatValue();
612+
case JsonTokenId.ID_START_ARRAY:
613+
return _deserializeFromArray(p, ctxt);
600614
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
601615
case JsonTokenId.ID_START_OBJECT:
602616
text = ctxt.extractScalarFromObject(p, this, _valueClass);
603-
break;
604-
case JsonTokenId.ID_START_ARRAY:
605-
return _deserializeFromArray(p, ctxt);
617+
// 17-May-2025, tatu: [databind#4656] need to check for `null`
618+
if (text != null) {
619+
break;
620+
}
621+
// fall through
606622
default:
607623
return (Float) ctxt.handleUnexpectedToken(getValueType(ctxt), p);
608624
}
@@ -696,12 +712,16 @@ protected final Double _parseDouble(JsonParser p, DeserializationContext ctxt) t
696712
// fall through to coerce
697713
case JsonTokenId.ID_NUMBER_FLOAT: // safe coercion
698714
return p.getDoubleValue();
715+
case JsonTokenId.ID_START_ARRAY:
716+
return _deserializeFromArray(p, ctxt);
699717
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
700718
case JsonTokenId.ID_START_OBJECT:
701719
text = ctxt.extractScalarFromObject(p, this, _valueClass);
702-
break;
703-
case JsonTokenId.ID_START_ARRAY:
704-
return _deserializeFromArray(p, ctxt);
720+
// 17-May-2025, tatu: [databind#4656] need to check for `null`
721+
if (text != null) {
722+
break;
723+
}
724+
// fall through
705725
default:
706726
return (Double) ctxt.handleUnexpectedToken(getValueType(ctxt), p);
707727
}
@@ -788,12 +808,16 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws Jack
788808
}
789809
}
790810
return p.getNumberValue();
811+
case JsonTokenId.ID_START_ARRAY:
812+
return _deserializeFromArray(p, ctxt);
791813
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
792814
case JsonTokenId.ID_START_OBJECT:
793815
text = ctxt.extractScalarFromObject(p, this, _valueClass);
794-
break;
795-
case JsonTokenId.ID_START_ARRAY:
796-
return _deserializeFromArray(p, ctxt);
816+
// 17-May-2025, tatu: [databind#4656] need to check for `null`
817+
if (text != null) {
818+
break;
819+
}
820+
// fall through
797821
default:
798822
return ctxt.handleUnexpectedToken(getValueType(ctxt), p);
799823
}
@@ -922,12 +946,16 @@ public BigInteger deserialize(JsonParser p, DeserializationContext ctxt) throws
922946
final BigDecimal bd = p.getDecimalValue();
923947
p.streamReadConstraints().validateBigIntegerScale(bd.scale());
924948
return bd.toBigInteger();
949+
case JsonTokenId.ID_START_ARRAY:
950+
return _deserializeFromArray(p, ctxt);
925951
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
926952
case JsonTokenId.ID_START_OBJECT:
927953
text = ctxt.extractScalarFromObject(p, this, _valueClass);
928-
break;
929-
case JsonTokenId.ID_START_ARRAY:
930-
return _deserializeFromArray(p, ctxt);
954+
// 17-May-2025, tatu: [databind#4656] need to check for `null`
955+
if (text != null) {
956+
break;
957+
}
958+
// fall through
931959
default:
932960
// String is ok too, can easily convert; otherwise, no can do:
933961
return (BigInteger) ctxt.handleUnexpectedToken(getValueType(ctxt), p);
@@ -994,12 +1022,16 @@ public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt)
9941022
case JsonTokenId.ID_STRING:
9951023
text = p.getString();
9961024
break;
1025+
case JsonTokenId.ID_START_ARRAY:
1026+
return _deserializeFromArray(p, ctxt);
9971027
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
9981028
case JsonTokenId.ID_START_OBJECT:
9991029
text = ctxt.extractScalarFromObject(p, this, _valueClass);
1000-
break;
1001-
case JsonTokenId.ID_START_ARRAY:
1002-
return _deserializeFromArray(p, ctxt);
1030+
// 17-May-2025, tatu: [databind#4656] need to check for `null`
1031+
if (text != null) {
1032+
break;
1033+
}
1034+
// fall through
10031035
default:
10041036
return (BigDecimal) ctxt.handleUnexpectedToken(getValueType(ctxt), p);
10051037
}

src/main/java/tools/jackson/databind/deser/std/FromStringDeserializer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ public T deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonEx
5858
}
5959
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
6060
text = ctxt.extractScalarFromObject(p, this, _valueClass);
61+
// 17-May-2025, tatu: [databind#4656] need to check for `null`
62+
if (text == null) {
63+
return (T) ctxt.handleUnexpectedToken(_valueClass, p);
64+
}
6165
}
6266
if (text.isEmpty()) {
6367
// 09-Jun-2020, tatu: Commonly `null` but may coerce to "empty" as well

0 commit comments

Comments
 (0)