Skip to content

Commit 26699c8

Browse files
authored
Merge branch 'master' into ckozak/test_reproducing_2_11_3_regression
2 parents 892b99a + 5636626 commit 26699c8

38 files changed

+991
-129
lines changed

README.md

+28
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,34 @@ assertEquals("Hassan", person.getName());
443443
assertEquals(23, person.getAge().intValue());
444444
```
445445

446+
If your builder pattern implementation uses other prefixes for methods or uses other names than build() for the builder method Jackson also provide a handy way for you.
447+
448+
For example, if you have a builder class uses the "set" prefix for its methods and use the create() method instead of build() for building the whole class, you have to annotate your class like:
449+
```java
450+
@JsonPOJOBuilder(buildMethodName = "create", withPrefix = "set")
451+
static class Builder {
452+
String name;
453+
Integer age;
454+
455+
Builder setName(String name) {
456+
this.name = name;
457+
return this;
458+
}
459+
460+
Builder setAge(Integer age) {
461+
this.age = age;
462+
return this;
463+
}
464+
465+
public Person create() {
466+
return new Person(name, age);
467+
}
468+
}
469+
```
470+
471+
472+
473+
446474
Overall, Jackson library is very powerful in deserializing objects using builder pattern.
447475

448476
# Contribute!

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
<dependency>
103103
<groupId>javax.measure</groupId>
104104
<artifactId>jsr-275</artifactId>
105-
<version>1.0.0</version>
105+
<version>0.9.1</version>
106106
<scope>test</scope>
107107
</dependency>
108108

release-notes/CREDITS-2.x

+19
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,10 @@ Daniel Wu (DanielYWoo@github)
11641164
* Reported #2840: `ObjectMapper.activateDefaultTypingAsProperty()` is not using
11651165
(2.11.3)
11661166
1167+
Łukasz Walkiewicz (lukasz-walkiewicz@github)
1168+
* Reported #2894: Fix type resolution for static methods (regression in 2.11.3)
1169+
(2.11.4)
1170+
11671171
Marc Carter (drekbour@github)
11681172
* Contributed #43 implementation: Add option to resolve type from multiple existing properties,
11691173
`@JsonTypeInfo(use=DEDUCTION)`
@@ -1226,6 +1230,21 @@ Swayam Raina (swayamraina@github)
12261230
* Contributed #2761: Support multiple names in `JsonSubType.Type`
12271231
(2.12.0)
12281232
1233+
Oguzhan Unlu (oguzhanunlu@github)
1234+
* Requested #2855: Add `JsonNode.canConvertToExactIntegral()` to indicate whether
1235+
floating-point/BigDecimal values could be converted to integers losslessly
1236+
(2.12.0)
1237+
1238+
Siavash Soleymani (siavashsoleymani@github)
1239+
* Contributed implementation for #2855:Add `JsonNode.canConvertToExactIntegral()` to
1240+
indicate whether floating-point/BigDecimal values could be converted to integers losslessly
1241+
(2.12.0)
1242+
12291243
Ilya Golovin (ilgo0413@github)
12301244
* Contributed #2873: `MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS` should work for enum as keys
12311245
(2.12.0)
1246+
1247+
Sergiy Yevtushenko (siy@github)
1248+
* Contributed #2879: Add support for disabling special handling of "Creator properties" wrt
1249+
alphabetic property ordering
1250+
(2.12.0)

release-notes/VERSION-2.x

+17-4
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,28 @@ Project: jackson-databind
44
=== Releases ===
55
------------------------------------------------------------------------
66

7-
87
2.12.0-rc2 (not yet released)
98

109
#1458: `@JsonAnyGetter` should be allowed on a field
1110
(contributed by Dominik K)
1211
#2775: Disabling `FAIL_ON_INVALID_SUBTYPE` breaks polymorphic deserialization of Enums
1312
(reported by holgerknoche@github)
13+
#2804: Throw `InvalidFormatException` instead of `MismatchedInputException`
14+
for ACCEPT_FLOAT_AS_INT coercion failures
15+
(requested by mjustin@github)
1416
#2878: Revert change initially made to fix #2805: change in signature
1517
of `ObjectMapper.treeToValue()` regarding exceptions
18+
#2879: Add support for disabling special handling of "Creator properties" wrt
19+
alphabetic property ordering
20+
(contributed by Sergiy Y)
1621
#2880: Revert removal of 2.7-deprecated `PropertyNamingStrategy` constants
1722
(reported by brettkail-wk@github)
18-
#2804: Throw `InvalidFormatException` instead of `MismatchedInputException`
19-
for ACCEPT_FLOAT_AS_INT coercion failures
20-
(requested by mjustin@github)
23+
#2885: Add `JsonNode.canConvertToExactIntegral()` to indicate whether floating-point/BigDecimal
24+
values could be converted to integers losslessly
25+
(requested by Oguzhan U; implementation contributed by Siavash S)
26+
#2903: Allow preventing "Enum from integer" coercion using new `CoercionConfig` system
27+
#2909: `@JsonValue` not considered when evaluating inclusion
28+
(reported by chrylis@github)
2129

2230
2.12.0-rc1 (12-Oct-2020)
2331

@@ -85,6 +93,11 @@ Project: jackson-databind
8593
- Add `BeanDeserializerBase.isCaseInsensitive()`
8694
- Some refactoring of `CollectionDeserializer` to solve CSV array handling issues
8795

96+
2.11.4 (not yet released)
97+
98+
#2894: Fix type resolution for static methods (regression in 2.11.3 due to #2821 fix)
99+
(reported by Łukasz W)
100+
88101
2.11.3 (02-Oct-2020)
89102

90103
#2795: Cannot detect creator arguments of mixins for JDK types

src/main/java/com/fasterxml/jackson/databind/JsonNode.java

+23
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,29 @@ public final boolean isBinary() {
393393
*/
394394
public boolean canConvertToLong() { return false; }
395395

396+
/**
397+
* Method that can be used to check whether contained value
398+
* is numeric (returns true for {@link #isNumber()}) and
399+
* can be losslessly converted to integral number (specifically,
400+
* {@link BigInteger} but potentially others, see
401+
* {@link #canConvertToInt} and {@link #canConvertToInt}).
402+
* Latter part allows floating-point numbers
403+
* (for which {@link #isFloatingPointNumber()} returns {@code true})
404+
* that do not have fractional part.
405+
* Note that "not-a-number" values of {@code double} and {@code float}
406+
* will return {@code false} as they can not be converted to matching
407+
* integral representations.
408+
*
409+
* @return True if the value is an actual number with no fractional
410+
* part; false for non-numeric types, NaN representations of floating-point
411+
* numbers, and floating-point numbers with fractional part.
412+
*
413+
* @since 2.12
414+
*/
415+
public boolean canConvertToExactIntegral() {
416+
return isIntegralNumber();
417+
}
418+
396419
/*
397420
/**********************************************************************
398421
/* Public API, straight value access

src/main/java/com/fasterxml/jackson/databind/MapperFeature.java

+22-3
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,8 @@ public enum MapperFeature implements ConfigFeature
256256

257257
/**
258258
* Feature that defines default property serialization order used
259-
* for POJO fields (note: does <b>not</b> apply to {@link java.util.Map}
260-
* serialization!):
261-
* if enabled, default ordering is alphabetic (similar to
259+
* for POJO properties.
260+
* If enabled, default ordering is alphabetic (similar to
262261
* how {@link com.fasterxml.jackson.annotation.JsonPropertyOrder#alphabetic()}
263262
* works); if disabled, order is unspecified (based on what JDK gives
264263
* us, which may be declaration order, but is not guaranteed).
@@ -267,10 +266,30 @@ public enum MapperFeature implements ConfigFeature
267266
* explicit overrides in classes (for example with
268267
* {@link com.fasterxml.jackson.annotation.JsonPropertyOrder} annotation)
269268
*<p>
269+
* Note: does <b>not</b> apply to {@link java.util.Map} serialization (since
270+
* entries are not considered Bean/POJO properties.
271+
*<p>
270272
* Feature is disabled by default.
271273
*/
272274
SORT_PROPERTIES_ALPHABETICALLY(false),
273275

276+
/**
277+
* Feature that defines whether Creator properties (ones passed through
278+
* constructor or static factory method) should be sorted before other properties
279+
* for which no explicit order is specified, in case where alphabetic
280+
* ordering is to be used for such properties.
281+
* Note that in either case explicit order (whether by name or by index)
282+
* will have precedence over this setting.
283+
*<p>
284+
* Note: does <b>not</b> apply to {@link java.util.Map} serialization (since
285+
* entries are not considered Bean/POJO properties.
286+
*<p>
287+
* Feature is enabled by default.
288+
*
289+
* @since 2.12
290+
*/
291+
SORT_CREATOR_PROPERTIES_FIRST(true),
292+
274293
/*
275294
/**********************************************************************
276295
/* Name-related features

src/main/java/com/fasterxml/jackson/databind/cfg/CoercionConfigs.java

+17-7
Original file line numberDiff line numberDiff line change
@@ -194,16 +194,26 @@ public CoercionAction findCoercion(DeserializationConfig config,
194194
}
195195

196196
// Otherwise there are some legacy features that can provide answer
197-
if (inputShape == CoercionInputShape.EmptyArray) {
197+
switch (inputShape) {
198+
case EmptyArray:
198199
// Default for setting is false
199200
return config.isEnabled(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT) ?
200201
CoercionAction.AsNull : CoercionAction.Fail;
201-
}
202-
if ((inputShape == CoercionInputShape.Float)
203-
&& (targetType == LogicalType.Integer)) {
204-
// Default for setting in 2.x is true
205-
return config.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT) ?
206-
CoercionAction.TryConvert : CoercionAction.Fail;
202+
case Float:
203+
if (targetType == LogicalType.Integer) {
204+
// Default for setting in 2.x is true
205+
return config.isEnabled(DeserializationFeature.ACCEPT_FLOAT_AS_INT) ?
206+
CoercionAction.TryConvert : CoercionAction.Fail;
207+
}
208+
break;
209+
case Integer:
210+
if (targetType == LogicalType.Enum) {
211+
if (config.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
212+
return CoercionAction.Fail;
213+
}
214+
}
215+
break;
216+
default:
207217
}
208218

209219
// classic scalars are numbers, booleans; but date/time also considered

src/main/java/com/fasterxml/jackson/databind/deser/std/EnumDeserializer.java

+90-35
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
import com.fasterxml.jackson.databind.*;
1010
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
11+
import com.fasterxml.jackson.databind.cfg.CoercionAction;
12+
import com.fasterxml.jackson.databind.cfg.CoercionInputShape;
1113
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
1214
import com.fasterxml.jackson.databind.deser.ValueInstantiator;
1315
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
@@ -128,46 +130,38 @@ public LogicalType logicalType() {
128130
return LogicalType.Enum;
129131
}
130132

133+
@Override // since 2.12
134+
public Object getEmptyValue(DeserializationContext ctxt) throws JsonMappingException {
135+
return _enumDefaultValue;
136+
}
137+
131138
@Override
132139
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
133140
{
134-
String text;
135-
JsonToken curr = p.currentToken();
136-
137141
// Usually should just get string value:
138142
// 04-Sep-2020, tatu: for 2.11.3 / 2.12.0, removed "FIELD_NAME" as allowed;
139143
// did not work and gave odd error message.
140-
if (curr == JsonToken.VALUE_STRING) {
141-
text = p.getText();
144+
if (p.hasToken(JsonToken.VALUE_STRING)) {
145+
return _fromString(p, ctxt, p.getText());
146+
}
147+
142148
// But let's consider int acceptable as well (if within ordinal range)
143-
} else if (curr == JsonToken.VALUE_NUMBER_INT) {
144-
// ... unless told not to do that
145-
int index = p.getIntValue();
146-
if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
147-
return ctxt.handleWeirdNumberValue(_enumClass(), index,
148-
"not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
149-
);
150-
}
151-
if (index >= 0 && index < _enumsByIndex.length) {
152-
return _enumsByIndex[index];
153-
}
154-
if ((_enumDefaultValue != null)
155-
&& ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
156-
return _enumDefaultValue;
157-
}
158-
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
159-
return ctxt.handleWeirdNumberValue(_enumClass(), index,
160-
"index value outside legal index range [0..%s]",
161-
_enumsByIndex.length-1);
162-
}
163-
return null;
164-
} else if (curr == JsonToken.START_OBJECT) {
165-
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
166-
text = ctxt.extractScalarFromObject(p, this, _valueClass);
167-
} else {
168-
return _deserializeOther(p, ctxt);
149+
if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
150+
return _fromInteger(p, ctxt, p.getIntValue());
169151
}
170152

153+
// 29-Jun-2020, tatu: New! "Scalar from Object" (mostly for XML)
154+
if (p.isExpectedStartObjectToken()) {
155+
return _fromString(p, ctxt,
156+
ctxt.extractScalarFromObject(p, this, _valueClass));
157+
}
158+
return _deserializeOther(p, ctxt);
159+
}
160+
161+
protected Object _fromString(JsonParser p, DeserializationContext ctxt,
162+
String text)
163+
throws IOException
164+
{
171165
CompactStringObjectMap lookup = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
172166
? _getToStringLookup(ctxt) : _lookupByName;
173167
Object result = lookup.find(text);
@@ -180,20 +174,81 @@ public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOEx
180174
return result;
181175
}
182176

177+
protected Object _fromInteger(JsonParser p, DeserializationContext ctxt,
178+
int index)
179+
throws IOException
180+
{
181+
final CoercionAction act = ctxt.findCoercionAction(logicalType(), handledType(),
182+
CoercionInputShape.Integer);
183+
184+
// First, check legacy setting for slightly different message
185+
if (act == CoercionAction.Fail) {
186+
if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
187+
return ctxt.handleWeirdNumberValue(_enumClass(), index,
188+
"not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
189+
);
190+
}
191+
// otherwise this will force failure with new setting
192+
_checkCoercionFail(ctxt, act, handledType(), index,
193+
"Integer value ("+index+")");
194+
}
195+
switch (act) {
196+
case AsNull:
197+
return null;
198+
case AsEmpty:
199+
return getEmptyValue(ctxt);
200+
case TryConvert:
201+
default:
202+
}
203+
if (index >= 0 && index < _enumsByIndex.length) {
204+
return _enumsByIndex[index];
205+
}
206+
if ((_enumDefaultValue != null)
207+
&& ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
208+
return _enumDefaultValue;
209+
}
210+
if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
211+
return ctxt.handleWeirdNumberValue(_enumClass(), index,
212+
"index value outside legal index range [0..%s]",
213+
_enumsByIndex.length-1);
214+
}
215+
return null;
216+
}
217+
/*
218+
return _checkCoercionFail(ctxt, act, rawTargetType, value,
219+
"empty String (\"\")");
220+
*/
221+
183222
/*
184223
/**********************************************************
185224
/* Internal helper methods
186225
/**********************************************************
187226
*/
188227

189228
private final Object _deserializeAltString(JsonParser p, DeserializationContext ctxt,
190-
CompactStringObjectMap lookup, String name) throws IOException
229+
CompactStringObjectMap lookup, String nameOrig) throws IOException
191230
{
192-
name = name.trim();
193-
if (name.length() == 0) {
194-
if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
231+
String name = nameOrig.trim();
232+
if (name.length() == 0) { // empty or blank
233+
CoercionAction act;
234+
if (nameOrig.length() == 0) {
235+
act = _findCoercionFromEmptyString(ctxt);
236+
act = _checkCoercionFail(ctxt, act, handledType(), nameOrig,
237+
"empty String (\"\")");
238+
} else {
239+
act = _findCoercionFromBlankString(ctxt);
240+
act = _checkCoercionFail(ctxt, act, handledType(), nameOrig,
241+
"blank String (all whitespace)");
242+
}
243+
switch (act) {
244+
case AsEmpty:
245+
case TryConvert:
195246
return getEmptyValue(ctxt);
247+
case AsNull:
248+
default: // Fail already handled earlier
196249
}
250+
return null;
251+
// if (ctxt.isEnabled(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)) {
197252
} else {
198253
// [databind#1313]: Case insensitive enum deserialization
199254
if (Boolean.TRUE.equals(_caseInsensitive)) {

0 commit comments

Comments
 (0)