Skip to content

Commit 0bd8779

Browse files
committed
Partial implementation of #1980
1 parent 34723d2 commit 0bd8779

File tree

5 files changed

+141
-3
lines changed

5 files changed

+141
-3
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,7 @@ public final List<JsonNode> findParents(String fieldName)
10871087
*/
10881088
public <T extends JsonNode> T withObject(String propertyName) {
10891089
throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but "
1090-
+getClass().getName()+"), cannot call with() on it");
1090+
+getClass().getName()+"), cannot call withObject() on it");
10911091
}
10921092

10931093
/**

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

+34
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,40 @@ public ArrayNode deepCopy()
6868
return ret;
6969
}
7070

71+
@SuppressWarnings("unchecked")
72+
@Override
73+
protected <T extends JsonNode> T _withObjectCreatePath(JsonPointer origPtr,
74+
JsonPointer currentPtr)
75+
{
76+
// With Arrays, bit different; the first entry needs to be index
77+
if (!currentPtr.mayMatchElement()) {
78+
return _reportWrongNodeType(
79+
"`JsonPointer` path \"%s\" must have index for `ArrayNode`; instead has property \"%s\"",
80+
origPtr.toString(),
81+
getClass().getName(),
82+
currentPtr.getMatchingProperty());
83+
}
84+
85+
// And we know there's no node at given index
86+
ObjectNode currentNode = this.objectNode();
87+
// One complication: may need to insert nulls
88+
final int ix = currentPtr.getMatchingIndex();
89+
while (ix >= size()) {
90+
add(nullNode());
91+
}
92+
set(ix, currentNode);
93+
94+
currentPtr = currentPtr.tail();
95+
96+
// Otherwise loop same as with ObjectNode
97+
while (!currentPtr.matches()) {
98+
// Should we try to build Arrays? For now, nope.
99+
currentNode = currentNode.putObject(currentPtr.getMatchingProperty());
100+
currentPtr = currentPtr.tail();
101+
}
102+
return (T) currentNode;
103+
}
104+
71105
/*
72106
/**********************************************************
73107
/* Overrides for JsonSerializable.Base

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

+32-2
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,39 @@ public <T extends JsonNode> T withObject(JsonPointer ptr) {
119119
return _withObject(ptr, ptr);
120120
}
121121

122+
@SuppressWarnings("unchecked")
122123
protected <T extends JsonNode> T _withObject(JsonPointer origPtr,
123-
JsonPointer currentPTr) {
124-
return null;
124+
JsonPointer currentPtr)
125+
{
126+
if (currentPtr.matches()) {
127+
if (this.isObject()) {
128+
return (T) this;
129+
}
130+
return _reportWrongNodeType(
131+
"`JsonNode` matching `JsonPointer` \"%s\" must be `ObjectNode`, not `%s`",
132+
origPtr.toString(),
133+
getClass().getName());
134+
}
135+
JsonNode n = _at(currentPtr);
136+
if ((n != null) && (n instanceof BaseJsonNode)) {
137+
return ((BaseJsonNode) n)._withObject(origPtr, currentPtr.tail());
138+
}
139+
return _withObjectCreatePath(origPtr, currentPtr);
140+
}
141+
142+
/**
143+
* Helper method for constructing specified path under this node, if possible;
144+
* or throwing an exception if not. If construction successful, needs to return
145+
* the innermost {@code ObjectNode} constructed.
146+
*/
147+
protected <T extends JsonNode> T _withObjectCreatePath(JsonPointer origPtr,
148+
JsonPointer currentPtr)
149+
{
150+
// Cannot traverse non-container nodes:
151+
return _reportWrongNodeType(
152+
"`JsonPointer` path \"%s\" cannot traverse non-container node of type `%s`",
153+
origPtr.toString(),
154+
getClass().getName());
125155
}
126156

127157
/*

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

+14
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@ public ObjectNode deepCopy()
6060
return ret;
6161
}
6262

63+
@SuppressWarnings("unchecked")
64+
@Override
65+
protected <T extends JsonNode> T _withObjectCreatePath(JsonPointer origPtr,
66+
JsonPointer currentPtr)
67+
{
68+
ObjectNode currentNode = this;
69+
while (!currentPtr.matches()) {
70+
// Should we try to build Arrays? For now, nope.
71+
currentNode = currentNode.putObject(currentPtr.getMatchingProperty());
72+
currentPtr = currentPtr.tail();
73+
}
74+
return (T) currentNode;
75+
}
76+
6377
/*
6478
/**********************************************************
6579
/* Overrides for JsonSerializable.Base
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.fasterxml.jackson.databind.node;
2+
3+
import com.fasterxml.jackson.core.JsonPointer;
4+
import com.fasterxml.jackson.databind.*;
5+
6+
// for [databuind#1980] implementation
7+
public class WithPathTest extends BaseMapTest
8+
{
9+
/*
10+
/**********************************************************************
11+
/* Test methods
12+
/**********************************************************************
13+
*/
14+
15+
private final ObjectMapper MAPPER = sharedMapper();
16+
17+
public void testValidWithObjectTrivial() throws Exception
18+
{
19+
ObjectNode root = MAPPER.createObjectNode();
20+
ObjectNode match = root.withObject(JsonPointer.empty());
21+
assertSame(root, match);
22+
}
23+
24+
public void testValidWithObjectSimple() throws Exception
25+
{
26+
ObjectNode root = MAPPER.createObjectNode();
27+
ObjectNode match = root.withObject(JsonPointer.compile("/a/b"));
28+
assertTrue(match.isObject());
29+
match.put("value", 42);
30+
31+
assertEquals(a2q("{'a':{'b':{'value':42}}}"),
32+
root.toString());
33+
34+
// and with that
35+
ObjectNode match2 = root.withObject(JsonPointer.compile("/a/b"));
36+
assertSame(match, match2);
37+
match.put("value2", true);
38+
39+
assertEquals(a2q("{'a':{'b':{'value':42,'value2':true}}}"),
40+
root.toString());
41+
}
42+
43+
public void testValidWithObjectWithArray() throws Exception
44+
{
45+
ObjectNode root = MAPPER.createObjectNode();
46+
root.putArray("arr");
47+
ObjectNode match = root.withObject(JsonPointer.compile("/arr/2"));
48+
assertTrue(match.isObject());
49+
match.put("value", 42);
50+
assertEquals(a2q("{'arr':[null,null,{'value':42}]}"),
51+
root.toString());
52+
53+
// But also verify we can match
54+
ObjectNode match2 = root.withObject(JsonPointer.compile("/arr/2"));
55+
assertSame(match, match2);
56+
match.put("value2", true);
57+
assertEquals(a2q("{'arr':[null,null,{'value':42,'value2':true}]}"),
58+
root.toString());
59+
}
60+
}

0 commit comments

Comments
 (0)