Skip to content

Commit cd955e6

Browse files
committed
Merge branch '2.19'
2 parents 0eb46a6 + 364aab1 commit cd955e6

File tree

5 files changed

+92
-10
lines changed

5 files changed

+92
-10
lines changed

release-notes/VERSION-2.x

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ Project: jackson-databind
4242
#4773: `SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS` should not apply to Maps
4343
with uncomparable keys
4444
(requested by @nathanukey)
45+
#4801: Add `JsonNodeFeature.USE_BIG_DECIMAL_FOR_FLOATS` to allow overriding
46+
`DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS`
47+
(fix by Joo-Hyuk K)
4548
#4849 Not able to deserialize Enum with default typing after upgrading 2.15.4 -> 2.17.1
4649
(reported by Kornel Zemla)
4750
#4863: Add basic Stream support in `JsonNode`: `valueStream()`, `propertyStream()`,

src/main/java/tools/jackson/databind/cfg/JsonNodeFeature.java

+21-4
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ public enum JsonNodeFeature implements DatatypeFeature
3333
* order from the input document.
3434
*<p>
3535
* Default value: {@code false}
36-
*
37-
* @since 2.16
3836
*/
3937
WRITE_PROPERTIES_SORTED(false),
4038

@@ -72,10 +70,29 @@ public enum JsonNodeFeature implements DatatypeFeature
7270
* <p>
7371
* Default value is {@code false} for backwards-compatibility, but will most likely be changed to
7472
* {@code true} in 3.0.
73+
*/
74+
FAIL_ON_NAN_TO_BIG_DECIMAL_COERCION(false),
75+
76+
/**
77+
* Determines whether floating-point numbers should be deserialized into
78+
* {@link java.math.BigDecimal} when reading {@link tools.jackson.databind.JsonNode}s.
79+
* This feature provides more precise control over number deserialization for {@code JsonNode}
80+
* and takes precedence over {@link tools.jackson.databind.DeserializationFeature#USE_BIG_DECIMAL_FOR_FLOATS}
81+
* if explicitly set.
7582
*
76-
* @since 2.16
83+
* <p>
84+
* Behavior follows these rules:
85+
* <ul>
86+
* <li>If explicitly enabled, floating-point numbers will be read as {@link java.math.BigDecimal}.</li>
87+
* <li>If explicitly disabled, floating-point numbers will be read as {@link java.lang.Double}.</li>
88+
* <li>If left undefined (default), the behavior follows {@code DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS}.</li>
89+
* </ul>
90+
*
91+
* <p>
92+
* Default value is {@code false} but unless explicitly set, handling
93+
* depends on more general {@code DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS}).
7794
*/
78-
FAIL_ON_NAN_TO_BIG_DECIMAL_COERCION(false)
95+
USE_BIG_DECIMAL_FOR_FLOATS(false),
7996
;
8097

8198
private final static int FEATURE_INDEX = DatatypeFeatures.FEATURE_INDEX_JSON_NODE;

