Skip to content

Commit 90424c8

Browse files
committed
Fix #2049 and #2525
1 parent eafd480 commit 90424c8

File tree

7 files changed

+77
-121
lines changed

7 files changed

+77
-121
lines changed

release-notes/CREDITS-2.x

+4
Original file line numberDiff line numberDiff line change
@@ -1006,3 +1006,7 @@ Ville Koskela (vjkoskela@github)
10061006
Fitz (Joongsoo.Park) (joongsoo@github)
10071007
* Contributed #2511: Add `SerializationFeature.WRITE_SELF_REFERENCES_AS_NULL`
10081008
(2.11.0)
1009+
1010+
Antonio Petrelli (apetrelli@github)
1011+
* Reported #2049: TreeTraversingParser and UTF8StreamJsonParser create contexts differently
1012+
(2.11.0)

release-notes/VERSION-2.x

+3
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ Project: jackson-databind
66

77
2.11.0 (not yet released)
88

9+
#2049: TreeTraversingParser and UTF8StreamJsonParser create contexts differently
10+
(reported by Antonio P)
911
#2487: BeanDeserializerBuilder Protected Factory Method for Extension
1012
(contributed by Ville K)
1113
#2503: Support `@JsonSerialize(keyUsing)` and `@JsonDeserialize(keyUsing)` on Key class
1214
#2511: Add `SerializationFeature.WRITE_SELF_REFERENCES_AS_NULL`
1315
(contributed by Joongsoo P)
1416
#2522: `DeserializationContext.handleMissingInstantiator()` throws `MismatchedInputException`
1517
for non-static inner classes
18+
#2525: Incorrect `JsonStreamContext` for `TokenBuffer` and `TreeTraversingParser`
1619
- Add `SerializerProvider.findContentValueSerializer()` methods
1720

1821
2.10.1 (not yet released)

src/main/java/com/fasterxml/jackson/databind/node/NodeCursor.java

