Skip to content

Commit 7f1a3db

Browse files
authored
Add withObject(JsonPointer)/withArray(JsonPointer) methods in JsonNode (#3554)
1 parent 75c5cb8 commit 7f1a3db

File tree

11 files changed

+625
-117
lines changed

11 files changed

+625
-117
lines changed

release-notes/VERSION-2.x

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Project: jackson-databind
66

77
2.14.0 (not yet released)
88

9+
#1980: Add method(s) in `JsonNode` that works like combination of `at()`
10+
and `with()`: `withObject(...)` and `withArray(...)`
911
#2541: Cannot merge polymorphic objects
1012
(reported by Matthew A)
1113
(fix contributed by James W)

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

+170-21
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.util.*;
77

88
import com.fasterxml.jackson.core.*;
9+
import com.fasterxml.jackson.databind.node.ArrayNode;
910
import com.fasterxml.jackson.databind.node.JsonNodeType;
1011
import com.fasterxml.jackson.databind.node.MissingNode;
1112
import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -1124,10 +1125,16 @@ public final List<JsonNode> findParents(String fieldName)
11241125
* If the node method is called on is not Object node,
11251126
* or if property exists and has value that is not Object node,
11261127
* {@link UnsupportedOperationException} is thrown
1128+
*
1129+
* @param propertyName Name of property for the {@link ObjectNode}
1130+
*
1131+
* @return {@link ObjectNode} found or created
1132+
*
1133+
* @since 2.14
11271134
*/
1128-
public <T extends JsonNode> T withObject(String propertyName) {
1129-
throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but "
1130-
+getClass().getName()+"), cannot call withObject() on it");
1135+
public ObjectNode withObject(String propertyName) {
1136+
throw new UnsupportedOperationException("`JsonNode` not of type `ObjectNode` (but "
1137+
+getClass().getName()+"), cannot call `withObject()` on it");
11311138
}
11321139

11331140
/**
@@ -1137,10 +1144,10 @@ public <T extends JsonNode> T withObject(String propertyName) {
11371144
* consider {@link JsonPointer} segments index if at all possible
11381145
* and only secondarily as property name
11391146
*
1140-
* @param ptr Pointer that indicates path to use for Object value to return
1147+
* @param ptr {@link JsonPointer} that indicates path to use for Object value to return
11411148
* (potentially creating as necessary)
11421149
*
1143-
* @return ObjectNode found or created
1150+
* @return {@link ObjectNode} found or created
11441151
*
11451152
* @since 2.14
11461153
*/
@@ -1149,47 +1156,189 @@ public final ObjectNode withObject(JsonPointer ptr) {
11491156
}
11501157

11511158
/**
1152-
* Method that can be called on Object nodes, to access a Object-valued
1159+
* Method that can be called on Object or Array nodes, to access a Object-valued
11531160
* node pointed to by given {@link JsonPointer}, if such a node exists:
1154-
* if not, an attempt is made to create it.
1155-
* If the node method is called on is not Object node,
1156-
* or if property exists and has value that is not Object node,
1157-
* {@link UnsupportedOperationException} is thrown
1161+
* or if not, an attempt is made to create one and return it.
1162+
* For example, on document
1163+
*<pre>
1164+
* { "a" : {
1165+
* "b" : {
1166+
* "c" : 13
1167+
* }
1168+
* }
1169+
* }
1170+
*</pre>
1171+
* calling method with {@link JsonPointer} of {@code /a/b} would return
1172+
* {@link ObjectNode}
1173+
*<pre>
1174+
* { "c" : 13 }
1175+
*</pre>
1176+
*<p>
1177+
* In cases where path leads to "missing" nodes, a path is created.
1178+
* So, for example, on above document, and
1179+
* {@link JsonPointer} of {@code /a/x} an empty {@link ObjectNode} would
1180+
* be returned and the document would look like:
1181+
*<pre>
1182+
* { "a" : {
1183+
* "b" : {
1184+
* "c" : 13
1185+
* },
1186+
* "x" : { }
1187+
* }
1188+
* }
1189+
*</pre>
1190+
* Finally, if the path is incompatible with the document -- there is an existing
1191+
* {@code JsonNode} through which expression cannot go -- a replacement is
1192+
* attempted if (and only if) conversion is allowed as per {@code overwriteMode}
1193+
* passed in. For example, with above document and expression of {@code /a/b/c},
1194+
* conversion is allowed if passing {@code OverwriteMode.SCALARS} or
1195+
* {@code OvewriteMode.ALL}, and resulting document would look like:
1196+
*<pre>
1197+
* { "a" : {
1198+
* "b" : {
1199+
* "c" : { }
1200+
* },
1201+
* "x" : { }
1202+
* }
1203+
* }
1204+
*</pre>
1205+
* but if different modes ({@code NONE} or {@code NULLS}) is passed, an exception
1206+
* is thrown instead.
11581207
*
1159-
* @param ptr Pointer that indicates path to use for Object value to return
1160-
* (potentially creating as necessary)
1161-
* @param overwriteMode Defines w
1208+
* @param ptr Pointer that indicates path to use for {@link ObjectNode} value to return
1209+
* (potentially creating one as necessary)
1210+
* @param overwriteMode Defines which node types may be converted in case of
1211+
* incompatible {@code JsonPointer} expression: if conversion not allowed,
1212+
* {@link UnsupportedOperationException} is thrown.
1213+
* @param preferIndex When creating a path (for empty or replacement), and path
1214+
* contains segment that may be an array index (simple integer number like
1215+
* {@code 3}), whether to construct an {@link ArrayNode} ({@code true}) or
1216+
* {@link ObjectNode} ({@code false}). In latter case matching property with
1217+
* quoted number (like {@code "3"}) is used within Object.
1218+
*
1219+
* @return {@link ObjectNode} found or created
11621220
*
1163-
* @return ObjectNode found or created
1221+
* @throws UnsupportedOperationException if a conversion would be needed for given
1222+
* {@code JsonPointer}, document, but was not allowed for the type encountered
11641223
*
11651224
* @since 2.14
11661225
*/
11671226
public ObjectNode withObject(JsonPointer ptr,
11681227
OverwriteMode overwriteMode, boolean preferIndex) {
11691228
// To avoid abstract method, base implementation just fails
1170-
throw new UnsupportedOperationException("`withObject(JsonPointer)` not implemented by "
1171-
+getClass().getName());
1229+
throw new UnsupportedOperationException("`withObject(JsonPointer)` not implemented by `"
1230+
+getClass().getName()+"`");
11721231
}
11731232

11741233
/**
1175-
* @deprecated Since 2.14 use {@code withObject} instead
1234+
* @deprecated Since 2.14 use {@code withObject(String)} instead
11761235
*/
1236+
@SuppressWarnings("unchecked")
11771237
@Deprecated // since 2.14
11781238
public final <T extends JsonNode> T with(String propertyName) {
1179-
return withObject(propertyName);
1239+
return (T) withObject(propertyName);
11801240
}
11811241

11821242
/**
1183-
* Method that can be called on Object nodes, to access a property
1243+
* Method that can be called on {@link ObjectNode} nodes, to access a property
11841244
* that has <code>Array</code> value; or if no such property exists, to create,
11851245
* add and return such Array node.
11861246
* If the node method is called on is not Object node,
11871247
* or if property exists and has value that is not Array node,
11881248
* {@link UnsupportedOperationException} is thrown
1249+
*
1250+
* @param propertyName Name of property for the {@link ArrayNode}
1251+
*
1252+
* @return {@link ArrayNode} found or created
11891253
*/
11901254
public <T extends JsonNode> T withArray(String propertyName) {
1191-
throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but "
1192-
+getClass().getName()+"), cannot call withArray() on it");
1255+
throw new UnsupportedOperationException("`JsonNode` not of type `ObjectNode` (but `"
1256+
+getClass().getName()+")`, cannot call `withArray()` on it");
1257+
}
1258+
1259+
/**
1260+
* Same as {@link #withArray(JsonPointer, OverwriteMode, boolean)} but
1261+
* with defaults of {@code OvewriteMode#NULLS} (overwrite mode)
1262+
* and {@code true} for {@code preferIndex}.
1263+
*
1264+
* @param ptr Pointer that indicates path to use for {@link ArrayNode} to return
1265+
* (potentially creating as necessary)
1266+
*
1267+
* @return {@link ArrayNode} found or created
1268+
*
1269+
* @since 2.14
1270+
*/
1271+
public final ArrayNode withArray(JsonPointer ptr) {
1272+
return withArray(ptr, OverwriteMode.NULLS, true);
1273+
}
1274+
1275+
/**
1276+
* Method that can be called on Object or Array nodes, to access a Array-valued
1277+
* node pointed to by given {@link JsonPointer}, if such a node exists:
1278+
* or if not, an attempt is made to create one and return it.
1279+
* For example, on document
1280+
*<pre>
1281+
* { "a" : {
1282+
* "b" : [ 1, 2 ]
1283+
* }
1284+
* }
1285+
*</pre>
1286+
* calling method with {@link JsonPointer} of {@code /a/b} would return
1287+
* {@code Array}
1288+
*<pre>
1289+
* [ 1, 2 ]
1290+
*</pre>
1291+
*<p>
1292+
* In cases where path leads to "missing" nodes, a path is created.
1293+
* So, for example, on above document, and
1294+
* {@link JsonPointer} of {@code /a/x} an empty {@code ArrayNode} would
1295+
* be returned and the document would look like:
1296+
*<pre>
1297+
* { "a" : {
1298+
* "b" : [ 1, 2 ],
1299+
* "x" : [ ]
1300+
* }
1301+
* }
1302+
*</pre>
1303+
* Finally, if the path is incompatible with the document -- there is an existing
1304+
* {@code JsonNode} through which expression cannot go -- a replacement is
1305+
* attempted if (and only if) conversion is allowed as per {@code overwriteMode}
1306+
* passed in. For example, with above document and expression of {@code /a/b/0},
1307+
* conversion is allowed if passing {@code OverwriteMode.SCALARS} or
1308+
* {@code OvewriteMode.ALL}, and resulting document would look like:
1309+
*<pre>
1310+
* { "a" : {
1311+
* "b" : [ [ ], 2 ],
1312+
* "x" : [ ]
1313+
* }
1314+
* }
1315+
*</pre>
1316+
* but if different modes ({@code NONE} or {@code NULLS}) is passed, an exception
1317+
* is thrown instead.
1318+
*
1319+
* @param ptr Pointer that indicates path to use for {@link ArrayNode} value to return
1320+
* (potentially creating it as necessary)
1321+
* @param overwriteMode Defines which node types may be converted in case of
1322+
* incompatible {@code JsonPointer} expression: if conversion not allowed,
1323+
* an exception is thrown.
1324+
* @param preferIndex When creating a path (for empty or replacement), and path
1325+
* contains segment that may be an array index (simple integer number like
1326+
* {@code 3}), whether to construct an {@link ArrayNode} ({@code true}) or
1327+
* {@link ObjectNode} ({@code false}). In latter case matching property with
1328+
* quoted number (like {@code "3"}) is used within Object.
1329+
*
1330+
* @return {@link ArrayNode} found or created
1331+
*
1332+
* @throws UnsupportedOperationException if a conversion would be needed for given
1333+
* {@code JsonPointer}, document, but was not allowed for the type encountered
1334+
*
1335+
* @since 2.14
1336+
*/
1337+
public ArrayNode withArray(JsonPointer ptr,
1338+
OverwriteMode overwriteMode, boolean preferIndex) {
1339+
// To avoid abstract method, base implementation just fails
1340+
throw new UnsupportedOperationException("`withArray(JsonPointer)` not implemented by "
1341+
+getClass().getName());
11931342
}
11941343

11951344
/*

0 commit comments

Comments
 (0)