Skip to content

Commit e797b22

Browse files
committed
Fix #2211 (last changes, still need to improve test coverage)
1 parent 6532c75 commit e797b22

File tree

4 files changed

+73
-22
lines changed

4 files changed

+73
-22
lines changed

release-notes/VERSION-2.x

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Project: jackson-databind
2121
#2196: Type safety for `readValue()` with `TypeReference`
2222
(suggested by nguyenfilip@github)
2323
#2204: Add `JsonNode.isEmpty()` as convenience alias
24+
#2211: Change of behavior (2.8 -> 2.9) with `ObjectMapper.readTree(input)` with no content
2425
#2217: Suboptimal memory allocation in `TextNode.getBinaryValue()`
2526
(reported by Christoph B)
2627
#2223: Add `missingNode()` method in `JsonNodeFactory`

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

+52-20
Original file line numberDiff line numberDiff line change
@@ -1166,14 +1166,14 @@ public JsonParser treeAsTokens(TreeNode n) {
11661166
@SuppressWarnings("unchecked")
11671167
@Override
11681168
public <T extends TreeNode> T readTree(JsonParser p) throws IOException {
1169-
return (T) _bindAsTree(p);
1169+
return (T) _bindAsTreeOrNull(p);
11701170
}
1171-
1171+
11721172
@Override
11731173
public void writeTree(JsonGenerator g, TreeNode rootNode) {
11741174
throw new UnsupportedOperationException();
11751175
}
1176-
1176+
11771177
/*
11781178
/**********************************************************
11791179
/* Deserialization methods; others similar to what ObjectMapper has
@@ -1669,9 +1669,7 @@ protected final JsonNode _bindAndCloseAsTree(JsonParser p0) throws IOException {
16691669

16701670
protected final JsonNode _bindAsTree(JsonParser p) throws IOException
16711671
{
1672-
// 27-Oct-2016, tatu: Need to inline `_initForReading()` due to
1673-
// special requirements by tree reading (no fail on eof)
1674-
1672+
// Need to inline `_initForReading()` due to tree reading handling end-of-input specially
16751673
_config.initialize(p);
16761674
if (_schema != null) {
16771675
p.setSchema(_schema);
@@ -1680,29 +1678,63 @@ protected final JsonNode _bindAsTree(JsonParser p) throws IOException
16801678
JsonToken t = p.getCurrentToken();
16811679
if (t == null) {
16821680
t = p.nextToken();
1683-
if (t == null) { // [databind#1406]: expose end-of-input as `null`
1684-
// [databind#2211]: return `MissingNode` (supercedes [databind#1406] which dictated
1685-
// returning `null`
1681+
if (t == null) {
16861682
return _config.getNodeFactory().missingNode();
16871683
}
16881684
}
1689-
DeserializationContext ctxt = createDeserializationContext(p);
1685+
final JsonNode resultNode;
16901686
if (t == JsonToken.VALUE_NULL) {
1691-
return _config.getNodeFactory().nullNode();
1692-
}
1693-
JsonDeserializer<Object> deser = _findTreeDeserializer(ctxt);
1694-
Object result;
1695-
if (_unwrapRoot) {
1696-
result = _unwrapAndDeserialize(p, ctxt, JSON_NODE_TYPE, deser);
1687+
resultNode = _config.getNodeFactory().nullNode();
16971688
} else {
1698-
result = deser.deserialize(p, ctxt);
1699-
if (_config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
1700-
_verifyNoTrailingTokens(p, ctxt, JSON_NODE_TYPE);
1689+
final DeserializationContext ctxt = createDeserializationContext(p);
1690+
final JsonDeserializer<Object> deser = _findTreeDeserializer(ctxt);
1691+
if (_unwrapRoot) {
1692+
resultNode = (JsonNode) _unwrapAndDeserialize(p, ctxt, JSON_NODE_TYPE, deser);
1693+
} else {
1694+
resultNode = (JsonNode) deser.deserialize(p, ctxt);
1695+
if (_config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
1696+
_verifyNoTrailingTokens(p, ctxt, JSON_NODE_TYPE);
1697+
}
17011698
}
17021699
}
1703-
return (JsonNode) result;
1700+
return resultNode;
17041701
}
17051702

1703+
/**
1704+
* Same as {@link #_bindAsTree} except end-of-input is reported by returning
1705+
* {@code null}, not "missing node"
1706+
*/
1707+
protected final JsonNode _bindAsTreeOrNull(JsonParser p) throws IOException
1708+
{
1709+
_config.initialize(p);
1710+
if (_schema != null) {
1711+
p.setSchema(_schema);
1712+
}
1713+
JsonToken t = p.getCurrentToken();
1714+
if (t == null) {
1715+
t = p.nextToken();
1716+
if (t == null) {
1717+
return null;
1718+
}
1719+
}
1720+
final JsonNode resultNode;
1721+
if (t == JsonToken.VALUE_NULL) {
1722+
resultNode = _config.getNodeFactory().nullNode();
1723+
} else {
1724+
final DeserializationContext ctxt = createDeserializationContext(p);
1725+
final JsonDeserializer<Object> deser = _findTreeDeserializer(ctxt);
1726+
if (_unwrapRoot) {
1727+
resultNode = (JsonNode) _unwrapAndDeserialize(p, ctxt, JSON_NODE_TYPE, deser);
1728+
} else {
1729+
resultNode = (JsonNode) deser.deserialize(p, ctxt);
1730+
if (_config.isEnabled(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)) {
1731+
_verifyNoTrailingTokens(p, ctxt, JSON_NODE_TYPE);
1732+
}
1733+
}
1734+
}
1735+
return resultNode;
1736+
}
1737+
17061738
/**
17071739
* @since 2.1
17081740
*/

src/test/java/com/fasterxml/jackson/databind/FullStreamReadTest.java

+19
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ public class FullStreamReadTest extends BaseMapTest
1717

1818
private final static String JSON_FAIL_ARRAY = JSON_OK_ARRAY + " [ ]";
1919

20+
private final static String JSON_OK_NULL = " null ";
21+
private final static String JSON_OK_NULL_WITH_COMMENT = " null /* stuff */ ";
22+
private final static String JSON_FAIL_NULL = JSON_OK_NULL + " false";
23+
2024
/*
2125
/**********************************************************
2226
/* Test methods, config
@@ -38,6 +42,16 @@ public void testMapperAcceptTrailing() throws Exception
3842
_verifyCollection(MAPPER.readValue(JSON_OK_ARRAY, List.class));
3943
_verifyCollection(MAPPER.readValue(JSON_OK_ARRAY_WITH_COMMENT, List.class));
4044
_verifyCollection(MAPPER.readValue(JSON_FAIL_ARRAY, List.class));
45+
46+
// ditto for getting `null` and some other token
47+
48+
assertTrue(MAPPER.readTree(JSON_OK_NULL).isNull());
49+
assertTrue(MAPPER.readTree(JSON_OK_NULL_WITH_COMMENT).isNull());
50+
assertTrue(MAPPER.readTree(JSON_FAIL_NULL).isNull());
51+
52+
assertNull(MAPPER.readValue(JSON_OK_NULL, Object.class));
53+
assertNull(MAPPER.readValue(JSON_OK_NULL_WITH_COMMENT, Object.class));
54+
assertNull(MAPPER.readValue(JSON_FAIL_NULL, Object.class));
4155
}
4256

4357
public void testMapperFailOnTrailing() throws Exception
@@ -92,6 +106,11 @@ public void testMapperFailOnTrailing() throws Exception
92106
.readValue(JSON_OK_ARRAY_WITH_COMMENT));
93107
}
94108

109+
public void testMapperFailOnTrailingWithNull() throws Exception
110+
{
111+
// !!! TODO
112+
}
113+
95114
public void testReaderAcceptTrailing() throws Exception
96115
{
97116
ObjectReader R = MAPPER.reader();

src/test/java/com/fasterxml/jackson/databind/node/EmptyContentAsTreeTest.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ public void testNullFromEOFWithParserAndMapper() throws Exception
5656
}
5757

5858
// [databind#1406]
59-
/*
6059
public void testNullFromEOFWithParserAndReader() throws Exception
6160
{
6261
try (JsonParser p = MAPPER.getFactory().createParser(EMPTY0)) {
@@ -89,7 +88,7 @@ public void testNullFromEOFWithParserAndReader() throws Exception
8988
_assertNullTree(MAPPER.reader().readTree(p));
9089
}
9190
}
92-
*/
91+
9392
// [databind#2211]: when passing content sources OTHER than `JsonParser`,
9493
// return "missing node" instead of alternate (return `null`, throw exception).
9594
public void testMissingNodeForEOFOtherMapper() throws Exception

0 commit comments

Comments
 (0)