Skip to content

Commit 576c1b3

Browse files
committed
Fix #5034: re-write JsonNode.asBoolean(), .asString(), variants
1 parent 67311ef commit 576c1b3

File tree

13 files changed

+315
-128
lines changed

13 files changed

+315
-128
lines changed

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ Versions: 3.x (for earlier see VERSION-2.x)
111111
#5025: Add support for automatic detection of subtypes (like `@JsonSubTypes`)
112112
from Java 17 sealed types
113113
(contributed by Andy B
114+
#5034: Extend, improve set of `JsonNode.asXxx()` methods for non-number
115+
types (Boolean, String)
114116
- Remove `MappingJsonFactory`
115117
- Add context parameter for `TypeSerializer` contextualization (`forProperty()`)
116118
- Default for `JsonNodeFeature.STRIP_TRAILING_BIGDECIMAL_ZEROES` changed to `false` for 3.0

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

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -672,29 +672,31 @@ public String asText(String defaultValue) {
672672
public abstract Optional<Boolean> booleanValueOpt();
673673

674674
/**
675-
* Method that will try to convert value of this node to a Java <b>boolean</b>.
676-
* JSON booleans map naturally; integer numbers other than 0 map to true, and
677-
* 0 maps to false
675+
* Method that will try to convert value of this node to a Java {@code boolean}.
676+
* JSON Booleans map naturally; Integer numbers other than 0 map to true, and
677+
* 0 maps to false; {@code null} maps to false
678678
* and Strings 'true' and 'false' map to corresponding values.
679-
*<p>
680-
* If representation cannot be converted to a boolean value (including structured types
681-
* like Objects and Arrays),
682-
* default value of <b>false</b> will be returned; no exceptions are thrown.
679+
* Other values (including structured types like Objects and Arrays) will
680+
* result in a {@link JsonNodeException} being thrown.
681+
*
682+
* @return Boolean value this node represents, if coercible; exception otherwise
683+
*
684+
* @throws JsonNodeException if node cannot be coerced to a Java {@code boolean}
683685
*/
684686
public abstract boolean asBoolean();
685687

686688
/**
687-
* Method that will try to convert value of this node to a Java <b>boolean</b>.
688-
* JSON booleans map naturally; integer numbers other than 0 map to true, and
689-
* 0 maps to false
690-
* and Strings 'true' and 'false' map to corresponding values.
691-
*<p>
692-
* If representation cannot be converted to a boolean value (including structured types
693-
* like Objects and Arrays),
694-
* specified <b>defaultValue</b> will be returned; no exceptions are thrown.
689+
* Similar to {@link #asBoolean()}, but instead of throwing an exception for
690+
* non-coercible values, will return specified default value.
695691
*/
696692
public abstract boolean asBoolean(boolean defaultValue);
697693

694+
/**
695+
* Similar to {@link #asBoolean()}, but instead of throwing an exception for
696+
* non-coercible values, will return {@code Optional.empty()}.
697+
*/
698+
public abstract Optional<Boolean> asBooleanOpt();
699+
698700
// // Scalar access: Numbers, generic
699701

700702
/**

src/main/java/tools/jackson/databind/node/BaseJsonNode.java

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ public abstract class BaseJsonNode
3232
{
3333
private static final long serialVersionUID = 3L;
3434

35+
protected final static Optional<Boolean> OPT_FALSE = Optional.of(false);
36+
protected final static Optional<Boolean> OPT_TRUE = Optional.of(true);
37+
3538
// Simplest way is by using a helper
3639
Object writeReplace() {
3740
return NodeSerialization.from(this);
@@ -216,12 +219,29 @@ public Optional<Boolean> booleanValueOpt() {
216219

217220
@Override
218221
public boolean asBoolean() {
219-
return asBoolean(false);
222+
Boolean b = _asBoolean();
223+
if (b == null) {
224+
return _reportCoercionFail("asBoolean()", Boolean.TYPE, "value type not boolean");
225+
}
226+
return b;
220227
}
221228

222229
@Override
223230
public boolean asBoolean(boolean defaultValue) {
224-
return defaultValue;
231+
Boolean b = _asBoolean();
232+
if (b == null) {
233+
return defaultValue;
234+
}
235+
return b;
236+
}
237+
238+
@Override
239+
public Optional<Boolean> asBooleanOpt() {
240+
Boolean b = _asBoolean();
241+
if (b == null) {
242+
return Optional.empty();
243+
}
244+
return b.booleanValue() ? OPT_TRUE : OPT_FALSE;
225245
}
226246

227247
@Override
@@ -264,7 +284,8 @@ public final JsonNode findPath(String fieldName)
264284
}
265285

266286
// Also, force (re)definition
267-
@Override public abstract int hashCode();
287+
@Override
288+
public abstract int hashCode();
268289

269290
/*
270291
/**********************************************************************
@@ -417,6 +438,24 @@ protected ArrayNode _withArray(JsonPointer origPtr,
417438
return null;
418439
}
419440

441+
/*
442+
/**********************************************************************
443+
/* asXxx() helper methods for sub-classes to implement
444+
/**********************************************************************
445+
*/
446+
447+
/**
448+
* Method sub-classes should override if they can produce {@code boolean}
449+
* values via {@link #asBoolean()} -- if not, return {@code null} (in which
450+
* case appropriate error will be thrown or default value returned).
451+
*
452+
* @return Coerced value if possible; otherwise {@code null} to indicate this
453+
* node cannot be coerced.
454+
*/
455+
protected Boolean _asBoolean() {
456+
return null;
457+
}
458+
420459
/*
421460
/**********************************************************************
422461
/* JacksonSerializable

src/main/java/tools/jackson/databind/node/BigIntegerNode.java

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ public BigIntegerNode(BigInteger v) {
7575
/* Overridden JsonNode methods, scalar access
7676
/**********************************************************************
7777
*/
78+
79+
@Override
80+
protected Boolean _asBoolean() {
81+
return !BigInteger.ZERO.equals(_value);
82+
}
7883

7984
@Override
8085
public Number numberValue() {
@@ -185,22 +190,11 @@ public Optional<BigDecimal> decimalValueOpt() {
185190
return Optional.of(new BigDecimal(_value));
186191
}
187192

188-
/*
189-
/**********************************************************
190-
/* General type coercions
191-
/**********************************************************
192-
*/
193-
194193
@Override
195194
public String asString() {
196195
return _value.toString();
197196
}
198197

199-
@Override
200-
public boolean asBoolean(boolean defaultValue) {
201-
return !BigInteger.ZERO.equals(_value);
202-
}
203-
204198
/*
205199
/**********************************************************
206200
/* Other overrides

src/main/java/tools/jackson/databind/node/BooleanNode.java

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ public class BooleanNode
1919

2020
public final static BooleanNode TRUE = new BooleanNode(true);
2121
public final static BooleanNode FALSE = new BooleanNode(false);
22-
23-
private final static Optional<Boolean> OPT_FALSE = Optional.of(false);
24-
private final static Optional<Boolean> OPT_TRUE = Optional.of(true);
2522

2623
private final boolean _value;
2724

@@ -72,31 +69,43 @@ protected String _valueDesc() {
7269
/**********************************************************************
7370
*/
7471

72+
// // // Override "asBoolean()" methods as minor optimization
73+
7574
@Override
76-
public boolean booleanValue() {
75+
public final boolean asBoolean() {
7776
return _value;
7877
}
7978

8079
@Override
81-
public boolean booleanValue(boolean defaultValue) {
80+
public final boolean asBoolean(boolean defaultValue) {
8281
return _value;
8382
}
8483

8584
@Override
86-
public Optional<Boolean> booleanValueOpt() {
85+
public Optional<Boolean> asBooleanOpt() {
8786
return _value ? OPT_TRUE : OPT_FALSE;
8887
}
88+
89+
@Override
90+
protected Boolean _asBoolean() {
91+
return _value;
92+
}
8993

9094
@Override
91-
public boolean asBoolean() {
95+
public boolean booleanValue() {
9296
return _value;
9397
}
9498

9599
@Override
96-
public boolean asBoolean(boolean defaultValue) {
100+
public boolean booleanValue(boolean defaultValue) {
97101
return _value;
98102
}
99103

104+
@Override
105+
public Optional<Boolean> booleanValueOpt() {
106+
return _value ? OPT_TRUE : OPT_FALSE;
107+
}
108+
100109
@Override
101110
public int asInt(int defaultValue) {
102111
return _value ? 1 : 0;

src/main/java/tools/jackson/databind/node/IntNode.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ public static IntNode valueOf(int i) {
7979
/**********************************************************************
8080
*/
8181

82+
@Override
83+
protected Boolean _asBoolean() {
84+
return (_value != 0);
85+
}
86+
8287
@Override
8388
public Number numberValue() {
8489
return Integer.valueOf(_value);
@@ -145,11 +150,6 @@ public String asString() {
145150
return String.valueOf(_value);
146151
}
147152

148-
@Override
149-
public boolean asBoolean(boolean defaultValue) {
150-
return _value != 0;
151-
}
152-
153153
/*
154154
/**********************************************************************
155155
/* Overridden JsonNode methods, other
@@ -163,6 +163,12 @@ public final void serialize(JsonGenerator g, SerializationContext provider)
163163
g.writeNumber(_value);
164164
}
165165

166+
/*
167+
/**********************************************************************
168+
/* Overridden standard methods
169+
/**********************************************************************
170+
*/
171+
166172
@Override
167173
public boolean equals(Object o)
168174
{

src/main/java/tools/jackson/databind/node/LongNode.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ public boolean canConvertToInt() {
6161
/* Overridden JsonNode methods, scalar access
6262
/**********************************************************************
6363
*/
64-
64+
65+
@Override
66+
protected Boolean _asBoolean() {
67+
return (_value != 0L);
68+
}
69+
6570
@Override
6671
public Number numberValue() {
6772
return Long.valueOf(_value);
@@ -135,18 +140,19 @@ public String asString() {
135140
return String.valueOf(_value);
136141
}
137142

138-
@Override
139-
public boolean asBoolean(boolean defaultValue) {
140-
return _value != 0;
141-
}
143+
/*
144+
/**********************************************************************
145+
/* Overridden methods, other
146+
/**********************************************************************
147+
*/
142148

143149
@Override
144150
public final void serialize(JsonGenerator jg, SerializationContext provider)
145151
throws JacksonException
146152
{
147153
jg.writeNumber(_value);
148154
}
149-
155+
150156
@Override
151157
public boolean equals(Object o)
152158
{

src/main/java/tools/jackson/databind/node/NullNode.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,27 @@ public JsonNodeType getNodeType() {
3636
@Override
3737
public NullNode deepCopy() { return this; }
3838

39+
/*
40+
/**********************************************************************
41+
/* Overridden JsonNode methods, scalar access
42+
/**********************************************************************
43+
*/
44+
3945
@Override
40-
protected String _valueDesc() {
41-
return "<null>";
46+
protected Boolean _asBoolean() {
47+
return Boolean.FALSE;
4248
}
4349

4450
@Override
4551
public String asString(String defaultValue) { return defaultValue; }
4652

4753
@Override public String asString() { return "null"; }
4854

55+
@Override
56+
protected String _valueDesc() {
57+
return "<null>";
58+
}
59+
4960
@SuppressWarnings("unchecked")
5061
@Override
5162
public JsonNode requireNonNull() {

0 commit comments

Comments
 (0)