Skip to content

Commit 37b593e

Browse files
authored
Implement #1981: ContainerNode.remove(JsonPointer) (#5428)
1 parent 74a5aa0 commit 37b593e

File tree

5 files changed

+422
-2
lines changed

5 files changed

+422
-2
lines changed

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Versions: 3.x (for earlier see VERSION-2.x)
1010
#1196: Add opt-in error collection for deserialization
1111
(requested by @odrotbohm)
1212
(contributed by @sri-adarsh-kumar)
13+
#1980: Add method `remove(JsonPointer)` in `ContainerNode`
14+
(fix by @cowtowncoder, w/ Claude code)
1315
#5350: Add `DeserializationFeature.USE_NULL_FOR_MISSING_REFERENCE_VALUES` for
1416
selecting `null` vs "empty/absent" value when deserializing missing `Optional` value
1517
#5361: Fix Maven SBOM publishing (worked in 3.0.0-rc4 but not in rc5 or later)

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ protected JsonNode _at(JsonPointer ptr) {
6060
return get(ptr.getMatchingIndex());
6161
}
6262

63+
@Override // @since 3.1
64+
protected JsonNode _removeAt(JsonPointer ptr) {
65+
JsonNode n = remove(ptr.getMatchingIndex());
66+
return (n == null) ? missingNode() : n;
67+
}
68+
6369
@Override
6470
protected String _valueDesc() {
6571
return "[...(" + _children.size() + " elements)]";

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

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,86 @@ public final NumericNode numberNode(long v) {
201201
public T removeNulls() {
202202
return removeIf(JsonNode::isNull);
203203
}
204+
205+
/*
206+
/**********************************************************************
207+
/* JsonPointer-based removal (3.1)
208+
/**********************************************************************
209+
*/
210+
211+
/**
212+
* Method for removing the child node pointed to by given {@link JsonPointer},
213+
* if such a node exists.
214+
*<p>
215+
* For example, given JSON document:
216+
*<pre>
217+
* {
218+
* "a" : {
219+
* "b" : {
220+
* "c" : 13
221+
* }
222+
* }
223+
* }
224+
*</pre>
225+
* calling {@code remove(JsonPointer.compile("/a/b/c"))} would remove the
226+
* numeric value {@code 13}, resulting in:
227+
*<pre>
228+
* {
229+
* "a" : {
230+
* "b" : { }
231+
* }
232+
* }
233+
*</pre>
234+
*<p>
235+
* If the pointer points to a missing node, nothing is removed and {@code null}
236+
* is returned.
237+
*
238+
* @param ptr Pointer to the node to remove
239+
*
240+
* @return The removed node, if it existed; {@link MissingNode} if no node was found
241+
* at the specified path
242+
*
243+
* @since 3.1
244+
*/
245+
public JsonNode remove(JsonPointer ptr) {
246+
// Empty pointer would mean remove this node, but that doesn't make sense
247+
// as we can't remove ourselves from parent context
248+
if (ptr.matches()) {
249+
return missingNode();
250+
}
251+
252+
// Navigate to the parent of the target node
253+
ContainerNode<?> parent = this;
254+
JsonPointer currentPtr = ptr;
255+
256+
// Keep navigating until we're at the parent of the target
257+
while (true) {
258+
JsonPointer tail = currentPtr.tail();
259+
260+
// If tail is empty, we're at the parent - remove from here
261+
if (tail.matches()) {
262+
return parent._removeAt(currentPtr);
263+
}
264+
265+
// Otherwise, navigate one level deeper
266+
JsonNode next = parent._at(currentPtr);
267+
if (next instanceof ContainerNode<?> cn) {
268+
parent = cn;
269+
} else {
270+
return missingNode();
271+
}
272+
273+
currentPtr = tail;
274+
}
275+
}
276+
277+
/**
278+
* Internal helper method for removing a node at the current (single-segment)
279+
* pointer location.
280+
*
281+
* @param ptr Pointer with single segment to remove
282+
*
283+
* @return The removed node, if it existed; {@link #missingNode()} if not found
284+
*/
285+
protected abstract JsonNode _removeAt(JsonPointer ptr);
204286
}

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ protected JsonNode _at(JsonPointer ptr) {
5656
return get(ptr.getMatchingProperty());
5757
}
5858

59+
@Override // @since 3.1
60+
protected JsonNode _removeAt(JsonPointer ptr) {
61+
JsonNode n = remove(ptr.getMatchingProperty());
62+
return (n == null) ? missingNode() : n;
63+
}
64+
5965
@Override
6066
protected String _valueDesc() {
6167
return "{...(" + _children.size() + " properties}]";
@@ -447,7 +453,6 @@ public List<JsonNode> findParents(String propertyName, List<JsonNode> foundSoFar
447453
* Method that can be called to serialize this node and
448454
* all of its descendants using specified JSON generator.
449455
*/
450-
@SuppressWarnings("deprecation")
451456
@Override
452457
public void serialize(JsonGenerator g, SerializationContext ctxt)
453458
throws JacksonException
@@ -479,7 +484,6 @@ public void serialize(JsonGenerator g, SerializationContext ctxt)
479484
g.writeEndObject();
480485
}
481486

482-
@SuppressWarnings("deprecation")
483487
@Override
484488
public void serializeWithType(JsonGenerator g, SerializationContext ctxt,
485489
TypeSerializer typeSer)

0 commit comments

Comments
 (0)