+34-49
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,10 @@ public void setCurrentValue(java.lang.Object v) {
7575
*/
7676

7777
public abstract JsonToken nextToken();
78-
public abstract JsonToken nextValue();
79-
public abstract JsonToken endToken();
80-
8178
public abstract JsonNode currentNode();
82-
public abstract boolean currentHasChildren();
79+
80+
public abstract NodeCursor startObject();
81+
public abstract NodeCursor startArray();
8382

8483
/**
8584
* Method called to create a new context for iterating all
@@ -104,9 +103,8 @@ public final NodeCursor iterateChildren() {
104103
*/
105104

106105
/**
107-
* Context matching root-level value nodes (i.e. anything other
108-
* than JSON Object and Array).
109-
* Note that context is NOT created for leaf values.
106+
* Context for all root-level value nodes (including Arrays and Objects):
107+
* only context for scalar values.
110108
*/
111109
protected final static class RootCursor
112110
extends NodeCursor
@@ -128,32 +126,35 @@ public void overrideCurrentName(String name) {
128126
@Override
129127
public JsonToken nextToken() {
130128
if (!_done) {
129+
++_index;
131130
_done = true;
132131
return _node.asToken();
133132
}
134133
_node = null;
135134
return null;
136135
}
137-
138-
@Override
139-
public JsonToken nextValue() { return nextToken(); }
136+
140137
@Override
141-
public JsonToken endToken() { return null; }
138+
public JsonNode currentNode() {
139+
// May look weird, but is necessary so as not to expose current node
140+
// before it has been traversed
141+
return _done ? _node : null;
142+
}
143+
142144
@Override
143-
public JsonNode currentNode() { return _node; }
145+
public NodeCursor startArray() { return new ArrayCursor(_node, this); }
146+
144147
@Override
145-
public boolean currentHasChildren() { return false; }
148+
public NodeCursor startObject() { return new ObjectCursor(_node, this); }
146149
}
147150

148-
/**
149-
* Cursor used for traversing non-empty JSON Array nodes
150-
*/
151+
// Cursor used for traversing JSON Array nodes
151152
protected final static class ArrayCursor
152153
extends NodeCursor
153154
{
154155
protected Iterator<JsonNode> _contents;
155156

156-
protected JsonNode _currentNode;
157+
protected JsonNode _currentElement;
157158

158159
public ArrayCursor(JsonNode n, NodeCursor p) {
159160
super(JsonStreamContext.TYPE_ARRAY, p);
@@ -164,30 +165,25 @@ public ArrayCursor(JsonNode n, NodeCursor p) {
164165
public JsonToken nextToken()
165166
{
166167
if (!_contents.hasNext()) {
167-
_currentNode = null;
168-
return null;
168+
_currentElement = null;
169+
return JsonToken.END_ARRAY;
169170
}
170-
_currentNode = _contents.next();
171-
return _currentNode.asToken();
171+
++_index;
172+
_currentElement = _contents.next();
173+
return _currentElement.asToken();
172174
}
173175

174176
@Override
175-
public JsonToken nextValue() { return nextToken(); }
176-
@Override
177-
public JsonToken endToken() { return JsonToken.END_ARRAY; }
177+
public JsonNode currentNode() { return _currentElement; }
178178

179179
@Override
180-
public JsonNode currentNode() { return _currentNode; }
180+
public NodeCursor startArray() { return new ArrayCursor(_currentElement, this); }
181+
181182
@Override
182-
public boolean currentHasChildren() {
183-
// note: ONLY to be called for container nodes
184-
return ((ContainerNode<?>) currentNode()).size() > 0;
185-
}
183+
public NodeCursor startObject() { return new ObjectCursor(_currentElement, this); }
186184
}
187185

188-
/**
189-
* Cursor used for traversing non-empty JSON Object nodes
190-
*/
186+
// Cursor used for traversing JSON Object nodes
191187
protected final static class ObjectCursor
192188
extends NodeCursor
193189
{
@@ -211,8 +207,9 @@ public JsonToken nextToken()
211207
if (!_contents.hasNext()) {
212208
_currentName = null;
213209
_current = null;
214-
return null;
210+
return JsonToken.END_OBJECT;
215211
}
212+
++_index;
216213
_needEntry = false;
217214
_current = _contents.next();
218215
_currentName = (_current == null) ? null : _current.getKey();
@@ -223,26 +220,14 @@ public JsonToken nextToken()
223220
}
224221

225222
@Override
226-
public JsonToken nextValue()
227-
{
228-
JsonToken t = nextToken();
229-
if (t == JsonToken.FIELD_NAME) {
230-
t = nextToken();
231-
}
232-
return t;
223+
public JsonNode currentNode() {
224+
return (_current == null) ? null : _current.getValue();
233225
}
234226

235227
@Override
236-
public JsonToken endToken() { return JsonToken.END_OBJECT; }
228+
public NodeCursor startArray() { return new ArrayCursor(currentNode(), this); }
237229

238230
@Override
239-
public JsonNode currentNode() {
240-
return (_current == null) ? null : _current.getValue();
241-
}
242-
@Override
243-
public boolean currentHasChildren() {
244-
// note: ONLY to be called for container nodes
245-
return ((ContainerNode<?>) currentNode()).size() > 0;
246-
}
231+
public NodeCursor startObject() { return new ObjectCursor(currentNode(), this); }
247232
}
248233
}

src/main/java/com/fasterxml/jackson/databind/node/TreeTraversingParser.java

+33-69
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import com.fasterxml.jackson.core.*;
99
import com.fasterxml.jackson.core.base.ParserMinimalBase;
10-
1110
import com.fasterxml.jackson.databind.JsonNode;
1211

1312
/**
@@ -37,18 +36,6 @@ public class TreeTraversingParser extends ParserMinimalBase
3736
/**********************************************************
3837
*/
3938

40-
/**
41-
* Sometimes parser needs to buffer a single look-ahead token; if so,
42-
* it'll be stored here. This is currently used for handling
43-
*/
44-
protected JsonToken _nextToken;
45-
46-
/**
47-
* Flag needed to handle recursion into contents of child
48-
* Array/Object nodes.
49-
*/
50-
protected boolean _startContainer;
51-
5239
/**
5340
* Flag that indicates whether parser is closed or not. Gets
5441
* set when parser is either closed by explicit call
@@ -68,15 +55,7 @@ public TreeTraversingParser(JsonNode n, ObjectCodec codec)
6855
{
6956
super(0);
7057
_objectCodec = codec;
71-
if (n.isArray()) {
72-
_nextToken = JsonToken.START_ARRAY;
73-
_nodeCursor = new NodeCursor.ArrayCursor(n, null);
74-
} else if (n.isObject()) {
75-
_nextToken = JsonToken.START_OBJECT;
76-
_nodeCursor = new NodeCursor.ObjectCursor(n, null);
77-
} else { // value node
78-
_nodeCursor = new NodeCursor.RootCursor(n, null);
79-
}
58+
_nodeCursor = new NodeCursor.RootCursor(n, null);
8059
}
8160

8261
@Override
@@ -119,57 +98,37 @@ public void close() throws IOException
11998
@Override
12099
public JsonToken nextToken() throws IOException, JsonParseException
121100
{
122-
if (_nextToken != null) {
123-
_currToken = _nextToken;
124-
_nextToken = null;
125-
return _currToken;
126-
}
127-
// are we to descend to a container child?
128-
if (_startContainer) {
129-
_startContainer = false;
130-
// minor optimization: empty containers can be skipped
131-
if (!_nodeCursor.currentHasChildren()) {
132-
_currToken = (_currToken == JsonToken.START_OBJECT) ?
133-
JsonToken.END_OBJECT : JsonToken.END_ARRAY;
134-
return _currToken;
135-
}
136-
_nodeCursor = _nodeCursor.iterateChildren();
137-
_currToken = _nodeCursor.nextToken();
138-
if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
139-
_startContainer = true;
140-
}
141-
return _currToken;
142-
}
143-
// No more content?
144-
if (_nodeCursor == null) {
101+
_currToken = _nodeCursor.nextToken();
102+
if (_currToken == null) {
145103
_closed = true; // if not already set
146104
return null;
147105
}
148-
// Otherwise, next entry from current cursor
149-
_currToken = _nodeCursor.nextToken();
150-
if (_currToken != null) {
151-
if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
152-
_startContainer = true;
153-
}
154-
return _currToken;
106+
switch (_currToken) {
107+
case START_OBJECT:
108+
_nodeCursor = _nodeCursor.startObject();
109+
break;
110+
case START_ARRAY:
111+
_nodeCursor = _nodeCursor.startArray();
112+
break;
113+
case END_OBJECT:
114+
case END_ARRAY:
115+
_nodeCursor = _nodeCursor.getParent();
116+
default:
155117
}
156-
// null means no more children; need to return end marker
157-
_currToken = _nodeCursor.endToken();
158-
_nodeCursor = _nodeCursor.getParent();
159118
return _currToken;
160119
}
161-
120+
162121
// default works well here:
163-
//public JsonToken nextValue() throws IOException, JsonParseException
122+
//public JsonToken nextValue() throws IOException
164123

165124
@Override
166-
public JsonParser skipChildren() throws IOException, JsonParseException
125+
public JsonParser skipChildren() throws IOException
167126
{
168127
if (_currToken == JsonToken.START_OBJECT) {
169-
_startContainer = false;
128+
_nodeCursor = _nodeCursor.getParent();
170129
_currToken = JsonToken.END_OBJECT;
171130
} else if (_currToken == JsonToken.START_ARRAY) {
172-
_startContainer = false;
131+
_nodeCursor = _nodeCursor.getParent();
173132
_currToken = JsonToken.END_ARRAY;
174133
}
175134
return this;
@@ -186,19 +145,24 @@ public boolean isClosed() {
186145
/**********************************************************
187146
*/
188147

189-
@Override
190-
public String getCurrentName() {
191-
return (_nodeCursor == null) ? null : _nodeCursor.getCurrentName();
148+
@Override public String getCurrentName() {
149+
NodeCursor crsr = _nodeCursor;
150+
if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
151+
crsr = crsr.getParent();
152+
}
153+
return (crsr == null) ? null : crsr.getCurrentName();
192154
}
193155

194-
@Override
195-
public void overrideCurrentName(String name)
196-
{
197-
if (_nodeCursor != null) {
198-
_nodeCursor.overrideCurrentName(name);
156+
@Override public void overrideCurrentName(String name) {
157+
NodeCursor crsr = _nodeCursor;
158+
if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
159+
crsr = crsr.getParent();
160+
}
161+
if (crsr != null) {
162+
crsr.overrideCurrentName(name);
199163
}
200164
}
201-
165+
202166
@Override
203167
public JsonStreamContext getParsingContext() {
204168
return _nodeCursor;

src/test/java/com/fasterxml/jackson/failing/ParsingContext2525Test.java src/test/java/com/fasterxml/jackson/databind/misc/ParsingContext2525Test.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.fasterxml.jackson.failing;
1+
package com.fasterxml.jackson.databind.misc;
22

33
import java.io.IOException;
44

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ public void testParser() throws Exception
260260
p.setCodec(null);
261261
assertNull(p.getCodec());
262262
assertNotNull(p.getParsingContext());
263-
// assertTrue(p.getParsingContext().inRoot());
263+
assertTrue(p.getParsingContext().inRoot());
264264
assertNotNull(p.getTokenLocation());
265265
assertNotNull(p.getCurrentLocation());
266266
assertNull(p.getEmbeddedObject());

src/test/java/com/fasterxml/jackson/failing/NodeContext2049Test.java src/test/java/com/fasterxml/jackson/databind/node/NodeContext2049Test.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.fasterxml.jackson.failing;
1+
package com.fasterxml.jackson.databind.node;
22

33
import java.io.IOException;
44
import java.util.ArrayList;

0 commit comments

Comments
 (0)