Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ Project: jackson-databind
custom `NullValueProvider`
#3535: Replace `JsonNode.with()` with `JsonNode.withObject()`
#3559: Support `null`-valued `Map` fields with "any setter"
#3568: Change `JsonNode.with(String)` and `withArray(String)` to consider
argument as `JsonPointer` if valid expression

2.13.4 (not yet released)

Expand Down
90 changes: 69 additions & 21 deletions src/main/java/com/fasterxml/jackson/databind/JsonNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -1119,22 +1119,33 @@ public final List<JsonNode> findParents(String fieldName)
*/

/**
* Method that can be called on Object nodes, to access a property
* that has Object value; or if no such property exists, to create,
* add and return such Object node.
* If the node method is called on is not Object node,
* or if property exists and has value that is not Object node,
* {@link UnsupportedOperationException} is thrown
* Short-cut equivalent to:
*<pre>
* withObject(JsonPointer.compile(expr);
*</pre>
* see {@link #withObject(JsonPointer)} for full explanation.
*
* @param propertyName Name of property for the {@link ObjectNode}
* @param expr {@link JsonPointer} expression to use
*
* @return {@link ObjectNode} found or created
*
* @since 2.14
*/
public ObjectNode withObject(String propertyName) {
throw new UnsupportedOperationException("`JsonNode` not of type `ObjectNode` (but "
+getClass().getName()+"), cannot call `withObject()` on it");
public final ObjectNode withObject(String expr) {
return withObject(JsonPointer.compile(expr));
}

/**
* Short-cut equivalent to:
*<pre>
* withObject(JsonPointer.compile(expr), overwriteMode, preferIndex);
*</pre>
*
* @since 2.14
*/
public final ObjectNode withObject(String expr,
OverwriteMode overwriteMode, boolean preferIndex) {
return withObject(JsonPointer.compile(expr), overwriteMode, preferIndex);
}

/**
Expand Down Expand Up @@ -1231,31 +1242,68 @@ public ObjectNode withObject(JsonPointer ptr,
}

/**
* Method that works in one of possible ways, depending on whether
* {@code exprOrProperty} is a valid {@link JsonPointer} expression or
* not (valid expression is either empty String {@code ""} or starts
* with leading slash {@code /} character).
* If it is, works as a short-cut to:
*<pre>
* withObject(JsonPointer.compile(exprOrProperty));
*</pre>
* If it is NOT a valid {@link JsonPointer} expression, value is taken
* as a literal Object property name and traversed like a single-segment
* {@link JsonPointer}.
*<p>
* NOTE: before Jackson 2.14 behavior was always that of non-expression usage;
* that is, {@code exprOrProperty} was always considered as a simple property name.
*
* @deprecated Since 2.14 use {@code withObject(String)} instead
*/
@SuppressWarnings("unchecked")
@Deprecated // since 2.14
public final <T extends JsonNode> T with(String propertyName) {
return (T) withObject(propertyName);
public <T extends JsonNode> T with(String exprOrProperty) {
throw new UnsupportedOperationException("`JsonNode` not of type `ObjectNode` (but "
+getClass().getName()+"), cannot call `with(String)` on it");
}

/**
* Method that can be called on {@link ObjectNode} nodes, to access a property
* that has <code>Array</code> value; or if no such property exists, to create,
* add and return such Array node.
* If the node method is called on is not Object node,
* or if property exists and has value that is not Array node,
* {@link UnsupportedOperationException} is thrown
* Method that works in one of possible ways, depending on whether
* {@code exprOrProperty} is a valid {@link JsonPointer} expression or
* not (valid expression is either empty String {@code ""} or starts
* with leading slash {@code /} character).
* If it is, works as a short-cut to:
*<pre>
* withObject(JsonPointer.compile(exprOrProperty));
*</pre>
* If it is NOT a valid {@link JsonPointer} expression, value is taken
* as a literal Object property name and traversed like a single-segment
* {@link JsonPointer}.
*<p>
* NOTE: before Jackson 2.14 behavior was always that of non-expression usage;
* that is, {@code exprOrProperty} was always considered as a simple property name.
*
* @param propertyName Name of property for the {@link ArrayNode}
* @param exprOrProperty Either {@link JsonPointer} expression for full access (if valid
* pointer expression), or the name of property for the {@link ArrayNode}.
*
* @return {@link ArrayNode} found or created
*/
public <T extends JsonNode> T withArray(String propertyName) {
public <T extends JsonNode> T withArray(String exprOrProperty) {
throw new UnsupportedOperationException("`JsonNode` not of type `ObjectNode` (but `"
+getClass().getName()+")`, cannot call `withArray()` on it");
}

/**
* Short-cut equivalent to:
*<pre>
* withArray(JsonPointer.compile(expr), overwriteMode, preferIndex);
*</pre>
*
* @since 2.14
*/
public ArrayNode withArray(String expr,
OverwriteMode overwriteMode, boolean preferIndex) {
return withArray(JsonPointer.compile(expr), overwriteMode, preferIndex);
}

/**
* Same as {@link #withArray(JsonPointer, OverwriteMode, boolean)} but
* with defaults of {@code OvewriteMode#NULLS} (overwrite mode)
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/com/fasterxml/jackson/databind/node/ArrayNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ public ArrayNode deepCopy()
/**********************************************************
*/

@SuppressWarnings("unchecked")
@Deprecated
@Override
public ObjectNode with(String exprOrProperty) {
JsonPointer ptr = _jsonPointerIfValid(exprOrProperty);
if (ptr != null) {
return withObject(ptr);
}
return super.with(exprOrProperty); // to give failure
}

@SuppressWarnings("unchecked")
@Override
public ArrayNode withArray(String exprOrProperty)
{
JsonPointer ptr = _jsonPointerIfValid(exprOrProperty);
if (ptr != null) {
return withArray(ptr);
}
return super.withArray(exprOrProperty); // to give failure
}

@Override
protected ObjectNode _withObject(JsonPointer origPtr,
JsonPointer currentPtr,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,4 +260,13 @@ protected <T> T _reportWrongNodeType(String msgTemplate, Object...args) {
protected <T> T _reportWrongNodeOperation(String msgTemplate, Object...args) {
throw new UnsupportedOperationException(String.format(msgTemplate, args));
}

// @since 2.14
protected JsonPointer _jsonPointerIfValid(String exprOrProperty) {
if (exprOrProperty.isEmpty() || exprOrProperty.charAt(0) == '/') {
return JsonPointer.compile(exprOrProperty);
}
return null;
}

}
78 changes: 44 additions & 34 deletions src/main/java/com/fasterxml/jackson/databind/node/ObjectNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,50 @@ public ObjectNode deepCopy()
/**********************************************************
*/

@SuppressWarnings("unchecked")
@Deprecated
@Override
public ObjectNode with(String exprOrProperty) {
JsonPointer ptr = _jsonPointerIfValid(exprOrProperty);
if (ptr != null) {
return withObject(ptr);
}
JsonNode n = _children.get(exprOrProperty);
if (n != null) {
if (n instanceof ObjectNode) {
return (ObjectNode) n;
}
throw new UnsupportedOperationException("Property '" + exprOrProperty
+ "' has value that is not of type `ObjectNode` (but `" + n
.getClass().getName() + "`)");
}
ObjectNode result = objectNode();
_children.put(exprOrProperty, result);
return result;
}

@SuppressWarnings("unchecked")
@Override
public ArrayNode withArray(String exprOrProperty)
{
JsonPointer ptr = _jsonPointerIfValid(exprOrProperty);
if (ptr != null) {
return withArray(ptr);
}
JsonNode n = _children.get(exprOrProperty);
if (n != null) {
if (n instanceof ArrayNode) {
return (ArrayNode) n;
}
throw new UnsupportedOperationException("Property '" + exprOrProperty
+ "' has value that is not of type `ArrayNode` (but `" + n
.getClass().getName() + "`)");
}
ArrayNode result = arrayNode();
_children.put(exprOrProperty, result);
return result;
}

@Override
protected ObjectNode _withObject(JsonPointer origPtr,
JsonPointer currentPtr,
Expand Down Expand Up @@ -239,40 +283,6 @@ public Iterator<Map.Entry<String, JsonNode>> fields() {
return _children.entrySet().iterator();
}

@Override
public ObjectNode withObject(String propertyName) {
JsonNode n = _children.get(propertyName);
if (n != null) {
if (n instanceof ObjectNode) {
return (ObjectNode) n;
}
throw new UnsupportedOperationException("Property '" + propertyName
+ "' has value that is not of type `ObjectNode` (but `" + n
.getClass().getName() + "`)");
}
ObjectNode result = objectNode();
_children.put(propertyName, result);
return result;
}

@SuppressWarnings("unchecked")
@Override
public ArrayNode withArray(String propertyName)
{
JsonNode n = _children.get(propertyName);
if (n != null) {
if (n instanceof ArrayNode) {
return (ArrayNode) n;
}
throw new UnsupportedOperationException("Property '" + propertyName
+ "' has value that is not of type `ArrayNode` (but `" + n
.getClass().getName() + "`)");
}
ArrayNode result = arrayNode();
_children.put(propertyName, result);
return result;
}

@Override
public boolean equals(Comparator<JsonNode> comparator, JsonNode o)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.databind.*;

public class JsonPointerWithNodeTest
public class JsonPointerAtNodeTest
extends BaseMapTest
{
private final ObjectMapper MAPPER = newJsonMapper();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public void testObjectNodeSerialization() throws Exception
root.put("answer", 42);
ArrayNode arr = root.withArray("matrix");
arr.add(1).add(12345678901L).add(true).add("...");
ObjectNode misc = root.withObject("misc");
ObjectNode misc = root.withObject("/misc");
misc.put("value", 0.25);

testNodeRoundtrip(root);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public void testValidWith() throws Exception
{
ObjectNode root = MAPPER.createObjectNode();
assertEquals("{}", MAPPER.writeValueAsString(root));
JsonNode child = root.withObject("prop");
JsonNode child = root.withObject("/prop");
assertTrue(child instanceof ObjectNode);
assertEquals("{\"prop\":{}}", MAPPER.writeValueAsString(root));
}
Expand All @@ -321,7 +321,7 @@ public void testInvalidWith() throws Exception
{
JsonNode root = MAPPER.createArrayNode();
try { // should not work for non-ObjectNode nodes:
root.withObject("prop");
root.with("prop");
fail("Expected exception");
} catch (UnsupportedOperationException e) {
verifyException(e, "not of type `ObjectNode`");
Expand All @@ -330,7 +330,7 @@ public void testInvalidWith() throws Exception
ObjectNode root2 = MAPPER.createObjectNode();
root2.put("prop", 13);
try { // should not work for non-ObjectNode nodes:
root2.withObject("prop");
root2.with("prop");
fail("Expected exception");
} catch (UnsupportedOperationException e) {
verifyException(e, "has value that is not");
Expand Down
Loading