src/main/java/tools/jackson/databind/deser/jackson/BaseNodeDeserializer.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import tools.jackson.core.*;
77
import tools.jackson.databind.*;
8+
import tools.jackson.databind.cfg.DatatypeFeatures;
89
import tools.jackson.databind.cfg.JsonNodeFeature;
910
import tools.jackson.databind.deser.std.StdDeserializer;
1011
import tools.jackson.databind.jsontype.TypeDeserializer;
@@ -530,7 +531,7 @@ protected final JsonNode _fromFloat(JsonParser p, DeserializationContext ctxt,
530531
{
531532
// 13-Jan-2024, tatu: With 2.17 we have `JsonParser.getNumberTypeFP()` which
532533
// will return concrete FP type if format has one (JSON doesn't; most binary
533-
// formats do.
534+
// formats do).
534535
// But with `DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS` we have mechanism
535536
// that may override optimal physical representation. So heuristics to use are:
536537
//
@@ -548,7 +549,14 @@ protected final JsonNode _fromFloat(JsonParser p, DeserializationContext ctxt,
548549
}
549550
return nodeFactory.numberNode(nr);
550551
}
551-
if (ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)) {
552+
// [databind#4801] Add JsonNodeFeature.USE_BIG_DECIMAL_FOR_FLOATS
553+
DatatypeFeatures dtf = ctxt.getDatatypeFeatures();
554+
Boolean dtfState = dtf.getExplicitState(JsonNodeFeature.USE_BIG_DECIMAL_FOR_FLOATS);
555+
boolean useBigDecimal = (dtfState == null) // not explicitly set
556+
? ctxt.isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
557+
: dtfState.booleanValue();
558+
559+
if (useBigDecimal) {
552560
// [databind#4194] Add an option to fail coercing NaN to BigDecimal
553561
// Currently, Jackson 2.x allows such coercion, but Jackson 3.x will not
554562
if (p.isNaN()) {

src/main/java/tools/jackson/databind/introspect/PotentialCreator.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public class PotentialCreator
3535
* Parameter definitions if (and only if) this represents a
3636
* Property-based Creator.
3737
*/
38-
private List<BeanPropertyDefinition> propertyDefs;
38+
private List<BeanPropertyDefinition> _propertyDefs;
3939

4040
public PotentialCreator(AnnotatedWithParams cr,
4141
JsonCreator.Mode cm)
@@ -67,7 +67,7 @@ public PotentialCreator overrideMode(JsonCreator.Mode mode) {
6767

6868
@SuppressWarnings("unchecked")
6969
public void assignPropertyDefs(List<? extends BeanPropertyDefinition> propertyDefs) {
70-
this.propertyDefs = (List<BeanPropertyDefinition>) propertyDefs;
70+
_propertyDefs = (List<BeanPropertyDefinition>) propertyDefs;
7171
}
7272

7373
public PotentialCreator introspectParamNames(MapperConfig<?> config)
@@ -214,10 +214,10 @@ public String implicitNameSimple(int ix) {
214214
}
215215

216216
public BeanPropertyDefinition[] propertyDefs() {
217-
if (propertyDefs == null || propertyDefs.isEmpty()) {
217+
if (_propertyDefs == null || _propertyDefs.isEmpty()) {
218218
return new BeanPropertyDefinition[0];
219219
}
220-
return propertyDefs.toArray(new BeanPropertyDefinition[propertyDefs.size()]);
220+
return _propertyDefs.toArray(new BeanPropertyDefinition[_propertyDefs.size()]);
221221
}
222222

223223
/*

src/test/java/tools/jackson/databind/node/NodeFeaturesTest.java

+54
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package tools.jackson.databind.node;
22

3+
import java.math.BigDecimal;
4+
35
import org.junit.jupiter.api.Test;
46

57
import tools.jackson.databind.*;
@@ -156,4 +158,56 @@ public void testWriteSortedProperties() throws Exception
156158
/* Other features
157159
/**********************************************************************
158160
*/
161+
162+
// [databind#4801] USE_BIG_DECIMAL_FOR_FLOATS
163+
@Test
164+
public void testBigDecimalForJsonNodeFeature() throws Exception {
165+
final String JSON = "0.1234567890123456789012345678912345"; // Precision-sensitive
166+
167+
BigDecimal expectedBigDecimal = new BigDecimal("0.1234567890123456789012345678912345"); // Full precision
168+
BigDecimal expectedDoubleLossy = new BigDecimal("0.12345678901234568"); // Precision loss
169+
170+
ObjectMapper mapper;
171+
172+
// Case 1: Both enabled → Should use BigDecimal
173+
mapper = JsonMapper.builder()
174+
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
175+
.enable(JsonNodeFeature.USE_BIG_DECIMAL_FOR_FLOATS)
176+
.build();
177+
assertEquals(expectedBigDecimal, mapper.readTree(JSON).decimalValue());
178+
179+
// Case 2: Global enabled, JsonNodeFeature disabled → Should use Double (truncated decimal)
180+
mapper = JsonMapper.builder()
181+
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
182+
.disable(JsonNodeFeature.USE_BIG_DECIMAL_FOR_FLOATS)
183+
.build();
184+
assertEquals(expectedDoubleLossy, mapper.readTree(JSON).decimalValue());
185+
186+
// Case 3: Global enabled, JsonNodeFeature undefined → Should use BigDecimal (default to global)
187+
mapper = JsonMapper.builder()
188+
.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
189+
.build();
190+
assertEquals(expectedBigDecimal, mapper.readTree(JSON).decimalValue());
191+
192+
// Case 4: Global disabled, JsonNodeFeature enabled → Should use BigDecimal
193+
mapper = JsonMapper.builder()
194+
.disable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
195+
.enable(JsonNodeFeature.USE_BIG_DECIMAL_FOR_FLOATS)
196+
.build();
197+
assertEquals(expectedBigDecimal, mapper.readTree(JSON).decimalValue());
198+
199+
// Case 5: Both disabled → Should use Double (truncated decimal)
200+
mapper = JsonMapper.builder()
201+
.disable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
202+
.disable(JsonNodeFeature.USE_BIG_DECIMAL_FOR_FLOATS)
203+
.build();
204+
assertEquals(expectedDoubleLossy, mapper.readTree(JSON).decimalValue());
205+
206+
// Case 6: Global disabled, JsonNodeFeature undefined → Should use Double (default to global, truncated decimal)
207+
mapper = JsonMapper.builder()
208+
.disable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
209+
.build();
210+
assertEquals(expectedDoubleLossy, mapper.readTree(JSON).decimalValue());
211+
}
212+
159213
}

0 commit comments

Comments
 (0)