This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new d8e501e620 Add MarshalledNode tree façade and RFC 6901 JSON-Pointer; 
remove public ObjectRest.
d8e501e620 is described below

commit d8e501e62015de9276970f11a6c19fb5ce5bec94
Author: James Bognar <[email protected]>
AuthorDate: Tue Jun 16 10:29:17 2026 -0400

    Add MarshalledNode tree façade and RFC 6901 JSON-Pointer; remove public 
ObjectRest.
    
    - Add MarshalledNode typed tree façade and JsonPointer (RFC 6901) 
addressing over MarshalledMap/MarshalledList.
    - Relocate the ObjectRest traversal engine to internal PathTraversal; 
rename ObjectRestException -> PathTraversalException.
    - Remove public ObjectRest and ResponseContent.asObjectRest(); migrate 
Traversable, Swagger/OpenApi findRef, and SwaggerVar/OpenApiVar to 
PathTraversal.
    
    Co-authored-by: Cursor <[email protected]>
---
 .../org/apache/juneau/bean/openapi3/OpenApi.java   |   2 +-
 .../org/apache/juneau/bean/swagger/Swagger.java    |   2 +-
 .../juneau/marshall/collections/JsonPointer.java   | 284 +++++++++++++++
 .../marshall/collections/MarshalledList.java       |  53 ++-
 .../juneau/marshall/collections/MarshalledMap.java |  53 ++-
 .../marshall/collections/MarshalledNode.java       | 391 +++++++++++++++++++++
 .../juneau/marshall/collections/package-info.java  |   2 +-
 .../{ObjectRest.java => PathTraversal.java}        | 165 +++------
 ...tException.java => PathTraversalException.java} |  12 +-
 .../test/java/org/apache/juneau/JsonMap_Test.java  |   7 +-
 .../marshall/collections/JsonPointer_Test.java     | 227 ++++++++++++
 .../marshall/collections/MarshalledNode_Test.java  | 346 ++++++++++++++++++
 ...bjectRest_Test.java => PathTraversal_Test.java} | 224 +++++-------
 .../apache/juneau/BasicRuntimeException_Test.java  |   4 +-
 .../classic/RestClient_Response_Body_Test.java     |   9 +-
 .../rest/client/classic/ResponseContent.java       |  41 +--
 .../juneau/rest/client/classic/RestClient.java     |   3 -
 .../juneau/rest/server/converter/Traversable.java  |   7 +-
 .../apache/juneau/rest/server/vars/OpenApiVar.java |   2 +-
 .../apache/juneau/rest/server/vars/SwaggerVar.java |   2 +-
 20 files changed, 1495 insertions(+), 341 deletions(-)

diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/OpenApi.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/OpenApi.java
index b5040fd62b..a1bfec4ef7 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/OpenApi.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/OpenApi.java
@@ -278,7 +278,7 @@ public class OpenApi extends OpenApiElement {
                if (! ref.startsWith("#/"))
                        throw rex("Unsupported reference:  ''{0}''", ref);
                try {
-                       return new ObjectRest(this).get(ref.substring(1), c);
+                       return new PathTraversal(this).get(ref.substring(1), c);
                } catch (Exception e) {
                        throw bex(e, c, "Reference ''{0}'' could not be 
converted to type ''{1}''.", ref, cn(c));
                }
diff --git 
a/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/Swagger.java
 
b/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/Swagger.java
index 1939d39dbe..55a38997fb 100644
--- 
a/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/Swagger.java
+++ 
b/juneau-bean/juneau-bean-swagger-v2/src/main/java/org/apache/juneau/bean/swagger/Swagger.java
@@ -514,7 +514,7 @@ public class Swagger extends SwaggerElement {
                if (! ref.startsWith("#/"))
                        throw rex("Unsupported reference:  ''{0}''", ref);
                try {
-                       return new ObjectRest(this).get(ref.substring(1), c);
+                       return new PathTraversal(this).get(ref.substring(1), c);
                } catch (Exception e) {
                        throw bex(e, c, "Reference ''{0}'' could not be 
converted to type ''{1}''.", ref, cn(c));
                }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/JsonPointer.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/JsonPointer.java
new file mode 100644
index 0000000000..4d6a895b7a
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/JsonPointer.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.marshall.collections;
+
+import static org.apache.juneau.commons.utils.ThrowableUtils.*;
+
+import java.util.*;
+
+/**
+ * An <a class="doclink" 
href="https://datatracker.ietf.org/doc/html/rfc6901";>RFC 6901</a> JSON Pointer 
that addresses a
+ * single value within a {@link Map}/{@link List} tree (such as the {@link 
MarshalledMap}/{@link MarshalledList} family).
+ *
+ * <p>
+ * A JSON Pointer is a string of zero or more <c>/</c>-prefixed reference 
tokens. The empty string <js>""</js> references
+ * the whole document (the root). A non-empty pointer must begin with 
<c>/</c>; each token is the text between <c>/</c>
+ * separators with <c>~1</c> decoded to <c>/</c> and <c>~0</c> decoded to 
<c>~</c>. For arrays a token is either a
+ * base-10 non-negative integer index or the single character <c>-</c> (which 
references the nonexistent element after
+ * the last element, used to append on writes); for objects the decoded token 
is the member name.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ *     JsonMap <jv>doc</jv> = JsonMap.<jsm>of</jsm>(<js>"foo"</js>, 
JsonList.<jsm>of</jsm>(<js>"bar"</js>, <js>"baz"</js>));
+ *
+ *     <jc>// Read.</jc>
+ *     Object <jv>v</jv> = 
JsonPointer.<jsm>of</jsm>(<js>"/foo/0"</js>).eval(<jv>doc</jv>);  <jc>// 
"bar"</jc>
+ *
+ *     <jc>// Auto-vivifying write (creates intermediate containers).</jc>
+ *     JsonPointer.<jsm>of</jsm>(<js>"/foo/-"</js>).set(<jv>doc</jv>, 
<js>"qux"</js>);     <jc>// appends "qux"</jc>
+ * </p>
+ *
+ * <h5 class='section'>Notes:</h5><ul>
+ *     <li class='warn'>This class is not thread safe.
+ *     <li>Read-miss (missing key, out-of-range index, or type mismatch) 
resolves to <jk>null</jk> via {@link #eval(Object)}.
+ * </ul>
+ *
+ * <p>
+ * <b>Beta — API subject to change:</b> This type is part of the 
next-generation typed tree model layered over the
+ * {@code Marshalled*} collections and may change incompatibly in a future 
release.
+ */
+public class JsonPointer {
+
+       /** Sentinel distinguishing an absent value from a present 
<jk>null</jk> value during traversal. */
+       private static final Object MISSING = new Object();
+
+       /**
+        * Parses and validates the specified RFC 6901 JSON Pointer string.
+        *
+        * @param pointer The pointer string. The empty string references the 
whole document; a non-empty pointer must begin with <c>/</c>.
+        * @return A new {@link JsonPointer}, never <jk>null</jk>.
+        * @throws IllegalArgumentException If the pointer is <jk>null</jk> or 
malformed (non-empty and not beginning with <c>/</c>).
+        */
+       public static JsonPointer of(String pointer) {
+               return new JsonPointer(parse(pointer));
+       }
+
+       /**
+        * Encodes a raw object member name into an RFC 6901 reference token 
(escapes <c>~</c> to <c>~0</c> and <c>/</c> to <c>~1</c>).
+        *
+        * @param rawMemberName The raw member name. Must not be <jk>null</jk>.
+        * @return The encoded reference token.
+        */
+       public static String encodeToken(String rawMemberName) {
+               return rawMemberName.replace("~", "~0").replace("/", "~1");
+       }
+
+       /**
+        * Decodes an RFC 6901 reference token into a raw object member name 
(unescapes <c>~1</c> to <c>/</c> then <c>~0</c> to <c>~</c>).
+        *
+        * @param token The reference token. Must not be <jk>null</jk>.
+        * @return The decoded member name.
+        */
+       public static String decodeToken(String token) {
+               return token.replace("~1", "/").replace("~0", "~");
+       }
+
+       private static List<String> parse(String pointer) {
+               if (pointer == null)
+                       throw illegalArg("JSON Pointer cannot be null.");
+               if (pointer.isEmpty())
+                       return List.of();
+               if (pointer.charAt(0) != '/')
+                       throw illegalArg("Invalid JSON Pointer ''{0}'': a 
non-empty pointer must begin with ''/''.", pointer);
+               var parts = pointer.split("/", -1);
+               var tokens = new ArrayList<String>(parts.length - 1);
+               for (var i = 1; i < parts.length; i++)
+                       tokens.add(decodeToken(parts[i]));
+               return tokens;
+       }
+
+       private final List<String> tokens;
+
+       private JsonPointer(List<String> tokens) {
+               this.tokens = tokens;
+       }
+
+       /**
+        * Reads the value addressed by this pointer from the specified root.
+        *
+        * <p>
+        * Walks the tokens from <c>root</c> (which may be a {@link Map}/{@link 
List} such as the {@link MarshalledMap}/
+        * {@link MarshalledList} family). The empty pointer returns 
<c>root</c>.
+        *
+        * @param root The root document. Can be <jk>null</jk>.
+        * @return The addressed value, or <jk>null</jk> on any missing key, 
out-of-range index, or type mismatch.
+        */
+       public Object eval(Object root) {
+               var o = evalOrMissing(root);
+               return o == MISSING ? null : o;
+       }
+
+       /**
+        * Writes the specified value at the location addressed by this 
pointer, auto-vivifying missing intermediate containers.
+        *
+        * <p>
+        * Intermediate containers are created based on the next token: a 
numeric or <c>-</c> token implies a {@link JsonList},
+        * otherwise a {@link JsonMap}. The <c>-</c> token on a list appends. 
The empty pointer cannot mutate <c>root</c> in
+        * place for the whole-document case, so it simply returns <c>value</c> 
as the new root.
+        *
+        * @param root The root document. For a non-empty pointer, must be a 
mutable {@link Map}/{@link List} tree.
+        * @param value The value to set.
+        * @return The (possibly newly-created) effective root.
+        */
+       public Object set(Object root, Object value) {
+               if (tokens.isEmpty())
+                       return value;
+               var parent = navigateToParent(root);
+               rawSet(parent, tokens.get(tokens.size() - 1), value);
+               return root;
+       }
+
+       /**
+        * Removes the member or element addressed by this pointer.
+        *
+        * @param root The root document.
+        * @return The removed value, or <jk>null</jk> if the addressed 
member/element was absent (or the pointer is the root pointer).
+        */
+       public Object remove(Object root) {
+               if (tokens.isEmpty())
+                       return null;
+               var parent = root;
+               for (var i = 0; i < tokens.size() - 1; i++) {
+                       parent = rawGet(parent, tokens.get(i));
+                       if (parent == MISSING)
+                               return null;
+               }
+               return rawRemove(parent, tokens.get(tokens.size() - 1));
+       }
+
+       @Override /* Overridden from Object */
+       public String toString() {
+               if (tokens.isEmpty())
+                       return "";
+               var sb = new StringBuilder();
+               for (var t : tokens)
+                       sb.append('/').append(encodeToken(t));
+               return sb.toString();
+       }
+
+       /**
+        * Returns <jk>true</jk> if this is the root pointer (the empty pointer 
addressing the whole document).
+        */
+       boolean isRoot() {
+               return tokens.isEmpty();
+       }
+
+       /**
+        * Same as {@link #eval(Object)} but returns the {@link #MISSING} 
sentinel (rather than <jk>null</jk>) for an absent
+        * value, so callers can distinguish a present <jk>null</jk> value from 
a read-miss.
+        */
+       Object evalOrMissing(Object root) {
+               var current = root;
+               for (var token : tokens) {
+                       current = rawGet(current, token);
+                       if (current == MISSING)
+                               return MISSING;
+               }
+               return current;
+       }
+
+       /** Returns <jk>true</jk> if the specified value is the absent-value 
sentinel returned by {@link #evalOrMissing(Object)}. */
+       static boolean isMissing(Object o) {
+               return o == MISSING;
+       }
+
+       private Object navigateToParent(Object root) {
+               var current = root;
+               for (var i = 0; i < tokens.size() - 1; i++) {
+                       var token = tokens.get(i);
+                       var child = rawGet(current, token);
+                       if (! (child instanceof Map || child instanceof List)) {
+                               child = isIndexToken(tokens.get(i + 1)) ? new 
JsonList() : new JsonMap();
+                               rawSet(current, token, child);
+                       }
+                       current = child;
+               }
+               return current;
+       }
+
+       private static Object rawGet(Object container, String token) {
+               if (container instanceof Map<?,?> container2)
+                       return container2.containsKey(token) ? 
container2.get(token) : MISSING;
+               if (container instanceof List<?> container2) {
+                       var idx = toArrayIndex(token);
+                       return (idx >= 0 && idx < container2.size()) ? 
container2.get(idx) : MISSING;
+               }
+               return MISSING;
+       }
+
+       @SuppressWarnings({
+               "unchecked" // Object-node maps accept String keys / Object 
values; array-node lists accept Object elements.
+       })
+       private static void rawSet(Object container, String token, Object 
value) {
+               if (container instanceof Map) {
+                       ((Map<String,Object>)container).put(token, value);
+                       return;
+               }
+               if (container instanceof List) {
+                       var list = (List<Object>)container;
+                       if ("-".equals(token)) {
+                               list.add(value);
+                               return;
+                       }
+                       var idx = toArrayIndex(token);
+                       if (idx < 0 || idx > list.size())
+                               throw illegalArg("Invalid JSON Pointer array 
index ''{0}'' for a list of size {1}.", token, list.size());
+                       if (idx == list.size())
+                               list.add(value);
+                       else
+                               list.set(idx, value);
+                       return;
+               }
+               throw illegalArg("Cannot set value at token ''{0}'' on a 
non-container value.", token);
+       }
+
+       private static Object rawRemove(Object container, String token) {
+               if (container instanceof Map<?,?> container2)
+                       return container2.containsKey(token) ? 
container2.remove(token) : null;
+               if (container instanceof List<?> container2) {
+                       var idx = toArrayIndex(token);
+                       return (idx >= 0 && idx < container2.size()) ? 
container2.remove(idx) : null;
+               }
+               return null;
+       }
+
+       private static boolean isIndexToken(String token) {
+               return "-".equals(token) || toArrayIndex(token) >= 0;
+       }
+
+       /*
+        * Returns the non-negative array index for the specified token, or -1 
if the token is not a valid RFC 6901 array
+        * index (no leading zeros except "0" itself; "-" is not a numeric 
index).
+        */
+       private static int toArrayIndex(String token) {
+               var len = token.length();
+               if (len == 0)
+                       return -1;
+               if (len > 1 && token.charAt(0) == '0')
+                       return -1;
+               for (var i = 0; i < len; i++) {
+                       var c = token.charAt(i);
+                       if (c < '0' || c > '9')
+                               return -1;
+               }
+               try {
+                       return Integer.parseInt(token);
+               } catch (NumberFormatException e) {  // HTT - guards against 
overflow on very long digit strings.
+                       return -1;
+               }
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledList.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledList.java
index a24ad6c53b..90c28a4c6c 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledList.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledList.java
@@ -36,7 +36,7 @@ import org.apache.juneau.marshall.serializer.*;
  * <p>
  * This class is the base for {@link JsonList} and the per-marshaller {@code 
XList} variants. It carries
  * all marshaller-neutral functionality such as typed accessors, fluent 
setters, bean integration,
- * and {@link ObjectRest}-based path navigation. The default {@link 
#toString()} comes from
+ * and {@link PathTraversal}-based path navigation. The default {@link 
#toString()} comes from
  * {@link LinkedList}; subclasses should override it to produce the 
appropriate marshalled form.
  *
  * <p>
@@ -384,7 +384,7 @@ public class MarshalledList extends LinkedList<Object> {
 
        transient MarshallingSession session = null;
 
-       private transient ObjectRest objectRest;
+       private transient PathTraversal pathTraversal;
 
        /**
         * Construct an empty list.
@@ -540,6 +540,25 @@ public class MarshalledList extends LinkedList<Object> {
                return this;
        }
 
+       /**
+        * Returns the node addressed by the specified <a class="doclink" 
href="https://datatracker.ietf.org/doc/html/rfc6901";>RFC 6901</a>
+        * JSON Pointer relative to this list.
+        *
+        * <p>
+        * Convenience for 
<c>MarshalledNode.<jsm>of</jsm>(<jk>this</jk>).at(<jv>pointer</jv>)</c>. A 
read-miss returns
+        * <jk>null</jk>; an addressed JSON <jk>null</jk> value returns a null 
node.
+        *
+        * <p>
+        * <b>Beta — API subject to change:</b> This method is part of the 
next-generation typed tree model layered over the
+        * {@code Marshalled*} collections and may change incompatibly in a 
future release.
+        *
+        * @param pointer The RFC 6901 JSON Pointer.
+        * @return A node wrapping the addressed value, or <jk>null</jk> if the 
addressed value is absent.
+        */
+       public MarshalledNode at(String pointer) {
+               return MarshalledNode.of(this).at(pointer);
+       }
+
        /**
         * Serializes this list to a string using the specified serializer.
         *
@@ -555,14 +574,14 @@ public class MarshalledList extends LinkedList<Object> {
         * this POJO.
         *
         * <p>
-        * This method uses the {@link ObjectRest} class to perform the lookup, 
so the map can contain any of the various
-        * class types that the {@link ObjectRest} class supports (e.g. beans, 
collections, arrays).
+        * This method uses the {@link PathTraversal} class to perform the 
lookup, so the map can contain any of the various
+        * class types that the {@link PathTraversal} class supports (e.g. 
beans, collections, arrays).
         *
         * @param path The path to the entry.
         * @return The previous value, or <jk>null</jk> if the entry doesn't 
exist.
         */
        public Object deleteAt(String path) {
-               return getObjectRest().delete(path);
+               return getPathTraversal().delete(path);
        }
 
        /**
@@ -646,7 +665,7 @@ public class MarshalledList extends LinkedList<Object> {
         * @return The value, or <jk>null</jk> if the entry doesn't exist.
         */
        public <T> T getAt(String path, Class<T> type) {
-               return getObjectRest().get(path, type);
+               return getPathTraversal().get(path, type);
        }
 
        /**
@@ -660,7 +679,7 @@ public class MarshalledList extends LinkedList<Object> {
         * @return The value, or <jk>null</jk> if the entry doesn't exist.
         */
        public <T> T getAt(String path, Type type, Type...args) {
-               return getObjectRest().get(path, type, args);
+               return getPathTraversal().get(path, type, args);
        }
 
        /**
@@ -798,15 +817,15 @@ public class MarshalledList extends LinkedList<Object> {
         * Similar to {@link #putAt(String,Object) putAt(String,Object)}, but 
used to append to collections and arrays.
         *
         * <p>
-        * This method uses the {@link ObjectRest} class to perform the lookup, 
so the map can contain any of the various
-        * class types that the {@link ObjectRest} class supports (e.g. beans, 
collections, arrays).
+        * This method uses the {@link PathTraversal} class to perform the 
lookup, so the map can contain any of the various
+        * class types that the {@link PathTraversal} class supports (e.g. 
beans, collections, arrays).
         *
         * @param path The path to the entry.
         * @param o The new value.
         * @return The previous value, or <jk>null</jk> if the entry doesn't 
exist.
         */
        public Object postAt(String path, Object o) {
-               return getObjectRest().post(path, o);
+               return getPathTraversal().post(path, o);
        }
 
        /**
@@ -814,15 +833,15 @@ public class MarshalledList extends LinkedList<Object> {
         * in this POJO.
         *
         * <p>
-        * This method uses the {@link ObjectRest} class to perform the lookup, 
so the map can contain any of the various
-        * class types that the {@link ObjectRest} class supports (e.g. beans, 
collections, arrays).
+        * This method uses the {@link PathTraversal} class to perform the 
lookup, so the map can contain any of the various
+        * class types that the {@link PathTraversal} class supports (e.g. 
beans, collections, arrays).
         *
         * @param path The path to the entry.
         * @param o The new value.
         * @return The previous value, or <jk>null</jk> if the entry doesn't 
exist.
         */
        public Object putAt(String path, Object o) {
-               return getObjectRest().put(path, o);
+               return getPathTraversal().put(path, o);
        }
 
        /**
@@ -864,10 +883,10 @@ public class MarshalledList extends LinkedList<Object> {
                return new UnmodifiableMarshalledList(this);
        }
 
-       private ObjectRest getObjectRest() {
-               if (objectRest == null)
-                       objectRest = new ObjectRest(this);
-               return objectRest;
+       private PathTraversal getPathTraversal() {
+               if (pathTraversal == null)
+                       pathTraversal = new PathTraversal(this);
+               return pathTraversal;
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledMap.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledMap.java
index 16b775728c..0210e3e6f3 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledMap.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledMap.java
@@ -42,7 +42,7 @@ import org.apache.juneau.marshall.swap.*;
  * <p>
  * This class is the base for {@link JsonMap} and the per-marshaller {@code 
XMap} variants. It carries
  * all marshaller-neutral functionality such as typed accessors, fluent 
setters, bean integration,
- * and {@link ObjectRest}-based path navigation. The default {@link 
#toString()} comes from
+ * and {@link PathTraversal}-based path navigation. The default {@link 
#toString()} comes from
  * {@link LinkedHashMap}; subclasses should override it to produce the 
appropriate marshalled form.
  *
  * <p>
@@ -290,7 +290,7 @@ public class MarshalledMap extends 
LinkedHashMap<String,Object> {
        private transient MarshallingSession session;
        private transient Map<String,Object> inner;
 
-       private transient ObjectRest objectRest;
+       private transient PathTraversal pathTraversal;
 
        private transient Predicate<Object> valueFilter = x -> true;
 
@@ -463,6 +463,25 @@ public class MarshalledMap extends 
LinkedHashMap<String,Object> {
                return this;
        }
 
+       /**
+        * Returns the node addressed by the specified <a class="doclink" 
href="https://datatracker.ietf.org/doc/html/rfc6901";>RFC 6901</a>
+        * JSON Pointer relative to this map.
+        *
+        * <p>
+        * Convenience for 
<c>MarshalledNode.<jsm>of</jsm>(<jk>this</jk>).at(<jv>pointer</jv>)</c>. A 
read-miss returns
+        * <jk>null</jk>; an addressed JSON <jk>null</jk> value returns a null 
node.
+        *
+        * <p>
+        * <b>Beta — API subject to change:</b> This method is part of the 
next-generation typed tree model layered over the
+        * {@code Marshalled*} collections and may change incompatibly in a 
future release.
+        *
+        * @param pointer The RFC 6901 JSON Pointer.
+        * @return A node wrapping the addressed value, or <jk>null</jk> if the 
addressed value is absent.
+        */
+       public MarshalledNode at(String pointer) {
+               return MarshalledNode.of(this).at(pointer);
+       }
+
        /**
         * Serializes this map to a string using the specified serializer.
         *
@@ -560,14 +579,14 @@ public class MarshalledMap extends 
LinkedHashMap<String,Object> {
         * in this POJO.
         *
         * <p>
-        * This method uses the {@link ObjectRest} class to perform the lookup, 
so the map can contain any of the various
-        * class types that the {@link ObjectRest} class supports (e.g. beans, 
collections, arrays).
+        * This method uses the {@link PathTraversal} class to perform the 
lookup, so the map can contain any of the various
+        * class types that the {@link PathTraversal} class supports (e.g. 
beans, collections, arrays).
         *
         * @param path The path to the entry.
         * @return The previous value, or <jk>null</jk> if the entry doesn't 
exist.
         */
        public Object deleteAt(String path) {
-               return getObjectRest().delete(path);
+               return getPathTraversal().delete(path);
        }
 
        @Override /* Overridden from Map */
@@ -860,7 +879,7 @@ public class MarshalledMap extends 
LinkedHashMap<String,Object> {
         * @return The value, or <jk>null</jk> if the entry doesn't exist.
         */
        public <T> T getAt(String path, Class<T> type) {
-               return getObjectRest().get(path, type);
+               return getPathTraversal().get(path, type);
        }
 
        /**
@@ -874,7 +893,7 @@ public class MarshalledMap extends 
LinkedHashMap<String,Object> {
         * @return The value, or <jk>null</jk> if the entry doesn't exist.
         */
        public <T> T getAt(String path, Type type, Type...args) {
-               return getObjectRest().get(path, type, args);
+               return getPathTraversal().get(path, type, args);
        }
 
        /**
@@ -1327,15 +1346,15 @@ public class MarshalledMap extends 
LinkedHashMap<String,Object> {
         * Similar to {@link #putAt(String,Object) putAt(String,Object)}, but 
used to append to collections and arrays.
         *
         * <p>
-        * This method uses the {@link ObjectRest} class to perform the lookup, 
so the map can contain any of the various
-        * class types that the {@link ObjectRest} class supports (e.g. beans, 
collections, arrays).
+        * This method uses the {@link PathTraversal} class to perform the 
lookup, so the map can contain any of the various
+        * class types that the {@link PathTraversal} class supports (e.g. 
beans, collections, arrays).
         *
         * @param path The path to the entry.
         * @param o The new value.
         * @return The previous value, or <jk>null</jk> if the entry doesn't 
exist.
         */
        public Object postAt(String path, Object o) {
-               return getObjectRest().post(path, o);
+               return getPathTraversal().post(path, o);
        }
 
        @Override
@@ -1350,15 +1369,15 @@ public class MarshalledMap extends 
LinkedHashMap<String,Object> {
         * POJO.
         *
         * <p>
-        * This method uses the {@link ObjectRest} class to perform the lookup, 
so the map can contain any of the various
-        * class types that the {@link ObjectRest} class supports (e.g. beans, 
collections, arrays).
+        * This method uses the {@link PathTraversal} class to perform the 
lookup, so the map can contain any of the various
+        * class types that the {@link PathTraversal} class supports (e.g. 
beans, collections, arrays).
         *
         * @param path The path to the entry.
         * @param o The new value.
         * @return The previous value, or <jk>null</jk> if the entry doesn't 
exist.
         */
        public Object putAt(String path, Object o) {
-               return getObjectRest().put(path, o);
+               return getPathTraversal().put(path, o);
        }
 
        /**
@@ -1598,10 +1617,10 @@ public class MarshalledMap extends 
LinkedHashMap<String,Object> {
                throw bex(cm.inner(), "Cannot convert to class type ''{0}''.  
Only beans and maps can be converted using this method.", cn(cm));
        }
 
-       private ObjectRest getObjectRest() {
-               if (objectRest == null)
-                       objectRest = new ObjectRest(this);
-               return objectRest;
+       private PathTraversal getPathTraversal() {
+               if (pathTraversal == null)
+                       pathTraversal = new PathTraversal(this);
+               return pathTraversal;
        }
 
        /*
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledNode.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledNode.java
new file mode 100644
index 0000000000..eb64e49cde
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/MarshalledNode.java
@@ -0,0 +1,391 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.marshall.collections;
+
+import static org.apache.juneau.commons.utils.ThrowableUtils.*;
+
+import java.util.*;
+
+import org.apache.juneau.marshall.*;
+
+/**
+ * Typed tree façade over the generic-collections model ({@link MarshalledMap} 
/ {@link MarshalledList} and their
+ * {@link JsonMap} / {@link JsonList} subclasses).
+ *
+ * <p>
+ * This is a <b>live, mutating view</b> (a façade — <i>not</i> a parallel node 
hierarchy) wrapping a single backing
+ * value which is one of:
+ * <ul>
+ *     <li>a {@link Map} (an <i>object</i> node),
+ *     <li>a {@link List} (an <i>array</i> node),
+ *     <li>a scalar value ({@link String}, {@link Number}, {@link Boolean}, 
etc.) (a <i>value</i> node), or
+ *     <li><jk>null</jk> (a <i>null</i> node).
+ * </ul>
+ *
+ * <p>
+ * It gives Jackson-migrants the ergonomics they expect (node-type 
introspection, typed value accessors, and fluent
+ * tree building) without replacing the underlying {@link Map}/{@link List} 
representation. Because it is a live view,
+ * mutating builders such as {@link #put(String,Object)} and {@link 
#add(Object)} mutate the backing container
+ * <i>in place</i> — callers holding the underlying map/list see the change. 
Use {@link #copy()} for an independent
+ * deep-copy snapshot.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ *     <jc>// Build a tree fluently (mutates a backing JsonMap/JsonList in 
place).</jc>
+ *     MarshalledNode <jv>node</jv> = MarshalledNode.<jsm>objectNode</jsm>()
+ *             .put(<js>"a"</js>, 1)
+ *             .put(<js>"b"</js>, 
MarshalledNode.<jsm>arrayNode</jsm>().add(<js>"x"</js>).add(<js>"y"</js>).value());
+ *
+ *     String <jv>json</jv> = Json.<jsm>of</jsm>(<jv>node</jv>.value());  
<jc>// {"a":1,"b":["x","y"]}</jc>
+ *
+ *     <jc>// Navigate and read typed values.</jc>
+ *     Integer <jv>a</jv> = <jv>node</jv>.get(<js>"a"</js>).asInt();      
<jc>// 1</jc>
+ *     String <jv>x</jv> = <jv>node</jv>.get(<js>"b"</js>).get(0).asString();  
<jc>// "x"</jc>
+ * </p>
+ *
+ * <h5 class='section'>Notes:</h5><ul>
+ *     <li class='warn'>This class is not thread safe.
+ * </ul>
+ *
+ * <p>
+ * <b>Beta — API subject to change:</b> This type is part of the 
next-generation typed tree model layered over the
+ * {@code Marshalled*} collections and may change incompatibly in a future 
release.
+ */
+public class MarshalledNode {
+
+       /**
+        * Wraps any value as a node.
+        *
+        * <p>
+        * The value is stored as-is (no copy): a {@link Map} becomes an object 
node, a {@link List} becomes an array node,
+        * a non-<jk>null</jk> non-container value becomes a value node, and 
<jk>null</jk> becomes a null node. For navigation
+        * and fluent building to work the value should be (or contain) the 
{@link MarshalledMap}/{@link MarshalledList}
+        * family, but any {@link Map}/{@link List} is accepted.
+        *
+        * @param value The value to wrap. Can be <jk>null</jk>.
+        * @return A new node wrapping the value, never <jk>null</jk>.
+        */
+       public static MarshalledNode of(Object value) {
+               return new MarshalledNode(value);
+       }
+
+       /**
+        * Creates a new object node backed by a new empty {@link JsonMap}.
+        *
+        * @return A new object node, never <jk>null</jk>.
+        */
+       public static MarshalledNode objectNode() {
+               return new MarshalledNode(new JsonMap());
+       }
+
+       /**
+        * Creates a new array node backed by a new empty {@link JsonList}.
+        *
+        * @return A new array node, never <jk>null</jk>.
+        */
+       public static MarshalledNode arrayNode() {
+               return new MarshalledNode(new JsonList());
+       }
+
+       private final Object value;
+
+       private MarshalledNode(Object value) {
+               this.value = value;
+       }
+
+       /**
+        * Returns <jk>true</jk> if this node wraps a {@link Map} (an object 
node).
+        *
+        * @return <jk>true</jk> if this is an object node.
+        */
+       public boolean isObject() {
+               return value instanceof Map;
+       }
+
+       /**
+        * Returns <jk>true</jk> if this node wraps a {@link List} (an array 
node).
+        *
+        * @return <jk>true</jk> if this is an array node.
+        */
+       public boolean isArray() {
+               return value instanceof List;
+       }
+
+       /**
+        * Returns <jk>true</jk> if this node wraps a non-<jk>null</jk> scalar 
(not a {@link Map} or {@link List}).
+        *
+        * @return <jk>true</jk> if this is a value node.
+        */
+       public boolean isValue() {
+               return value != null && ! (value instanceof Map) && ! (value 
instanceof List);
+       }
+
+       /**
+        * Returns <jk>true</jk> if this node wraps <jk>null</jk>.
+        *
+        * @return <jk>true</jk> if this is a null node.
+        */
+       public boolean isNull() {
+               return value == null;
+       }
+
+       /**
+        * Returns the backing value converted to a {@link String}.
+        *
+        * @return The converted value, or <jk>null</jk> if this is not a value 
node or the value cannot be converted.
+        */
+       public String asString() {
+               return asScalar(String.class);
+       }
+
+       /**
+        * Returns the backing value converted to an {@link Integer}.
+        *
+        * @return The converted value, or <jk>null</jk> if this is not a value 
node or the value cannot be converted.
+        */
+       public Integer asInt() {
+               return asScalar(Integer.class);
+       }
+
+       /**
+        * Returns the backing value converted to a {@link Long}.
+        *
+        * @return The converted value, or <jk>null</jk> if this is not a value 
node or the value cannot be converted.
+        */
+       public Long asLong() {
+               return asScalar(Long.class);
+       }
+
+       /**
+        * Returns the backing value converted to a {@link Double}.
+        *
+        * @return The converted value, or <jk>null</jk> if this is not a value 
node or the value cannot be converted.
+        */
+       public Double asDouble() {
+               return asScalar(Double.class);
+       }
+
+       /**
+        * Returns the backing value converted to a {@link Boolean}.
+        *
+        * @return The converted value, or <jk>null</jk> if this is not a value 
node or the value cannot be converted.
+        */
+       public Boolean asBoolean() {
+               return asScalar(Boolean.class);
+       }
+
+       /**
+        * Converts the raw backing value to the specified type.
+        *
+        * <p>
+        * Unlike the scalar accessors, this works on object nodes too (e.g. 
converting a {@link Map} object node to a bean).
+        * Routes through the same conversion machinery used by {@link 
MarshalledMap#get(String,Class)}.
+        *
+        * @param <T> The type to convert to.
+        * @param type The type to convert to.
+        * @return The converted value, or <jk>null</jk> if the backing value 
is <jk>null</jk> or cannot be converted.
+        */
+       public <T> T as(Class<T> type) {
+               if (value == null)
+                       return null;
+               try {
+                       return 
MarshallingContext.DEFAULT_SESSION.convertToType(value, type);
+               } catch (InvalidDataConversionException e) {
+                       return null;
+               }
+       }
+
+       /**
+        * Returns the raw backing value wrapped by this node.
+        *
+        * @return The raw backing value. Can be <jk>null</jk>.
+        */
+       public Object value() {
+               return value;
+       }
+
+       /**
+        * Returns the child node for the specified key on an object node.
+        *
+        * @param name The key.
+        * @return The child node, or <jk>null</jk> if this is not an object 
node or the key is absent.
+        */
+       public MarshalledNode get(String name) {
+               if (value instanceof Map<?,?> value2 && 
value2.containsKey(name))
+                       return new MarshalledNode(value2.get(name));
+               return null;
+       }
+
+       /**
+        * Returns the element node at the specified index on an array node.
+        *
+        * @param index The zero-based index.
+        * @return The element node, or <jk>null</jk> if this is not an array 
node or the index is out of range.
+        */
+       public MarshalledNode get(int index) {
+               if (value instanceof List<?> value2 && index >= 0 && index < 
value2.size())
+                       return new MarshalledNode(value2.get(index));
+               return null;
+       }
+
+       /**
+        * Returns the number of entries (object) or elements (array) in this 
node.
+        *
+        * @return The size, or <c>0</c> if this is a value or null node.
+        */
+       public int size() {
+               if (value instanceof Map<?,?> value2)
+                       return value2.size();
+               if (value instanceof List<?> value2)
+                       return value2.size();
+               return 0;
+       }
+
+       /**
+        * Adds or replaces an entry on an object node, mutating the backing 
{@link Map} in place.
+        *
+        * @param name The key.
+        * @param value The value.
+        * @return This object.
+        * @throws IllegalStateException If this is not an object node.
+        */
+       @SuppressWarnings({
+               "unchecked" // Backing map of an object node always accepts 
String keys / Object values.
+       })
+       public MarshalledNode put(String name, Object value) {
+               if (! isObject())
+                       throw illegalState("put(String,Object) is only valid on 
an object node.");
+               ((Map<String,Object>)this.value).put(name, value);
+               return this;
+       }
+
+       /**
+        * Appends a value on an array node, mutating the backing {@link List} 
in place.
+        *
+        * @param value The value to append.
+        * @return This object.
+        * @throws IllegalStateException If this is not an array node.
+        */
+       @SuppressWarnings({
+               "unchecked" // Backing list of an array node always accepts 
Object elements.
+       })
+       public MarshalledNode add(Object value) {
+               if (! isArray())
+                       throw illegalState("add(Object) is only valid on an 
array node.");
+               ((List<Object>)this.value).add(value);
+               return this;
+       }
+
+       /**
+        * Returns a new node backed by a deep copy of this node's backing 
value.
+        *
+        * <p>
+        * Maps are copied into new {@link JsonMap} instances and lists into 
new {@link JsonList} instances (recursively);
+        * scalars and <jk>null</jk> are returned as-is (they are immutable). 
Mutating the copy does not affect the original
+        * and vice versa.
+        *
+        * @return A new independent node, never <jk>null</jk>.
+        */
+       public MarshalledNode copy() {
+               return new MarshalledNode(deepCopy(value));
+       }
+
+       /**
+        * Returns the node addressed by the specified <a class="doclink" 
href="https://datatracker.ietf.org/doc/html/rfc6901";>RFC 6901</a>
+        * JSON Pointer relative to this node's backing value.
+        *
+        * <p>
+        * A read-miss (missing key, out-of-range index, or type mismatch) 
returns <jk>null</jk> so callers can null-check.
+        * If the addressed value exists and is JSON <jk>null</jk>, a null node 
is returned (use {@link #isNull()} to test it).
+        * The empty pointer addresses this node's whole backing value.
+        *
+        * @param pointer The RFC 6901 JSON Pointer.
+        * @return A node wrapping the addressed value, or <jk>null</jk> if the 
addressed value is absent.
+        */
+       public MarshalledNode at(String pointer) {
+               return find(pointer).orElse(null);
+       }
+
+       /**
+        * Optional read variant of {@link #at(String)}.
+        *
+        * @param pointer The RFC 6901 JSON Pointer.
+        * @return The addressed node (present, possibly a null node), or an 
empty {@link Optional} if the addressed value is absent.
+        */
+       public Optional<MarshalledNode> find(String pointer) {
+               var o = JsonPointer.of(pointer).evalOrMissing(value);
+               return JsonPointer.isMissing(o) ? Optional.empty() : 
Optional.of(new MarshalledNode(o));
+       }
+
+       /**
+        * Writes the specified value at the location addressed by the specified
+        * <a class="doclink" 
href="https://datatracker.ietf.org/doc/html/rfc6901";>RFC 6901</a> JSON Pointer, 
mutating the
+        * backing container in place and auto-vivifying missing intermediate 
containers.
+        *
+        * <p>
+        * Intermediate containers are created based on the next token (a 
numeric or <c>-</c> token implies a {@link JsonList},
+        * otherwise a {@link JsonMap}); the <c>-</c> token on a list appends. 
Because the backing value is <jk>final</jk>,
+        * the empty (root) pointer cannot replace this node's whole document 
in place and is therefore rejected.
+        *
+        * @param pointer The RFC 6901 JSON Pointer. Must not be the root 
pointer.
+        * @param value The value to set.
+        * @return This object.
+        * @throws IllegalArgumentException If <c>pointer</c> is the empty/root 
pointer.
+        */
+       public MarshalledNode set(String pointer, Object value) {
+               var p = JsonPointer.of(pointer);
+               if (p.isRoot())
+                       throw illegalArg("Cannot set the whole document via the 
root pointer on a node.");
+               p.set(this.value, value);
+               return this;
+       }
+
+       /**
+        * Removes the member or element addressed by the specified
+        * <a class="doclink" 
href="https://datatracker.ietf.org/doc/html/rfc6901";>RFC 6901</a> JSON Pointer.
+        *
+        * @param pointer The RFC 6901 JSON Pointer.
+        * @return The removed value, or <jk>null</jk> if the addressed 
member/element was absent.
+        */
+       public Object remove(String pointer) {
+               return JsonPointer.of(pointer).remove(value);
+       }
+
+       private <T> T asScalar(Class<T> type) {
+               if (! isValue())
+                       return null;
+               try {
+                       return 
MarshallingContext.DEFAULT_SESSION.convertToType(value, type);
+               } catch (InvalidDataConversionException e) {
+                       return null;
+               }
+       }
+
+       private static Object deepCopy(Object v) {
+               if (v instanceof Map<?,?> v2) {
+                       var r = new JsonMap();
+                       v2.forEach((k, val) -> r.put(String.valueOf(k), 
deepCopy(val)));
+                       return r;
+               }
+               if (v instanceof List<?> v2) {
+                       var r = new JsonList();
+                       v2.forEach(e -> r.add(deepCopy(e)));
+                       return r;
+               }
+               return v;
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/package-info.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/package-info.java
index 44c84c56a6..4fd7a65af6 100755
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/package-info.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/collections/package-info.java
@@ -21,7 +21,7 @@
  * <ul>
  *     <li>{@link org.apache.juneau.marshall.collections.MarshalledMap} / 
{@link org.apache.juneau.marshall.collections.MarshalledList}
  *             &mdash; neutral, marshaller-agnostic base classes carrying 
typed accessors, fluent setters,
- *             bean integration, and {@link 
org.apache.juneau.marshall.objecttools.ObjectRest}-based path navigation.
+ *             bean integration, and {@link 
org.apache.juneau.marshall.objecttools.PathTraversal}-based path navigation.
  *             Their default {@link java.lang.Object#toString()} is the 
inherited {@link java.util.LinkedHashMap}
  *             / {@link java.util.LinkedList} form.
  *     <li>{@link org.apache.juneau.marshall.collections.JsonMap} / {@link 
org.apache.juneau.marshall.collections.JsonList}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/ObjectRest.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/PathTraversal.java
similarity index 81%
rename from 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/ObjectRest.java
rename to 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/PathTraversal.java
index d8718392fe..a5872b0ec2 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/ObjectRest.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/PathTraversal.java
@@ -20,21 +20,22 @@ import static java.net.HttpURLConnection.*;
 import static org.apache.juneau.commons.utils.StringUtils.*;
 import static org.apache.juneau.commons.utils.Utils.*;
 
-import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
 
-import org.apache.juneau.commons.reflect.*;
 import org.apache.juneau.marshall.*;
 import org.apache.juneau.marshall.collections.*;
-import org.apache.juneau.marshall.json5.*;
 import org.apache.juneau.marshall.parser.*;
 
 /**
- * POJO REST API.
+ * Traversal engine for performing standard REST operations (GET, PUT, POST, 
DELETE) against nodes in a POJO model.
+ *
+ * <p>
+ * <b>Internal API:</b> This class is the relocated traversal engine behind 
the {@code Marshalled*} path-addressing
+ * methods and the Swagger/OpenApi {@code $ref} resolver. It is public only 
for cross-module access and is <b>not</b> a
+ * supported public API &mdash; do not use it directly.
  *
  * <p>
- * Provides the ability to perform standard REST operations (GET, PUT, POST, 
DELETE) against nodes in a POJO model.
  * Nodes in the POJO model are addressed using URLs.
  *
  * <p>
@@ -84,8 +85,8 @@ import org.apache.juneau.marshall.parser.*;
  *             + <js>"} "</js>
  *     );
  *
- *     <jc>// Wrap Map inside an ObjectRest object</jc>
- *     ObjectRest <jv>johnSmith</jv> = 
ObjectRest.<jsm>create</jsm>(<jv>map</jv>);
+ *     <jc>// Wrap Map inside a PathTraversal object</jc>
+ *     PathTraversal <jv>johnSmith</jv> = 
PathTraversal.<jsm>create</jsm>(<jv>map</jv>);
  *
  *     <jc>// Get a simple value at the top level</jc>
  *     <jc>// "John Smith"</jc>
@@ -130,14 +131,14 @@ import org.apache.juneau.marshall.parser.*;
  * <h5 class='section'>Example:</h5>
  * <p class='bjava'>
  *     <jc>// Get map/bean with name attribute value of 'foo' from a list of 
items</jc>
- *     Map <jv>map</jv> = 
<jv>objectRest</jv>.getMap(<js>"/items/@name=foo"</js>);
+ *     Map <jv>map</jv> = 
<jv>pathTraversal</jv>.getMap(<js>"/items/@name=foo"</js>);
  * </p>
  */
 @SuppressWarnings({
        "unchecked", // Type erasure requires unchecked operations for generic 
collections
        "rawtypes"   // Raw types necessary for generic type handling
 })
-public class ObjectRest {
+public class PathTraversal {
        class JsonNode {
                Object o;
                ClassMeta cm;
@@ -167,20 +168,20 @@ public class ObjectRest {
        /**
         * Static creator.
         * @param o The object being wrapped.
-        * @return A new {@link ObjectRest} object.
+        * @return A new {@link PathTraversal} object.
         */
-       public static ObjectRest create(Object o) {
-               return new ObjectRest(o);
+       public static PathTraversal create(Object o) {
+               return new PathTraversal(o);
        }
 
        /**
         * Static creator.
         * @param o The object being wrapped.
         * @param parser The parser to use for parsing arguments and converting 
objects to the correct data type.
-        * @return A new {@link ObjectRest} object.
+        * @return A new {@link PathTraversal} object.
         */
-       public static ObjectRest create(Object o, ReaderParser parser) {
-               return new ObjectRest(o, parser);
+       public static PathTraversal create(Object o, ReaderParser parser) {
+               return new PathTraversal(o, parser);
        }
 
        /** Handle nulls and strip off leading '/' char. */
@@ -201,7 +202,7 @@ public class ObjectRest {
                try {
                        return Integer.parseInt(key);
                } catch (@SuppressWarnings("unused") NumberFormatException e) {
-                       throw new ObjectRestException(HTTP_BAD_REQUEST, "Cannot 
address an item in an array with a non-integer key ''{0}''", key);
+                       throw new PathTraversalException(HTTP_BAD_REQUEST, 
"Cannot address an item in an array with a non-integer key ''{0}''", key);
                }
        }
 
@@ -214,8 +215,6 @@ public class ObjectRest {
                return a2;
        }
 
-       private ReaderParser parser = Json5Parser.DEFAULT;
-
        final MarshallingSession session;
 
        /** If true, the root cannot be overwritten */
@@ -232,7 +231,7 @@ public class ObjectRest {
         *
         * @param o The object to be wrapped.
         */
-       public ObjectRest(Object o) {
+       public PathTraversal(Object o) {
                this(o, null);
        }
 
@@ -245,11 +244,8 @@ public class ObjectRest {
         * @param o The object to be wrapped.
         * @param parser The parser to use for parsing arguments and converting 
objects to the correct data type.
         */
-       public ObjectRest(Object o, ReaderParser parser) {
+       public PathTraversal(Object o, ReaderParser parser) {
                this.session = parser == null ? 
MarshallingContext.DEFAULT_SESSION : 
parser.getMarshallingContext().getSession();
-               if (parser == null)
-                       parser = Json5Parser.DEFAULT;
-               this.parser = parser;
                this.root = new JsonNode(null, null, o, session.object());
        }
 
@@ -288,22 +284,22 @@ public class ObjectRest {
         *
         * <h5 class='section'>Examples:</h5>
         * <p class='bjava'>
-        *      ObjectRest <jv>objectRest</jv> = <jk>new</jk> 
ObjectRest(<jv>object</jv>);
+        *      PathTraversal <jv>pathTraversal</jv> = <jk>new</jk> 
PathTraversal(<jv>object</jv>);
         *
         *      <jc>// Value converted to a string.</jc>
-        *      String <jv>string</jv> = 
<jv>objectRest</jv>.get(<js>"path/to/string"</js>, String.<jk>class</jk>);
+        *      String <jv>string</jv> = 
<jv>pathTraversal</jv>.get(<js>"path/to/string"</js>, String.<jk>class</jk>);
         *
         *      <jc>// Value converted to a bean.</jc>
-        *      MyBean <jv>bean</jv> = 
<jv>objectRest</jv>.get(<js>"path/to/bean"</js>, MyBean.<jk>class</jk>);
+        *      MyBean <jv>bean</jv> = 
<jv>pathTraversal</jv>.get(<js>"path/to/bean"</js>, MyBean.<jk>class</jk>);
         *
         *      <jc>// Value converted to a bean array.</jc>
-        *      MyBean[] <jv>beanArray</jv> = 
<jv>objectRest</jv>.get(<js>"path/to/beanarray"</js>, MyBean[].<jk>class</jk>);
+        *      MyBean[] <jv>beanArray</jv> = 
<jv>pathTraversal</jv>.get(<js>"path/to/beanarray"</js>, 
MyBean[].<jk>class</jk>);
         *
         *      <jc>// Value converted to a linked-list of objects.</jc>
-        *      List <jv>list</jv> = 
<jv>objectRest</jv>.get(<js>"path/to/list"</js>, LinkedList.<jk>class</jk>);
+        *      List <jv>list</jv> = 
<jv>pathTraversal</jv>.get(<js>"path/to/list"</js>, LinkedList.<jk>class</jk>);
         *
         *      <jc>// Value converted to a map of object keys/values.</jc>
-        *      Map <jv>map</jv> = 
<jv>objectRest</jv>.get(<js>"path/to/map"</js>, TreeMap.<jk>class</jk>);
+        *      Map <jv>map</jv> = 
<jv>pathTraversal</jv>.get(<js>"path/to/map"</js>, TreeMap.<jk>class</jk>);
         * </p>
         *
         * @param url
@@ -329,22 +325,22 @@ public class ObjectRest {
         *
         * <h5 class='section'>Examples:</h5>
         * <p class='bjava'>
-        *      ObjectRest <jv>objectRest</jv> = <jk>new</jk> 
ObjectRest(<jv>object</jv>);
+        *      PathTraversal <jv>pathTraversal</jv> = <jk>new</jk> 
PathTraversal(<jv>object</jv>);
         *
         *      <jc>// Value converted to a linked-list of strings.</jc>
-        *      List&lt;String&gt; <jv>list1</jv> = 
<jv>objectRest</jv>.get(<js>"path/to/list1"</js>, LinkedList.<jk>class</jk>, 
String.<jk>class</jk>);
+        *      List&lt;String&gt; <jv>list1</jv> = 
<jv>pathTraversal</jv>.get(<js>"path/to/list1"</js>, LinkedList.<jk>class</jk>, 
String.<jk>class</jk>);
         *
         *      <jc>// Value converted to a linked-list of beans.</jc>
-        *      List&lt;MyBean&gt; <jv>list2</jv> = 
<jv>objectRest</jv>.get(<js>"path/to/list2"</js>, LinkedList.<jk>class</jk>, 
MyBean.<jk>class</jk>);
+        *      List&lt;MyBean&gt; <jv>list2</jv> = 
<jv>pathTraversal</jv>.get(<js>"path/to/list2"</js>, LinkedList.<jk>class</jk>, 
MyBean.<jk>class</jk>);
         *
         *      <jc>// Value converted to a linked-list of linked-lists of 
strings.</jc>
-        *      List&lt;List&lt;String&gt;&gt; <jv>list3</jv> = 
<jv>objectRest</jv>.get(<js>"path/to/list3"</js>, LinkedList.<jk>class</jk>, 
LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+        *      List&lt;List&lt;String&gt;&gt; <jv>list3</jv> = 
<jv>pathTraversal</jv>.get(<js>"path/to/list3"</js>, LinkedList.<jk>class</jk>, 
LinkedList.<jk>class</jk>, String.<jk>class</jk>);
         *
         *      <jc>// Value converted to a map of string keys/values.</jc>
-        *      Map&lt;String,String&gt; <jv>map1</jv> = 
<jv>objectRest</jv>.get(<js>"path/to/map1"</js>, TreeMap.<jk>class</jk>, 
String.<jk>class</jk>, String.<jk>class</jk>);
+        *      Map&lt;String,String&gt; <jv>map1</jv> = 
<jv>pathTraversal</jv>.get(<js>"path/to/map1"</js>, TreeMap.<jk>class</jk>, 
String.<jk>class</jk>, String.<jk>class</jk>);
         *
         *      <jc>// Value converted to a map containing string keys and 
values of lists containing beans.</jc>
-        *      Map&lt;String,List&lt;MyBean&gt;&gt; <jv>map2</jv> = 
<jv>objectRest</jv>.get(<js>"path/to/map2"</js>, TreeMap.<jk>class</jk>, 
String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
+        *      Map&lt;String,List&lt;MyBean&gt;&gt; <jv>map2</jv> = 
<jv>pathTraversal</jv>.get(<js>"path/to/map2"</js>, TreeMap.<jk>class</jk>, 
String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
         * </p>
         *
         * <p>
@@ -602,28 +598,6 @@ public class ObjectRest {
                return getWithDefault(url, defVal, Map.class);
        }
 
-       /**
-        * Returns the list of available methods that can be passed to the 
{@link #invokeMethod(String, String, String)}
-        * for the object addressed by the specified URL.
-        *
-        * @param url The URL.
-        * @return The list of methods.
-        */
-       @SuppressWarnings({
-               "java:S1168"     // null when object not found at URL.
-       })
-       public Collection<String> getPublicMethods(String url) {
-               var o = get(url);
-               if (o == null)
-                       return null;
-               return session
-                       .getClassMeta(o.getClass())
-                       .getPublicMethods()
-                       .stream()
-                       .map(x -> x.getSignature())
-                       .toList();
-       }
-
        /**
         * The root object that was passed into the constructor of this method.
         *
@@ -711,41 +685,6 @@ public class ObjectRest {
                return session.convertToType(o, type, args);
        }
 
-       /**
-        * Executes the specified method with the specified parameters on the 
specified object.
-        *
-        * @param url The URL of the element to retrieve.
-        * @param method
-        *      The method signature.
-        *      <p>
-        *      Can be any of the following formats:
-        *      <ul class='spaced-list'>
-        *              <li>
-        *                      Method name only.  e.g. <js>"myMethod"</js>.
-        *              <li>
-        *                      Method name with class names.  e.g. 
<js>"myMethod(String,int)"</js>.
-        *              <li>
-        *                      Method name with fully-qualified class names.  
e.g. <js>"myMethod(java.util.String,int)"</js>.
-        *      </ul>
-        *      <p>
-        *      As a rule, use the simplest format needed to uniquely resolve a 
method.
-        * @param args
-        *      The arguments to pass as parameters to the method.
-        *      These will automatically be converted to the appropriate object 
type if possible.
-        *      This must be an array, like a JSON array.
-        * @return The returned object from the method call.
-        * @throws ExecutableException Exception occurred on invoked 
constructor/method/field.
-        * @throws ParseException Malformed input encountered.
-        * @throws IOException Thrown by underlying stream.
-        */
-       public Object invokeMethod(String url, String method, String args) 
throws ExecutableException, ParseException, IOException {
-               try {
-                       return new ObjectIntrospector(get(url), 
parser).invokeMethod(method, args);
-               } catch (NoSuchMethodException | IllegalArgumentException | 
InvocationTargetException | IllegalAccessException e) {
-                       throw new ExecutableException(e);
-               }
-       }
-
        /**
         * Adds a value to a list element in a POJO model.
         *
@@ -799,7 +738,7 @@ public class ObjectRest {
         *
         * @return This object.
         */
-       public ObjectRest setRootLocked() {
+       public PathTraversal setRootLocked() {
                this.rootLocked = true;
                return this;
        }
@@ -833,7 +772,7 @@ public class ObjectRest {
                "java:S3776", // Cognitive complexity acceptable for REST 
operation routing logic
                "java:S6541" // Cognitive complexity acceptable for REST 
operation routing logic
        })
-       private Object service(int method, String url, Object val) throws 
ObjectRestException {
+       private Object service(int method, String url, Object val) throws 
PathTraversalException {
 
                url = normalizeUrl(url);
 
@@ -850,16 +789,16 @@ public class ObjectRest {
                if (method == PUT) {
                        if (url.isEmpty()) {
                                if (rootLocked)
-                                       throw new 
ObjectRestException(HTTP_FORBIDDEN, "Cannot overwrite root object");
+                                       throw new 
PathTraversalException(HTTP_FORBIDDEN, "Cannot overwrite root object");
                                var o = root.o;
                                root = new JsonNode(null, null, val, 
session.object());
                                return o;
                        }
                        var n = (parentUrl == null ? root : getNode(parentUrl, 
root));
                        if (n == null)
-                               throw new ObjectRestException(HTTP_NOT_FOUND, 
"Node at URL ''{0}'' not found.", parentUrl);
+                               throw new 
PathTraversalException(HTTP_NOT_FOUND, "Node at URL ''{0}'' not found.", 
parentUrl);
                        if (n.o == null && n.cm.isObject())
-                               throw new ObjectRestException(HTTP_NOT_FOUND, 
"Node at URL ''{0}'' not found.", parentUrl);
+                               throw new 
PathTraversalException(HTTP_NOT_FOUND, "Node at URL ''{0}'' not found.", 
parentUrl);
                        var cm = n.cm;
                        var o = n.o;
                        if (cm.isMap())
@@ -867,7 +806,7 @@ public class ObjectRest {
                        if (cm.isCollection() && o instanceof List o2)
                                return o2.set(parseInt(childKey), convert(val, 
cm.getElementType()));
                        if (cm.isCollection())
-                               throw new ObjectRestException(HTTP_BAD_REQUEST, 
"Cannot PUT to indexed position in non-List collection ''{0}'' of type 
''{1}''", url, cm);
+                               throw new 
PathTraversalException(HTTP_BAD_REQUEST, "Cannot PUT to indexed position in 
non-List collection ''{0}'' of type ''{1}''", url, cm);
                        if (cm.isArray()) {
                                o = setArrayEntry(n.o, parseInt(childKey), val, 
cm.getElementType());
                                var pct = n.parent.cm;
@@ -881,11 +820,11 @@ public class ObjectRest {
                                        m.put(n.keyName, o);
                                        return url;
                                }
-                               throw new ObjectRestException(HTTP_BAD_REQUEST, 
"Cannot perform PUT on ''{0}'' with parent node type ''{1}''", url, pct);
+                               throw new 
PathTraversalException(HTTP_BAD_REQUEST, "Cannot perform PUT on ''{0}'' with 
parent node type ''{1}''", url, pct);
                        }
                        if (cm.isBean())
                                return session.toBeanMap(o).put(childKey, val);
-                       throw new ObjectRestException(HTTP_BAD_REQUEST, "Cannot 
perform PUT on ''{0}'' whose parent is of type ''{1}''", url, cm);
+                       throw new PathTraversalException(HTTP_BAD_REQUEST, 
"Cannot perform PUT on ''{0}'' whose parent is of type ''{1}''", url, cm);
                }
 
                if (method == POST) {
@@ -903,13 +842,13 @@ public class ObjectRest {
                                        root = new JsonNode(null, null, o2, 
null);
                                        return url + "/" + (o2.length - 1);
                                }
-                               throw new ObjectRestException(HTTP_BAD_REQUEST, 
"Cannot perform POST on ''{0}'' of type ''{1}''", url, cm);
+                               throw new 
PathTraversalException(HTTP_BAD_REQUEST, "Cannot perform POST on ''{0}'' of 
type ''{1}''", url, cm);
                        }
                        var n = getNode(url, root);
                        if (n == null)
-                               throw new ObjectRestException(HTTP_NOT_FOUND, 
"Node at URL ''{0}'' not found.", url);
+                               throw new 
PathTraversalException(HTTP_NOT_FOUND, "Node at URL ''{0}'' not found.", url);
                        if (n.o == null && n.cm.isObject())
-                               throw new ObjectRestException(HTTP_NOT_FOUND, 
"Node at URL ''{0}'' not found.", url);
+                               throw new 
PathTraversalException(HTTP_NOT_FOUND, "Node at URL ''{0}'' not found.", url);
                        var cm = n.cm;
                        var o = n.o;
                        if (cm.isArray()) {
@@ -925,29 +864,29 @@ public class ObjectRest {
                                        m.put(childKey, o2);
                                        return url + "/" + (o2.length - 1);
                                }
-                               throw new ObjectRestException(HTTP_BAD_REQUEST, 
"Cannot perform POST on ''{0}'' with parent node type ''{1}''", url, pct);
+                               throw new 
PathTraversalException(HTTP_BAD_REQUEST, "Cannot perform POST on ''{0}'' with 
parent node type ''{1}''", url, pct);
                        }
                        if (cm.isCollection()) {
                                var c = (Collection)o;
                                c.add(convert(val, cm.getElementType()));
                                return (c instanceof List c2 ? url + "/" + 
(c2.size() - 1) : null);
                        }
-                       throw new ObjectRestException(HTTP_BAD_REQUEST, "Cannot 
perform POST on ''{0}'' of type ''{1}''", url, cm);
+                       throw new PathTraversalException(HTTP_BAD_REQUEST, 
"Cannot perform POST on ''{0}'' of type ''{1}''", url, cm);
                }
 
                if (method == DELETE) {
                        if (url.isEmpty()) {
                                if (rootLocked)
-                                       throw new 
ObjectRestException(HTTP_FORBIDDEN, "Cannot overwrite root object");
+                                       throw new 
PathTraversalException(HTTP_FORBIDDEN, "Cannot overwrite root object");
                                var o = root.o;
                                root = new JsonNode(null, null, null, 
session.object());
                                return o;
                        }
                        var n = (parentUrl == null ? root : getNode(parentUrl, 
root));
                        if (n == null)
-                               throw new ObjectRestException(HTTP_NOT_FOUND, 
"Node at URL ''{0}'' not found.", parentUrl);
+                               throw new 
PathTraversalException(HTTP_NOT_FOUND, "Node at URL ''{0}'' not found.", 
parentUrl);
                        if (n.o == null && n.cm.isObject())
-                               throw new ObjectRestException(HTTP_NOT_FOUND, 
"Node at URL ''{0}'' not found.", parentUrl);
+                               throw new 
PathTraversalException(HTTP_NOT_FOUND, "Node at URL ''{0}'' not found.", 
parentUrl);
                        var cm = n.cm;
                        var o = n.o;
                        if (cm.isMap())
@@ -955,7 +894,7 @@ public class ObjectRest {
                        if (cm.isCollection() && o instanceof List o2)
                                return o2.remove(parseInt(childKey));
                        if (cm.isCollection())
-                               throw new ObjectRestException(HTTP_BAD_REQUEST, 
"Cannot DELETE indexed position in non-List collection ''{0}'' of type 
''{1}''", url, cm);
+                               throw new 
PathTraversalException(HTTP_BAD_REQUEST, "Cannot DELETE indexed position in 
non-List collection ''{0}'' of type ''{1}''", url, cm);
                        if (cm.isArray()) {
                                int index = parseInt(childKey);
                                var old = ((Object[])o)[index];
@@ -971,11 +910,11 @@ public class ObjectRest {
                                        m.put(n.keyName, o2);
                                        return old;
                                }
-                               throw new ObjectRestException(HTTP_BAD_REQUEST, 
"Cannot perform POST on ''{0}'' with parent node type ''{1}''", url, pct);
+                               throw new 
PathTraversalException(HTTP_BAD_REQUEST, "Cannot perform POST on ''{0}'' with 
parent node type ''{1}''", url, pct);
                        }
                        if (cm.isBean())
                                return session.toBeanMap(o).put(childKey, null);
-                       throw new ObjectRestException(HTTP_BAD_REQUEST, "Cannot 
perform PUT on ''{0}'' whose parent is of type ''{1}''", url, cm);
+                       throw new PathTraversalException(HTTP_BAD_REQUEST, 
"Cannot perform PUT on ''{0}'' whose parent is of type ''{1}''", url, cm);
                }
 
                return null;    // Never gets here.
@@ -1039,7 +978,7 @@ public class ObjectRest {
                        o2 = m.get(parentKey);
                        var pMeta = m.getPropertyMeta(parentKey);
                        if (pMeta == null)
-                               throw new ObjectRestException(HTTP_BAD_REQUEST, 
"Unknown property ''{0}'' encountered while trying to parse into class 
''{1}''", parentKey, m.getBeanInfo());
+                               throw new 
PathTraversalException(HTTP_BAD_REQUEST, "Unknown property ''{0}'' encountered 
while trying to parse into class ''{1}''", parentKey, m.getBeanInfo());
                        ct2 = (ClassMeta) pMeta.getBeanInfo();
                }
 
@@ -1048,4 +987,4 @@ public class ObjectRest {
 
                return getNode(childUrl, new JsonNode(n, parentKey, o2, ct2));
        }
-}
\ No newline at end of file
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/ObjectRestException.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/PathTraversalException.java
similarity index 87%
rename from 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/ObjectRestException.java
rename to 
juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/PathTraversalException.java
index 26a293ccdf..8ca231399c 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/ObjectRestException.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/objecttools/PathTraversalException.java
@@ -22,7 +22,7 @@ import java.text.*;
 import org.apache.juneau.marshall.*;
 
 /**
- * Generic exception thrown from the {@link ObjectRest} class.
+ * Generic exception thrown from the {@link PathTraversal} class.
  *
  * <p>
  * Typically, this is a user-error, such as trying to address a non-existent 
node in the tree.
@@ -48,7 +48,7 @@ import org.apache.juneau.marshall.*;
        "java:S110", // Deep inheritance inherent to the exception hierarchy
        "java:S2166" // False positive: this IS an exception 
(BasicRuntimeException -> RuntimeException); name is correct public API.
 })
-public class ObjectRestException extends BasicRuntimeException {
+public class PathTraversalException extends BasicRuntimeException {
 
        private static final long serialVersionUID = 1L;
 
@@ -61,7 +61,7 @@ public class ObjectRestException extends 
BasicRuntimeException {
         * @param message The detailed message.
         * @param args Optional {@link MessageFormat}-style arguments.
         */
-       public ObjectRestException(int status, String message, Object...args) {
+       public PathTraversalException(int status, String message, 
Object...args) {
                this(null, status, message, args);
        }
 
@@ -73,7 +73,7 @@ public class ObjectRestException extends 
BasicRuntimeException {
         * @param message The {@link MessageFormat}-style message.
         * @param args Optional {@link MessageFormat}-style arguments.
         */
-       public ObjectRestException(Throwable cause, int status, String message, 
Object...args) {
+       public PathTraversalException(Throwable cause, int status, String 
message, Object...args) {
                super(cause, message, args);
                this.status = status;
        }
@@ -89,8 +89,8 @@ public class ObjectRestException extends 
BasicRuntimeException {
        public int getStatus() { return status; }
 
        @Override /* Overridden from BasicRuntimeException */
-       public ObjectRestException setMessage(String message, Object...args) {
+       public PathTraversalException setMessage(String message, Object...args) 
{
                super.setMessage(message, args);
                return this;
        }
-}
\ No newline at end of file
+}
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/JsonMap_Test.java 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/JsonMap_Test.java
index a648ca0899..877e155744 100755
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/JsonMap_Test.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/JsonMap_Test.java
@@ -26,7 +26,6 @@ import java.util.*;
 import org.apache.juneau.marshall.collections.*;
 import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.marshall.json5.*;
-import org.apache.juneau.marshall.objecttools.*;
 import org.junit.jupiter.api.*;
 
 @SuppressWarnings({
@@ -244,13 +243,11 @@ class JsonMap_Test extends TestBase {
        }
 
        private static String getDeepString(Json5Map m, String url) {
-               var r = ObjectRest.create(m);
-               return (String)r.get(url);
+               return m.getAt(url, String.class);
        }
 
        private static Boolean getDeepBoolean(Json5Map m, String url) {
-               var r = ObjectRest.create(m);
-               return (Boolean)r.get(url);
+               return m.getAt(url, Boolean.class);
        }
 
        private static void checkStep(int step, String input, String output, 
String expectedValue) {
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/JsonPointer_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/JsonPointer_Test.java
new file mode 100644
index 0000000000..c6bb2bfe3a
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/JsonPointer_Test.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.marshall.collections;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Tests for the RFC 6901 JSON-Pointer implementation {@link JsonPointer}.
+ *
+ * <p>
+ * Covers parsing/validation, the RFC 6901 §5 example vectors, {@code 
~0}/{@code ~1} token escaping,
+ * nested read navigation, auto-vivifying writes (including the {@code -} 
append token), and removal.
+ */
+class JsonPointer_Test extends TestBase {
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // a - Parsing / validation / toString
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void a01_emptyPointerIsRoot() {
+               assertEquals("", JsonPointer.of("").toString());
+       }
+
+       @Test void a02_toStringRoundTrip() {
+               assertEquals("/foo/0", JsonPointer.of("/foo/0").toString());
+               assertEquals("/a~1b", JsonPointer.of("/a~1b").toString());
+               assertEquals("/m~0n", JsonPointer.of("/m~0n").toString());
+               assertEquals("/", JsonPointer.of("/").toString());
+       }
+
+       @Test void a03_malformedPointerThrows() {
+               assertThrows(IllegalArgumentException.class, () -> 
JsonPointer.of("foo"));
+               assertThrows(IllegalArgumentException.class, () -> 
JsonPointer.of("foo/bar"));
+               assertThrows(IllegalArgumentException.class, () -> 
JsonPointer.of(null));
+       }
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // b - Token escape / unescape
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void b01_decodeToken() {
+               assertEquals("a/b", JsonPointer.decodeToken("a~1b"));
+               assertEquals("m~n", JsonPointer.decodeToken("m~0n"));
+               // ~01 decodes to ~1 (the ~0 -> ~ is applied after ~1 -> /).
+               assertEquals("~1", JsonPointer.decodeToken("~01"));
+               assertEquals("plain", JsonPointer.decodeToken("plain"));
+       }
+
+       @Test void b02_encodeToken() {
+               assertEquals("a~1b", JsonPointer.encodeToken("a/b"));
+               assertEquals("m~0n", JsonPointer.encodeToken("m~n"));
+               assertEquals("~01", JsonPointer.encodeToken("~1"));
+               assertEquals("plain", JsonPointer.encodeToken("plain"));
+       }
+
+       @Test void b03_escapeRoundTrip() {
+               for (var raw : new String[]{"a/b", "m~n", "~", "/", "a~1b", 
"c/d~e"})
+                       assertEquals(raw, 
JsonPointer.decodeToken(JsonPointer.encodeToken(raw)));
+       }
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // c - RFC 6901 §5 example vectors
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       private static JsonMap rfcDoc() {
+               return JsonMap.of(
+                       "foo", JsonList.of("bar", "baz"),
+                       "", 0,
+                       "a/b", 1,
+                       "c%d", 2,
+                       "e^f", 3,
+                       "g|h", 4,
+                       "i\\j", 5,
+                       "k\"l", 6,
+                       " ", 7,
+                       "m~n", 8
+               );
+       }
+
+       @Test void c01_wholeDocument() {
+               var doc = rfcDoc();
+               assertSame(doc, JsonPointer.of("").eval(doc));
+       }
+
+       @Test void c02_array() {
+               var doc = rfcDoc();
+               assertEquals(JsonList.of("bar", "baz"), 
JsonPointer.of("/foo").eval(doc));
+       }
+
+       @Test void c03_arrayIndex() {
+               assertEquals("bar", JsonPointer.of("/foo/0").eval(rfcDoc()));
+       }
+
+       @Test void c04_emptyStringKey() {
+               assertEquals(0, JsonPointer.of("/").eval(rfcDoc()));
+       }
+
+       @Test void c05_escapedSlash() {
+               assertEquals(1, JsonPointer.of("/a~1b").eval(rfcDoc()));
+       }
+
+       @Test void c06_percent() {
+               assertEquals(2, JsonPointer.of("/c%d").eval(rfcDoc()));
+       }
+
+       @Test void c07_caret() {
+               assertEquals(3, JsonPointer.of("/e^f").eval(rfcDoc()));
+       }
+
+       @Test void c08_pipe() {
+               assertEquals(4, JsonPointer.of("/g|h").eval(rfcDoc()));
+       }
+
+       @Test void c09_backslash() {
+               assertEquals(5, JsonPointer.of("/i\\j").eval(rfcDoc()));
+       }
+
+       @Test void c10_quote() {
+               assertEquals(6, JsonPointer.of("/k\"l").eval(rfcDoc()));
+       }
+
+       @Test void c11_space() {
+               assertEquals(7, JsonPointer.of("/ ").eval(rfcDoc()));
+       }
+
+       @Test void c12_escapedTilde() {
+               assertEquals(8, JsonPointer.of("/m~0n").eval(rfcDoc()));
+       }
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // d - Read navigation + miss behavior
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void d01_nested() {
+               var doc = JsonMap.of("a", JsonMap.of("b", JsonList.of("x", "y", 
"z")));
+               assertEquals("z", JsonPointer.of("/a/b/2").eval(doc));
+       }
+
+       @Test void d02_missingKey() {
+               assertNull(JsonPointer.of("/missing").eval(JsonMap.of("a", 1)));
+               assertNull(JsonPointer.of("/a/missing").eval(JsonMap.of("a", 
JsonMap.of("b", 1))));
+       }
+
+       @Test void d03_indexOutOfRange() {
+               assertNull(JsonPointer.of("/0").eval(JsonList.of()));
+               assertNull(JsonPointer.of("/5").eval(JsonList.of("x")));
+       }
+
+       @Test void d04_typeMismatch() {
+               // Descending into a scalar yields a miss.
+               assertNull(JsonPointer.of("/a/b").eval(JsonMap.of("a", 
"scalar")));
+               // Non-numeric token on an array yields a miss.
+               assertNull(JsonPointer.of("/x").eval(JsonList.of(1, 2)));
+       }
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // e - Write (set) + auto-vivify + append
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void e01_replaceExisting() {
+               var root = JsonMap.of("a", 1);
+               assertSame(root, JsonPointer.of("/a").set(root, 2));
+               assertEquals(2, JsonPointer.of("/a").eval(root));
+       }
+
+       @Test void e02_vivifyNestedObject() {
+               var root = new JsonMap();
+               JsonPointer.of("/a/b/c").set(root, 1);
+               assertEquals("{\"a\":{\"b\":{\"c\":1}}}", Json.of(root));
+       }
+
+       @Test void e03_vivifyNestedArray() {
+               var root = new JsonMap();
+               JsonPointer.of("/a/0").set(root, "x");
+               assertEquals("{\"a\":[\"x\"]}", Json.of(root));
+               assertInstanceOf(JsonList.class, root.get("a"));
+       }
+
+       @Test void e04_appendToken() {
+               var root = JsonMap.of("a", JsonList.of(1, 2));
+               JsonPointer.of("/a/-").set(root, 3);
+               assertEquals("{\"a\":[1,2,3]}", Json.of(root));
+       }
+
+       @Test void e05_rootPointerReturnsValue() {
+               assertEquals("v", JsonPointer.of("").set(JsonMap.of("a", 1), 
"v"));
+       }
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // f - Remove
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void f01_removeMember() {
+               var root = JsonMap.of("a", 1, "b", 2);
+               assertEquals(1, JsonPointer.of("/a").remove(root));
+               assertEquals("{\"b\":2}", Json.of(root));
+       }
+
+       @Test void f02_removeArrayElement() {
+               var root = JsonMap.of("a", JsonList.of("x", "y", "z"));
+               assertEquals("y", JsonPointer.of("/a/1").remove(root));
+               assertEquals("{\"a\":[\"x\",\"z\"]}", Json.of(root));
+       }
+
+       @Test void f03_removeAbsentReturnsNull() {
+               assertNull(JsonPointer.of("/missing").remove(JsonMap.of("a", 
1)));
+               assertNull(JsonPointer.of("/a/5").remove(JsonMap.of("a", 
JsonList.of("x"))));
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/MarshalledNode_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/MarshalledNode_Test.java
new file mode 100644
index 0000000000..7b82ab49ca
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/collections/MarshalledNode_Test.java
@@ -0,0 +1,346 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.marshall.collections;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Tests for the typed tree façade {@link MarshalledNode}.
+ *
+ * <p>
+ * Covers node-type predicates, typed accessors, child navigation, fluent tree 
building (asserting both the
+ * underlying {@link JsonMap}/{@link JsonList} structure and the serialized 
JSON output), the live-view mutation
+ * contract, and {@link MarshalledNode#copy()} deep-copy independence.
+ */
+class MarshalledNode_Test extends TestBase {
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // a - Predicates
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void a01_objectNodePredicates() {
+               var n = MarshalledNode.of(JsonMap.of("a", 1));
+               assertTrue(n.isObject());
+               assertFalse(n.isArray());
+               assertFalse(n.isValue());
+               assertFalse(n.isNull());
+       }
+
+       @Test void a02_arrayNodePredicates() {
+               var n = MarshalledNode.of(JsonList.of(1, 2, 3));
+               assertFalse(n.isObject());
+               assertTrue(n.isArray());
+               assertFalse(n.isValue());
+               assertFalse(n.isNull());
+       }
+
+       @Test void a03_valueNodePredicates() {
+               var n = MarshalledNode.of("hello");
+               assertFalse(n.isObject());
+               assertFalse(n.isArray());
+               assertTrue(n.isValue());
+               assertFalse(n.isNull());
+       }
+
+       @Test void a04_nullNodePredicates() {
+               var n = MarshalledNode.of(null);
+               assertFalse(n.isObject());
+               assertFalse(n.isArray());
+               assertFalse(n.isValue());
+               assertTrue(n.isNull());
+       }
+
+       @Test void a05_objectNodeFactory() {
+               var n = MarshalledNode.objectNode();
+               assertTrue(n.isObject());
+               assertInstanceOf(JsonMap.class, n.value());
+               assertEquals(0, n.size());
+       }
+
+       @Test void a06_arrayNodeFactory() {
+               var n = MarshalledNode.arrayNode();
+               assertTrue(n.isArray());
+               assertInstanceOf(JsonList.class, n.value());
+               assertEquals(0, n.size());
+       }
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // b - Typed accessors
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void b01_asString() {
+               assertEquals("hello", MarshalledNode.of("hello").asString());
+               assertEquals("123", MarshalledNode.of(123).asString());
+       }
+
+       @Test void b02_asInt() {
+               assertEquals(Integer.valueOf(42), 
MarshalledNode.of(42).asInt());
+               assertEquals(Integer.valueOf(7), 
MarshalledNode.of("7").asInt());
+       }
+
+       @Test void b03_asLong() {
+               assertEquals(Long.valueOf(9999999999L), 
MarshalledNode.of(9999999999L).asLong());
+               assertEquals(Long.valueOf(5), MarshalledNode.of("5").asLong());
+       }
+
+       @Test void b04_asDouble() {
+               assertEquals(Double.valueOf(3.5), 
MarshalledNode.of(3.5).asDouble());
+               assertEquals(Double.valueOf(2.0), 
MarshalledNode.of("2.0").asDouble());
+       }
+
+       @Test void b05_asBoolean() {
+               assertEquals(Boolean.TRUE, MarshalledNode.of(true).asBoolean());
+               assertEquals(Boolean.TRUE, 
MarshalledNode.of("true").asBoolean());
+               assertEquals(Boolean.FALSE, 
MarshalledNode.of("false").asBoolean());
+       }
+
+       public static class B06_Bean {
+               public String name;
+               public int age;
+       }
+
+       @Test void b06_asBean() {
+               var n = MarshalledNode.of(JsonMap.of("name", "John", "age", 
45));
+               var b = n.as(B06_Bean.class);
+               assertNotNull(b);
+               assertEquals("John", b.name);
+               assertEquals(45, b.age);
+       }
+
+       @Test void b07_wrongTypeReturnsNull() {
+               // Scalar accessors on container/null nodes return null.
+               assertNull(MarshalledNode.of(JsonMap.of("a", 1)).asString());
+               assertNull(MarshalledNode.of(JsonList.of(1)).asInt());
+               assertNull(MarshalledNode.of(null).asString());
+               assertNull(MarshalledNode.of(null).asInt());
+               // Non-convertible scalar returns null rather than throwing.
+               assertNull(MarshalledNode.of("notanumber").asInt());
+       }
+
+       @Test void b08_value() {
+               var raw = JsonMap.of("a", 1);
+               assertSame(raw, MarshalledNode.of(raw).value());
+               assertEquals("x", MarshalledNode.of("x").value());
+               assertNull(MarshalledNode.of(null).value());
+       }
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // c - Navigation
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void c01_getByName() {
+               var n = MarshalledNode.of(JsonMap.of("a", "A", "b", 
JsonMap.of("c", "C")));
+               assertEquals("A", n.get("a").asString());
+               assertTrue(n.get("b").isObject());
+               assertEquals("C", n.get("b").get("c").asString());
+       }
+
+       @Test void c02_getByNameMissing() {
+               var n = MarshalledNode.of(JsonMap.of("a", "A"));
+               assertNull(n.get("missing"));
+       }
+
+       @Test void c03_getByNameWrongType() {
+               assertNull(MarshalledNode.of(JsonList.of(1, 2)).get("a"));
+               assertNull(MarshalledNode.of("scalar").get("a"));
+       }
+
+       @Test void c04_getByIndex() {
+               var n = MarshalledNode.of(JsonList.of("x", "y", "z"));
+               assertEquals("x", n.get(0).asString());
+               assertEquals("z", n.get(2).asString());
+       }
+
+       @Test void c05_getByIndexOutOfRange() {
+               var n = MarshalledNode.of(JsonList.of("x"));
+               assertNull(n.get(5));
+               assertNull(n.get(-1));
+       }
+
+       @Test void c06_getByIndexWrongType() {
+               assertNull(MarshalledNode.of(JsonMap.of("a", 1)).get(0));
+               assertNull(MarshalledNode.of("scalar").get(0));
+       }
+
+       @Test void c07_size() {
+               assertEquals(2, MarshalledNode.of(JsonMap.of("a", 1, "b", 
2)).size());
+               assertEquals(3, MarshalledNode.of(JsonList.of(1, 2, 3)).size());
+               assertEquals(0, MarshalledNode.of("scalar").size());
+               assertEquals(0, MarshalledNode.of(null).size());
+       }
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // d - Fluent build
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void d01_fluentBuild() {
+               var n = MarshalledNode.objectNode()
+                       .put("a", 1)
+                       .put("b", 
MarshalledNode.arrayNode().add("x").add("y").value());
+
+               // Underlying structure.
+               var m = (JsonMap)n.value();
+               assertEquals(1, m.getInt("a"));
+               assertInstanceOf(JsonList.class, m.get("b"));
+               assertEquals("x", m.getList("b").getString(0));
+               assertEquals("y", m.getList("b").getString(1));
+
+               // Serialized JSON output.
+               assertEquals("{\"a\":1,\"b\":[\"x\",\"y\"]}", Json.of(m));
+       }
+
+       @Test void d02_putRequiresObjectNode() {
+               assertThrows(IllegalStateException.class, () -> 
MarshalledNode.arrayNode().put("a", 1));
+               assertThrows(IllegalStateException.class, () -> 
MarshalledNode.of("scalar").put("a", 1));
+               assertThrows(IllegalStateException.class, () -> 
MarshalledNode.of(null).put("a", 1));
+       }
+
+       @Test void d03_addRequiresArrayNode() {
+               assertThrows(IllegalStateException.class, () -> 
MarshalledNode.objectNode().add("x"));
+               assertThrows(IllegalStateException.class, () -> 
MarshalledNode.of("scalar").add("x"));
+               assertThrows(IllegalStateException.class, () -> 
MarshalledNode.of(null).add("x"));
+       }
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // e - Live view
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void e01_putMutatesBackingMap() {
+               var map = JsonMap.of("a", 1);
+               MarshalledNode.of(map).put("b", 2);
+               assertEquals(2, map.getInt("b"));
+               assertEquals("{\"a\":1,\"b\":2}", Json.of(map));
+       }
+
+       @Test void e02_addMutatesBackingList() {
+               var list = JsonList.of("x");
+               MarshalledNode.of(list).add("y");
+               assertEquals(2, list.size());
+               assertEquals("y", list.getString(1));
+       }
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // f - Copy independence
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void f01_copyMutateCopyLeavesOriginal() {
+               var original = JsonMap.of("a", 1, "nested", JsonList.of(10, 
20));
+               var node = MarshalledNode.of(original);
+               var copy = node.copy();
+
+               ((JsonMap)copy.value()).put("a", 99);
+               copy.get("nested").add(30);
+
+               // Original is unchanged.
+               assertEquals(1, original.getInt("a"));
+               assertEquals(2, original.getList("nested").size());
+       }
+
+       @Test void f02_copyMutateOriginalLeavesCopy() {
+               var original = JsonMap.of("a", 1, "nested", JsonList.of(10, 
20));
+               var copy = MarshalledNode.of(original).copy();
+
+               original.put("a", 99);
+               original.getList("nested").add(30);
+
+               // Copy is unchanged.
+               assertEquals(1, copy.get("a").asInt());
+               assertEquals(2, copy.get("nested").size());
+       }
+
+       @Test void f03_copyScalarAndNull() {
+               assertEquals("x", MarshalledNode.of("x").copy().asString());
+               assertTrue(MarshalledNode.of(null).copy().isNull());
+       }
+
+       // 
-----------------------------------------------------------------------------------------------------------------
+       // g - JSON-Pointer (RFC 6901)
+       // 
-----------------------------------------------------------------------------------------------------------------
+
+       @Test void g01_at() {
+               var n = MarshalledNode.of(JsonMap.of("a", JsonMap.of("b", 
JsonList.of("x", "y"))));
+               assertEquals("x", n.at("/a/b/0").asString());
+               assertTrue(n.at("").isObject());
+       }
+
+       @Test void g02_atMissReturnsNull() {
+               var n = MarshalledNode.of(JsonMap.of("a", 1));
+               assertNull(n.at("/missing"));
+               assertNull(n.at("/a/b"));
+       }
+
+       @Test void g03_atPresentNullReturnsNullNode() {
+               var m = new JsonMap();
+               m.put("a", null);
+               var r = MarshalledNode.of(m).at("/a");
+               assertNotNull(r);
+               assertTrue(r.isNull());
+       }
+
+       @Test void g04_find() {
+               var n = MarshalledNode.of(JsonMap.of("a", 1));
+               assertTrue(n.find("/a").isPresent());
+               assertEquals(Integer.valueOf(1), n.find("/a").get().asInt());
+               assertFalse(n.find("/missing").isPresent());
+       }
+
+       @Test void g05_findPresentNull() {
+               var m = new JsonMap();
+               m.put("a", null);
+               var opt = MarshalledNode.of(m).find("/a");
+               assertTrue(opt.isPresent());
+               assertTrue(opt.get().isNull());
+       }
+
+       @Test void g06_set() {
+               var m = new JsonMap();
+               var n = MarshalledNode.of(m);
+               assertSame(n, n.set("/a/b", 1));
+               assertEquals("{\"a\":{\"b\":1}}", Json.of(m));
+       }
+
+       @Test void g07_setAppend() {
+               var m = JsonMap.of("a", JsonList.of(1, 2));
+               MarshalledNode.of(m).set("/a/-", 3);
+               assertEquals("{\"a\":[1,2,3]}", Json.of(m));
+       }
+
+       @Test void g08_setRootThrows() {
+               assertThrows(IllegalArgumentException.class, () -> 
MarshalledNode.objectNode().set("", 1));
+       }
+
+       @Test void g09_remove() {
+               var m = JsonMap.of("a", 1, "b", 2);
+               assertEquals(Integer.valueOf(1), 
MarshalledNode.of(m).remove("/a"));
+               assertEquals("{\"b\":2}", Json.of(m));
+               assertNull(MarshalledNode.of(m).remove("/missing"));
+       }
+
+       @Test void g10_marshalledMapAt() {
+               var m = JsonMap.of("a", JsonMap.of("b", 1));
+               assertEquals(Integer.valueOf(1), m.at("/a/b").asInt());
+       }
+
+       @Test void g11_marshalledListAt() {
+               var l = JsonList.of("x", "y");
+               assertEquals("y", l.at("/1").asString());
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/objecttools/ObjectRest_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/objecttools/PathTraversal_Test.java
old mode 100755
new mode 100644
similarity index 84%
rename from 
juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/objecttools/ObjectRest_Test.java
rename to 
juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/objecttools/PathTraversal_Test.java
index 0b5c9953df..7507893dbe
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/objecttools/ObjectRest_Test.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/objecttools/PathTraversal_Test.java
@@ -25,7 +25,6 @@ import java.util.function.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.commons.bean.*;
-import org.apache.juneau.commons.reflect.*;
 import org.apache.juneau.marshall.*;
 import org.apache.juneau.marshall.collections.*;
 import org.apache.juneau.marshall.json5.*;
@@ -37,14 +36,14 @@ import org.junit.jupiter.api.*;
        "serial",  // serialVersionUID not required for test classes.
        "java:S5961"  // Test comprehensiveness requires more than 25 
assertions.
 })
-class ObjectRest_Test extends TestBase {
+class PathTraversal_Test extends TestBase {
 
        
//====================================================================================================
        // testBasic
        
//====================================================================================================
        @Test void a01_basic() {
 
-               var model = ObjectRest.create(new Json5Map()); // An empty 
model.
+               var model = PathTraversal.create(new Json5Map()); // An empty 
model.
 
                // Do a PUT
                model.put("A", new Json5Map());
@@ -70,7 +69,7 @@ class ObjectRest_Test extends TestBase {
        // testBeans
        
//====================================================================================================
        @Test void b01_beans() throws Exception {
-               var model = ObjectRest.create(new Json5Map());
+               var model = PathTraversal.create(new Json5Map());
 
                // Java beans.
                var p = new Person("some name", 123,
@@ -114,7 +113,7 @@ class ObjectRest_Test extends TestBase {
                assertEquals(expectedValue, s);
 
                // Try adding an address
-               model = ObjectRest.create(p);
+               model = PathTraversal.create(p);
                model.post("addresses", new Address("street C", "city C", 
"state C", 12345, true));
                s = ((Address)model.get("addresses/2")).toString();
                expectedValue = "Address(street=street C,city=city 
C,state=state C,zip=12345,isCurrent=true)";
@@ -168,7 +167,7 @@ class ObjectRest_Test extends TestBase {
 
                // Make sure we can get non-existent branches without throwing 
any exceptions.
                // get() method should just return null.
-               model = ObjectRest.create(new Json5Map());
+               model = PathTraversal.create(new Json5Map());
                var o = model.get("xxx");
                assertEquals("null", (""+o));
 
@@ -189,7 +188,7 @@ class ObjectRest_Test extends TestBase {
                assertEquals("{x:2}", s);
 
                // Make sure doing a POST against "" or "/" adds to the root 
object.
-               model = ObjectRest.create(new Json5List());
+               model = PathTraversal.create(new Json5List());
                model.post("", Integer.valueOf(1));
                model.post("/", Integer.valueOf(2));
                s = model.get("").toString();
@@ -200,7 +199,7 @@ class ObjectRest_Test extends TestBase {
        // testAddressBook
        
//====================================================================================================
        @Test void b02_addressBook() {
-               var model = ObjectRest.create(new AddressBook());
+               var model = PathTraversal.create(new AddressBook());
 
                // Try adding a person to the address book.
                var billClinton = new Person("Bill Clinton", 65,
@@ -270,10 +269,10 @@ class ObjectRest_Test extends TestBase {
        }
 
        
//====================================================================================================
-       // PojoRest(Object,ReaderParser)
+       // PathTraversal(Object,ReaderParser)
        
//====================================================================================================
        @Test void c01_constructors() {
-               var model = ObjectRest.create(new AddressBook(), 
Json5Parser.DEFAULT);
+               var model = PathTraversal.create(new AddressBook(), 
Json5Parser.DEFAULT);
 
                // Try adding a person to the address book.
                var billClinton = new Person("Bill Clinton", 65,
@@ -290,17 +289,17 @@ class ObjectRest_Test extends TestBase {
        // setRootLocked()
        
//====================================================================================================
        @Test void d01_rootLocked() {
-               var model = ObjectRest.create(new 
AddressBook()).setRootLocked();
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
overwrite root object", ()->model.put("", new AddressBook()));
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
overwrite root object", ()->model.put(null, new AddressBook()));
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
overwrite root object", ()->model.put("/", new AddressBook()));
+               var model = PathTraversal.create(new 
AddressBook()).setRootLocked();
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
overwrite root object", ()->model.put("", new AddressBook()));
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
overwrite root object", ()->model.put(null, new AddressBook()));
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
overwrite root object", ()->model.put("/", new AddressBook()));
        }
 
        
//====================================================================================================
        // getRootObject()
        
//====================================================================================================
        @Test void e01_getRootObject() {
-               var model = ObjectRest.create(new AddressBook());
+               var model = PathTraversal.create(new AddressBook());
                assertTrue(model.getRootObject() instanceof AddressBook);
                model.put("", "foobar");
                assertTrue(model.getRootObject() instanceof String);
@@ -329,13 +328,13 @@ class ObjectRest_Test extends TestBase {
        // getJsonList(String url, JsonList defVal)
        
//====================================================================================================
        @Test void f01_getMethods() throws Exception {
-               var model = ObjectRest.create(new A());
+               var model = PathTraversal.create(new A());
                var l = Json5List.ofString("[{a:'b'}]");
                var m = Json5Map.ofString("{a:'b'}");
                var jm = new JsonMap(m);
                var jl = new JsonList(l);
 
-               assertMapped(model, ObjectRest::get,
+               assertMapped(model, PathTraversal::get,
                        "f1,f2,f3,f4,f2a,f3a,f4a,f5,f6,f7,f8",
                        
"<null>,0,0,false,<null>,<null>,<null>,<null>,<null>,<null>,<null>");
 
@@ -343,7 +342,7 @@ class ObjectRest_Test extends TestBase {
                        "f1,f2,f3,f4,f2a,f3a,f4a,f5,f6,f7,f8",
                        "foo,0,0,false,foo,foo,foo,foo,foo,foo,foo");
 
-               assertMapped(model, ObjectRest::getString,
+               assertMapped(model, PathTraversal::getString,
                        "f1,f2,f3,f4,f2a,f3a,f4a,f5,f6,f7,f8",
                        
"<null>,0,0,false,<null>,<null>,<null>,<null>,<null>,<null>,<null>");
 
@@ -351,7 +350,7 @@ class ObjectRest_Test extends TestBase {
                        "f1,f2,f3,f4,f2a,f3a,f4a,f5,f6,f7,f8",
                        "foo,0,0,false,foo,foo,foo,foo,foo,foo,foo");
 
-               assertMapped(model, ObjectRest::getInt,
+               assertMapped(model, PathTraversal::getInt,
                        "f1,f2,f3,f4,f2a,f3a,f4a,f5,f6,f7,f8",
                        
"<null>,0,0,0,<null>,<null>,<null>,<null>,<null>,<null>,<null>");
 
@@ -359,7 +358,7 @@ class ObjectRest_Test extends TestBase {
                        "f1,f2,f3,f4,f2a,f3a,f4a,f5,f6,f7,f8",
                        "1,0,0,0,1,1,1,1,1,1,1");
 
-               assertMapped(model, ObjectRest::getLong,
+               assertMapped(model, PathTraversal::getLong,
                        "f1,f2,f3,f4,f2a,f3a,f4a,f5,f6,f7,f8",
                        
"<null>,0,0,0,<null>,<null>,<null>,<null>,<null>,<null>,<null>");
 
@@ -367,7 +366,7 @@ class ObjectRest_Test extends TestBase {
                        "f1,f2,f3,f4,f2a,f3a,f4a,f5,f6,f7,f8",
                        "1,0,0,0,1,1,1,1,1,1,1");
 
-               assertMapped(model, ObjectRest::getBoolean,
+               assertMapped(model, PathTraversal::getBoolean,
                        "f1,f2,f3,f4,f2a,f3a,f4a,f5,f6,f7,f8",
                        
"<null>,false,false,false,<null>,<null>,<null>,<null>,<null>,<null>,<null>");
 
@@ -375,7 +374,7 @@ class ObjectRest_Test extends TestBase {
                        "f1,f2,f3,f4,f2a,f3a,f4a,f5,f6,f7,f8",
                        
"true,false,false,false,true,true,true,true,true,true,true");
 
-               BiFunction<ObjectRest,String,Object> f1 = (r,p) -> {
+               BiFunction<PathTraversal,String,Object> f1 = (r,p) -> {
                        try {
                                return r.getMap(p);
                        } catch (Exception e) {
@@ -399,7 +398,7 @@ class ObjectRest_Test extends TestBase {
                assertEquals("{a:'b'}", model.getMap("f7", m).toString());
                assertEquals("{a:'b'}", model.getMap("f8", m).toString());
 
-               BiFunction<ObjectRest,String,Object> f2 = (r,p) -> {
+               BiFunction<PathTraversal,String,Object> f2 = (r,p) -> {
                        try {
                                return r.getMap(p, m);
                        } catch (Exception e) {
@@ -411,7 +410,7 @@ class ObjectRest_Test extends TestBase {
                        "f1,f2,f2a,f3,f3a,f4,f4a,f5,f6,f7,f8",
                        
"{a=b},InvalidDataConversionException,{a=b},InvalidDataConversionException,{a=b},InvalidDataConversionException,{a=b},{a=b},{a=b},{a=b},{a=b}");
 
-               BiFunction<ObjectRest,String,Object> f3 = (r,p) -> {
+               BiFunction<PathTraversal,String,Object> f3 = (r,p) -> {
                        try {
                                return r.getJsonMap(p);
                        } catch (Exception e) {
@@ -423,7 +422,7 @@ class ObjectRest_Test extends TestBase {
                        "f1,f2,f3,f4,f2a,f3a,f4a,f5,f6,f7,f8",
                        
"<null>,InvalidDataConversionException,InvalidDataConversionException,InvalidDataConversionException,<null>,<null>,<null>,<null>,<null>,<null>,<null>");
 
-               BiFunction<ObjectRest,String,Object> f4 = (r,p) -> {
+               BiFunction<PathTraversal,String,Object> f4 = (r,p) -> {
                        try {
                                return r.getJsonMap(p, jm);
                        } catch (Exception e) {
@@ -735,35 +734,11 @@ class ObjectRest_Test extends TestBase {
                }
        }
 
-       
//====================================================================================================
-       // invokeMethod(String url, String method, String args)
-       
//====================================================================================================
-       @Test void f02_invokeMethod() throws Exception {
-
-               var model = ObjectRest.create(new AddressBook().init());
-               assertEquals("Person(name=Bill Clinton,age=65)", 
model.invokeMethod("0", "toString", ""));
-
-               model = ObjectRest.create(new AddressBook().init(), 
Json5Parser.DEFAULT);
-               assertEquals("Person(name=Bill Clinton,age=65)", 
model.invokeMethod("0", "toString", ""));
-               assertEquals("NY", model.invokeMethod("0/addresses/0/state", 
"toString", ""));
-               assertNull(model.invokeMethod("1", "toString", ""));
-       }
-
-       
//====================================================================================================
-       // getPublicMethods(String url)
-       
//====================================================================================================
-       @Test void f03_getPublicMethods() {
-               var model = ObjectRest.create(new AddressBook().init());
-               
assertTrue(Json5Serializer.DEFAULT.toString(model.getPublicMethods("0")).contains("'toString'"));
-               
assertTrue(Json5Serializer.DEFAULT.toString(model.getPublicMethods("0/addresses/0/state")).contains("'toString'"));
-               assertNull(model.getPublicMethods("1"));
-       }
-
        
//====================================================================================================
        // getClassMeta(String url)
        
//====================================================================================================
        @Test void f04_getClassMeta() {
-               var model = ObjectRest.create(new AddressBook().init());
+               var model = PathTraversal.create(new AddressBook().init());
                assertEquals("Person", 
model.getClassMeta("0").inner().getSimpleName());
                assertEquals("String", 
model.getClassMeta("0/addresses/0/state").inner().getSimpleName());
                assertNull(model.getClassMeta("1"));
@@ -774,7 +749,7 @@ class ObjectRest_Test extends TestBase {
        // g - get(url, Type, Type[]) and getWithDefault(url, def, Type, 
Type[]) - parameterized typed getters
        
//====================================================================================================
        @Test void g01_getParameterized_listOfStrings() {
-               var model = 
ObjectRest.create(Json5Map.ofString("{a:['x','y','z']}"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{a:['x','y','z']}"));
                List<String> v = model.get("a", LinkedList.class, String.class);
                assertEquals(3, v.size());
                assertEquals("x", v.get(0));
@@ -782,20 +757,20 @@ class ObjectRest_Test extends TestBase {
        }
 
        @Test void g02_getParameterized_mapOfStrings() {
-               var model = 
ObjectRest.create(Json5Map.ofString("{a:{x:'1',y:'2'}}"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{a:{x:'1',y:'2'}}"));
                Map<String,Integer> v = model.get("a", TreeMap.class, 
String.class, Integer.class);
                assertEquals(2, v.size());
                assertEquals(Integer.valueOf(1), v.get("x"));
        }
 
        @Test void g03_getParameterized_nullReturnsNull() {
-               var model = ObjectRest.create(new Json5Map());
+               var model = PathTraversal.create(new Json5Map());
                List<String> v = model.get("missing", LinkedList.class, 
String.class);
                assertNull(v);
        }
 
        @Test void g04_getWithDefaultParameterized_returnsDefault() {
-               var model = ObjectRest.create(new Json5Map());
+               var model = PathTraversal.create(new Json5Map());
                var def = new LinkedList<String>();
                def.add("default");
                List<String> v = model.getWithDefault("missing", def, 
LinkedList.class, String.class);
@@ -803,7 +778,7 @@ class ObjectRest_Test extends TestBase {
        }
 
        @Test void g05_getWithDefaultParameterized_returnsConverted() {
-               var model = 
ObjectRest.create(Json5Map.ofString("{a:['x','y']}"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{a:['x','y']}"));
                var def = new LinkedList<String>();
                List<String> v = model.getWithDefault("a", def, 
LinkedList.class, String.class);
                assertEquals(2, v.size());
@@ -815,21 +790,21 @@ class ObjectRest_Test extends TestBase {
        
//====================================================================================================
        @Test void h01_put_deepPathInMap() {
                // MarshalledMap.put now returns the prior value from the 
underlying LinkedHashMap,
-               // matching the previous-value contract documented on 
ObjectRest.put.
+               // matching the previous-value contract documented on 
PathTraversal.put.
                var inner = new Json5Map();
                inner.put("c", "oldVal");
                var middle = new Json5Map();
                middle.put("b", inner);
                var outer = new Json5Map();
                outer.put("a", middle);
-               var model = ObjectRest.create(outer);
+               var model = PathTraversal.create(outer);
                var prev = model.put("a/b/c", "newVal");
                assertEquals("oldVal", prev);
                assertEquals("newVal", model.get("a/b/c"));
        }
 
        @Test void h02_put_intoList() {
-               var model = 
ObjectRest.create(Json5Map.ofString("{l:['a','b','c']}"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{l:['a','b','c']}"));
                var prev = model.put("l/1", "B");
                assertEquals("b", prev);
                assertEquals("B", model.get("l/1"));
@@ -839,20 +814,20 @@ class ObjectRest_Test extends TestBase {
                // parentUrl resolves through a null intermediate -> getNode 
returns null -> 404
                var m = new HashMap<String,Object>();
                m.put("a", null);
-               var model = ObjectRest.create(m);
-               assertThrowsWithMessage(ObjectRestException.class, "Node at URL 
'a/b' not found.", ()->model.put("a/b/c", "v"));
+               var model = PathTraversal.create(m);
+               assertThrowsWithMessage(PathTraversalException.class, "Node at 
URL 'a/b' not found.", ()->model.put("a/b/c", "v"));
        }
 
        @Test void h03_put_parentMissing_404() {
                // When a parent URL resolves to a missing key in a Map, the 
resolved JsonNode has o==null and cm
                // defaults to Object; service() detects this and returns 404.
-               var model = ObjectRest.create(new Json5Map());
-               assertThrowsWithMessage(ObjectRestException.class, "Node at URL 
'missing' not found.", ()->model.put("missing/x", "v"));
+               var model = PathTraversal.create(new Json5Map());
+               assertThrowsWithMessage(PathTraversalException.class, "Node at 
URL 'missing' not found.", ()->model.put("missing/x", "v"));
        }
 
        @Test void h04_put_intoBean() {
                // BeanMap.put may not return prior value depending on impl; we 
verify the put took effect.
-               var model = ObjectRest.create(new AddressBook().init());
+               var model = PathTraversal.create(new AddressBook().init());
                model.put("0/name", "Hillary Clinton");
                assertEquals("Hillary Clinton", model.get("0/name"));
        }
@@ -862,7 +837,7 @@ class ObjectRest_Test extends TestBase {
                var arr = new String[]{"a","b","c"};
                var map = new Json5Map();
                map.put("arr", arr);
-               var model = ObjectRest.create(map);
+               var model = PathTraversal.create(map);
                var result = model.put("arr/1", "B");
                assertEquals("arr/1", result);
                assertEquals("B", ((String[])map.get("arr"))[1]);
@@ -870,7 +845,7 @@ class ObjectRest_Test extends TestBase {
 
        @Test void h06_put_intoArray_parentBean() {
                var p = new Person("a", 1, new Address("s","c","st",1,true), 
new Address("s2","c2","st2",2,false));
-               var model = ObjectRest.create(p);
+               var model = PathTraversal.create(p);
                var newAddr = new Address("xs","xc","xst",99,true);
                var result = model.put("addresses/0", newAddr);
                assertEquals("addresses/0", result);
@@ -880,7 +855,7 @@ class ObjectRest_Test extends TestBase {
        @Test void h07_put_intoArray_listIndexOutOfBounds_expands() {
                // setArrayEntry expansion path (a.length <= index)
                var p = new Person("a", 1, new Address("s","c","st",1,true));
-               var model = ObjectRest.create(p);
+               var model = PathTraversal.create(p);
                var newAddr = new Address("xs","xc","xst",99,true);
                var result = model.put("addresses/3", newAddr);
                assertEquals("addresses/3", result);
@@ -890,15 +865,15 @@ class ObjectRest_Test extends TestBase {
 
        @Test void h08_put_nonIntegerArrayIndexThrows() {
                var p = new Person("a", 1, new Address("s","c","st",1,true));
-               var model = ObjectRest.create(p);
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
address an item in an array with a non-integer key", 
()->model.put("addresses/foo", new Address()));
+               var model = PathTraversal.create(p);
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
address an item in an array with a non-integer key", 
()->model.put("addresses/foo", new Address()));
        }
 
        @Test void h09_put_intoLeafTypeThrows() {
                // PUT into a node whose parent is a bean property of type 
"Object" -- when leaf is a String, can't traverse further
-               var model = 
ObjectRest.create(Json5Map.ofString("{name:'foo'}"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{name:'foo'}"));
                // "name" resolves to a String; trying to PUT name/x means the 
parent ("name") is leaf of unsupported type
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
perform PUT on", ()->model.put("name/x", "v"));
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
perform PUT on", ()->model.put("name/x", "v"));
        }
 
        @Test void h11_put_setNotListThrows() {
@@ -907,14 +882,14 @@ class ObjectRest_Test extends TestBase {
                s.add("a");
                var m = new HashMap<String,Object>();
                m.put("s", s);
-               var model = ObjectRest.create(m);
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot PUT 
to indexed position in non-List collection 's/0'", ()->model.put("s/0", "x"));
+               var model = PathTraversal.create(m);
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
PUT to indexed position in non-List collection 's/0'", ()->model.put("s/0", 
"x"));
        }
 
        @Test void h10_put_mapWithBeanValueType_convertsMap() {
                // Cover convert() bean+Map path: put a HashMap into a typed 
map slot whose value type is Address
                var p = new Person("a", 1, new Address("s","c","st",1,true));
-               var model = ObjectRest.create(p);
+               var model = PathTraversal.create(p);
                var asMap = new HashMap<String,Object>();
                asMap.put("street","S2"); asMap.put("city","C2"); 
asMap.put("state","ST2"); asMap.put("zip",2); asMap.put("isCurrent",false);
                model.put("addresses/0", asMap);
@@ -925,19 +900,19 @@ class ObjectRest_Test extends TestBase {
        // i - POST operations
        
//====================================================================================================
        @Test void i01_post_toCollection_returnsIndexedUrl() {
-               var model = ObjectRest.create(Json5Map.ofString("{l:['a']}"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{l:['a']}"));
                var ret = model.post("l", "b");
                assertEquals("l/1", ret);
        }
 
        @Test void i02_post_toRoot_listAddsAndReturnsUrl() {
-               var model = ObjectRest.create(new Json5List());
+               var model = PathTraversal.create(new Json5List());
                var ret = model.post("", "x");
                assertEquals("/0", ret);
        }
 
        @Test void i03_post_toRoot_array() {
-               var model = ObjectRest.create(new String[]{"a","b"});
+               var model = PathTraversal.create(new String[]{"a","b"});
                var ret = model.post("", "c");
                assertEquals("/2", ret);
                var arr = (String[])model.getRootObject();
@@ -946,20 +921,20 @@ class ObjectRest_Test extends TestBase {
        }
 
        @Test void i04_post_toRoot_invalidTypeThrows() {
-               var model = ObjectRest.create(new Json5Map());
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
perform POST on", ()->model.post("", "v"));
+               var model = PathTraversal.create(new Json5Map());
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
perform POST on", ()->model.post("", "v"));
        }
 
        @Test void i05_post_missingNode_404() {
                // Same scenario as h03 — a missing key resolves to an 
Object-typed node; service() detects this and returns 404.
-               var model = ObjectRest.create(new Json5Map());
-               assertThrowsWithMessage(ObjectRestException.class, "Node at URL 
'missing' not found.", ()->model.post("missing", "v"));
+               var model = PathTraversal.create(new Json5Map());
+               assertThrowsWithMessage(PathTraversalException.class, "Node at 
URL 'missing' not found.", ()->model.post("missing", "v"));
        }
 
        @Test void i06_post_toArray_parentMap() {
                var map = new Json5Map();
                map.put("arr", new String[]{"a","b"});
-               var model = ObjectRest.create(map);
+               var model = PathTraversal.create(map);
                var ret = model.post("arr", "c");
                assertEquals("arr/2", ret);
                assertEquals(3, ((String[])map.get("arr")).length);
@@ -967,7 +942,7 @@ class ObjectRest_Test extends TestBase {
 
        @Test void i07_post_toArray_parentBean() {
                var p = new Person("a", 1, new Address("s","c","st",1,true));
-               var model = ObjectRest.create(p);
+               var model = PathTraversal.create(p);
                var newAddr = new Address("xs","xc","xst",99,true);
                var ret = model.post("addresses", newAddr);
                assertEquals("addresses/1", ret);
@@ -978,7 +953,7 @@ class ObjectRest_Test extends TestBase {
                // POST to root Set: c.add returns true, but `c instanceof 
List` false -> service returns null.
                var s = new LinkedHashSet<String>();
                s.add("a");
-               var model = ObjectRest.create(s);
+               var model = PathTraversal.create(s);
                var ret = model.post("", "b");
                assertNull(ret);
                assertTrue(s.contains("b"));
@@ -990,29 +965,29 @@ class ObjectRest_Test extends TestBase {
                s.add("a");
                var m = new HashMap<String,Object>();
                m.put("s", s);
-               var model = ObjectRest.create(m);
+               var model = PathTraversal.create(m);
                var ret = model.post("s", "b");
                assertNull(ret);
                assertTrue(s.contains("b"));
        }
 
        @Test void i08_post_toLeafTypeThrows() {
-               var model = 
ObjectRest.create(Json5Map.ofString("{name:'foo'}"));
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
perform POST on", ()->model.post("name", "v"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{name:'foo'}"));
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
perform POST on", ()->model.post("name", "v"));
        }
 
        
//====================================================================================================
        // j - DELETE operations
        
//====================================================================================================
        @Test void j01_delete_fromMap() {
-               var model = 
ObjectRest.create(Json5Map.ofString("{a:'x',b:'y'}"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{a:'x',b:'y'}"));
                var prev = model.delete("a");
                assertEquals("x", prev);
                assertNull(model.get("a"));
        }
 
        @Test void j02_delete_fromList() {
-               var model = 
ObjectRest.create(Json5Map.ofString("{l:['a','b','c']}"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{l:['a','b','c']}"));
                var prev = model.delete("l/1");
                assertEquals("b", prev);
                var l = (List<?>)model.get("l");
@@ -1024,7 +999,7 @@ class ObjectRest_Test extends TestBase {
        @Test void j03_delete_fromArray_parentMap() {
                var map = new Json5Map();
                map.put("arr", new String[]{"a","b","c"});
-               var model = ObjectRest.create(map);
+               var model = PathTraversal.create(map);
                var prev = model.delete("arr/1");
                assertEquals("b", prev);
                assertEquals(2, ((String[])map.get("arr")).length);
@@ -1032,14 +1007,14 @@ class ObjectRest_Test extends TestBase {
 
        @Test void j04_delete_fromArray_parentBean() {
                var p = new Person("a", 1, new Address("s","c","st",1,true), 
new Address("s2","c2","st2",2,false));
-               var model = ObjectRest.create(p);
+               var model = PathTraversal.create(p);
                var prev = (Address)model.delete("addresses/0");
                assertEquals("c", prev.city);
                assertEquals(1, p.addresses.length);
        }
 
        @Test void j05_delete_fromBean_setsNull() {
-               var model = ObjectRest.create(new AddressBook().init());
+               var model = PathTraversal.create(new AddressBook().init());
                // DELETE on bean property invokes BeanMap.put(key, null)
                model.delete("0/name");
                var p = (Person)model.get("0");
@@ -1047,21 +1022,21 @@ class ObjectRest_Test extends TestBase {
        }
 
        @Test void j06_delete_root() {
-               var model = ObjectRest.create(Json5Map.ofString("{a:'x'}"));
+               var model = PathTraversal.create(Json5Map.ofString("{a:'x'}"));
                var prev = model.delete("");
                assertEquals("{a:'x'}", prev.toString());
                assertNull(model.getRootObject());
        }
 
        @Test void j07_delete_root_locked() {
-               var model = ObjectRest.create(new Json5Map()).setRootLocked();
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
overwrite root object", ()->model.delete(""));
+               var model = PathTraversal.create(new 
Json5Map()).setRootLocked();
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
overwrite root object", ()->model.delete(""));
        }
 
        @Test void j08_delete_nonIntegerArrayIndex() {
                var p = new Person("a", 1, new Address("s","c","st",1,true));
-               var model = ObjectRest.create(p);
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
address an item in an array with a non-integer key", 
()->model.delete("addresses/foo"));
+               var model = PathTraversal.create(p);
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
address an item in an array with a non-integer key", 
()->model.delete("addresses/foo"));
        }
 
        @Test void j09a_delete_setNotListThrows() {
@@ -1070,14 +1045,14 @@ class ObjectRest_Test extends TestBase {
                s.add("a"); s.add("b");
                var m = new HashMap<String,Object>();
                m.put("s", s);
-               var model = ObjectRest.create(m);
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
DELETE indexed position in non-List collection 's/0'", ()->model.delete("s/0"));
+               var model = PathTraversal.create(m);
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
DELETE indexed position in non-List collection 's/0'", ()->model.delete("s/0"));
        }
 
        @Test void j09_delete_unsupportedLeafTypeThrows() {
                // DELETE on a leaf node whose parent type isn't 
map/list/array/bean
-               var model = 
ObjectRest.create(Json5Map.ofString("{name:'foo'}"));
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
perform PUT on", ()->model.delete("name/x"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{name:'foo'}"));
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
perform PUT on", ()->model.delete("name/x"));
        }
 
        
//====================================================================================================
@@ -1085,38 +1060,38 @@ class ObjectRest_Test extends TestBase {
        
//====================================================================================================
        @Test void k01_get_nullValueInMap() {
                // get path that traverses through a null value in a map
-               var model = ObjectRest.create(Json5Map.ofString("{a:null}"));
+               var model = PathTraversal.create(Json5Map.ofString("{a:null}"));
                assertNull(model.get("a/b"));
        }
 
        @Test void k02_get_listIndexOutOfBounds_returnsNull() {
-               var model = 
ObjectRest.create(Json5Map.ofString("{l:['a','b']}"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{l:['a','b']}"));
                assertNull(model.get("l/5"));
        }
 
        @Test void k03_get_arrayIndexOutOfBounds_returnsNull() {
-               var model = ObjectRest.create(new String[]{"a","b"});
+               var model = PathTraversal.create(new String[]{"a","b"});
                assertNull(model.get("5"));
        }
 
        @Test void k04_get_arrayElement() {
-               var model = ObjectRest.create(new String[]{"a","b","c"});
+               var model = PathTraversal.create(new String[]{"a","b","c"});
                assertEquals("b", model.get("1"));
        }
 
        @Test void k05_get_listNonIntegerKeyThrows() {
-               var model = 
ObjectRest.create(Json5Map.ofString("{l:['a','b']}"));
-               assertThrowsWithMessage(ObjectRestException.class, "Cannot 
address an item in an array with a non-integer key", ()->model.get("l/foo"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{l:['a','b']}"));
+               assertThrowsWithMessage(PathTraversalException.class, "Cannot 
address an item in an array with a non-integer key", ()->model.get("l/foo"));
        }
 
        @Test void k06_get_beanUnknownPropertyThrows() {
-               var model = ObjectRest.create(new AddressBook().init());
-               assertThrowsWithMessage(ObjectRestException.class, "Unknown 
property", ()->model.get("0/notARealField"));
+               var model = PathTraversal.create(new AddressBook().init());
+               assertThrowsWithMessage(PathTraversalException.class, "Unknown 
property", ()->model.get("0/notARealField"));
        }
 
        @Test void k07_get_blankAndSlashReturnRoot() {
                var m = new Json5Map();
-               var model = ObjectRest.create(m);
+               var model = PathTraversal.create(m);
                assertSame(m, model.get(""));
                assertSame(m, model.get("/"));
                assertSame(m, model.get(null));
@@ -1124,7 +1099,7 @@ class ObjectRest_Test extends TestBase {
 
        @Test void k08_get_deepPathThroughMixedTypes() {
                var p = new Person("a", 1, new 
Address("s","city1","st",1,true), new Address("s","city2","st",2,false));
-               var model = ObjectRest.create(p);
+               var model = PathTraversal.create(p);
                assertEquals("city2", model.get("addresses/1/city"));
        }
 
@@ -1135,7 +1110,7 @@ class ObjectRest_Test extends TestBase {
                s.add("x"); s.add("y");
                var m = new HashMap<String,Object>();
                m.put("s", s);
-               var model = ObjectRest.create(m);
+               var model = PathTraversal.create(m);
                // Indexing into a Set traverses through the (cm.isCollection() 
&& !instanceof List) branch
                // and returns a JsonNode with o=null cm=Object since ct2 stays 
null. The result is null.
                assertNull(model.get("s/0"));
@@ -1145,25 +1120,16 @@ class ObjectRest_Test extends TestBase {
                // Bean property whose value is null - traversal returns null
                var p = new Person();
                p.name = null;
-               var model = ObjectRest.create(p);
+               var model = PathTraversal.create(p);
                assertNull(model.get("name/sub"));
        }
 
-       
//====================================================================================================
-       // l - invokeMethod exception path
-       
//====================================================================================================
-       @Test void l01_invokeMethod_invalidMethodThrows() {
-               var model = ObjectRest.create(new AddressBook().init());
-               // Invoking a method that doesn't exist should yield 
ExecutableException
-               assertThrows(ExecutableException.class, 
()->model.invokeMethod("0", "noSuchMethodOnPerson", ""));
-       }
-
        
//====================================================================================================
        // m - JsonNode constructor branch coverage (cm == null path, cm == 
object path)
        
//====================================================================================================
        @Test void m01_jsonNode_nullValueGetsObjectMeta() {
-               // Construct ObjectRest over null - JsonNode constructor takes 
the o==null branch in cm assignment
-               var model = ObjectRest.create(null);
+               // Construct PathTraversal over null - JsonNode constructor 
takes the o==null branch in cm assignment
+               var model = PathTraversal.create(null);
                assertNull(model.getRootObject());
                // PUT "" replaces root even when starting from null
                model.put("", "x");
@@ -1171,7 +1137,7 @@ class ObjectRest_Test extends TestBase {
        }
 
        @Test void m02_create_withParserUsesParserSession() {
-               var model = ObjectRest.create(new Json5Map(), 
Json5Parser.DEFAULT);
+               var model = PathTraversal.create(new Json5Map(), 
Json5Parser.DEFAULT);
                model.put("a", "v");
                assertEquals("v", model.get("a"));
        }
@@ -1180,18 +1146,18 @@ class ObjectRest_Test extends TestBase {
        // n - URI/path syntax: leading slashes, empty segments
        
//====================================================================================================
        @Test void n01_path_leadingSlashIgnored() {
-               var model = ObjectRest.create(Json5Map.ofString("{a:'v'}"));
+               var model = PathTraversal.create(Json5Map.ofString("{a:'v'}"));
                assertEquals("v", model.get("a"));
                assertEquals("v", model.get("/a"));
        }
 
        @Test void n02_path_listIndexNavigation() {
-               var model = 
ObjectRest.create(Json5Map.ofString("{l:[{x:1},{x:2},{x:3}]}"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{l:[{x:1},{x:2},{x:3}]}"));
                assertEquals(2, model.getInt("l/1/x"));
        }
 
        @Test void n03_post_emptyRootIsOk() {
-               var model = ObjectRest.create(new Json5List());
+               var model = PathTraversal.create(new Json5List());
                model.post(null, "x");
                model.post("/", "y");
                var l = (List<?>)model.getRootObject();
@@ -1204,7 +1170,7 @@ class ObjectRest_Test extends TestBase {
        // o - Patch-like behavior: mix of put/delete on the same model
        
//====================================================================================================
        @Test void o01_patch_likeWorkflow() {
-               var model = 
ObjectRest.create(Json5Map.ofString("{user:{name:'Alice',roles:['admin','editor']}}"));
+               var model = 
PathTraversal.create(Json5Map.ofString("{user:{name:'Alice',roles:['admin','editor']}}"));
                model.put("user/name", "Bob");
                model.delete("user/roles/0");
                model.post("user/roles", "viewer");
@@ -1214,4 +1180,4 @@ class ObjectRest_Test extends TestBase {
                assertEquals("editor", roles.get(0));
                assertEquals("viewer", roles.get(1));
        }
-}
\ No newline at end of file
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/BasicRuntimeException_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/BasicRuntimeException_Test.java
index efa435f7f0..ff17beef1f 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/BasicRuntimeException_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/BasicRuntimeException_Test.java
@@ -92,8 +92,8 @@ class BasicRuntimeException_Test extends TestBase {
                assertEquals("Message arg1", x.getMessage());
        }
 
-       @Test void a08_ObjectRestException_fluentSetters() {
-               var x = new ObjectRestException(404, "Original message");
+       @Test void a08_PathTraversalException_fluentSetters() {
+               var x = new PathTraversalException(404, "Original message");
 
                // Test setMessage returns same instance for fluent chaining
                assertSame(x, x.setMessage("New message"));
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/classic/RestClient_Response_Body_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/classic/RestClient_Response_Body_Test.java
index 2d88cc29a1..475bb3bf9a 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/classic/RestClient_Response_Body_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/classic/RestClient_Response_Body_Test.java
@@ -36,6 +36,7 @@ import org.apache.http.entity.*;
 import org.apache.http.message.*;
 import org.apache.juneau.*;
 import org.apache.juneau.marshall.*;
+import org.apache.juneau.marshall.collections.*;
 import org.apache.juneau.marshall.json5.*;
 import org.apache.juneau.marshall.parser.*;
 import org.apache.juneau.marshall.xml.*;
@@ -247,11 +248,11 @@ class RestClient_Response_Body_Test extends TestBase {
                var x18 = 
testClient().entity(stringEntity("12345")).get().run().getContent().asAbbreviatedString(4);
                assertEquals("1...", x18);
 
-               var x20 = 
testClient().entity(stringEntity("{f:1}")).get().run().getContent().asObjectRest(ABean.class);
-               assertString("1", x20.get("f"));
+               var x20 = 
testClient().entity(stringEntity("{f:1}")).get().run().getContent().as(JsonMap.class).getAt("f",
 String.class);
+               assertString("1", x20);
 
-               var x22 = 
testClient().entity(stringEntity("{f:1}")).get().run().getContent().asObjectRest();
-               assertString("1", x22.get("f"));
+               var x22 = 
testClient().entity(stringEntity("{f:1}")).get().run().getContent().as(JsonMap.class).getAt("f",
 String.class);
+               assertString("1", x22);
 
                var x24 = 
testClient().entity(stringEntity("foo=123")).get().run().getContent().asMatcher(Pattern.compile("foo=(.*)"));
                assertTrue(x24.matches());
diff --git 
a/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/ResponseContent.java
 
b/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/ResponseContent.java
index b55caa28b2..43a79740ef 100644
--- 
a/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/ResponseContent.java
+++ 
b/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/ResponseContent.java
@@ -39,7 +39,6 @@ import org.apache.juneau.marshall.*;
 import org.apache.juneau.marshall.collections.*;
 import org.apache.juneau.marshall.httppart.*;
 import org.apache.juneau.marshall.oapi.*;
-import org.apache.juneau.marshall.objecttools.*;
 import org.apache.juneau.marshall.parser.*;
 import org.apache.juneau.marshall.parser.ParseException;
 import org.apache.juneau.marshall.stream.*;
@@ -52,6 +51,11 @@ import org.apache.juneau.rest.client.classic.assertion.*;
  * An extension of an HttpClient {@link HttpEntity} that provides various 
support for converting the body to POJOs and
  * other convenience methods.
  *
+ * <p>
+ * To quickly retrieve a single value from inside a larger JSON document, 
parse the body into a {@link JsonMap} and use
+ * its path-addressing methods &mdash; e.g. 
<c>getContent().as(JsonMap.<jk>class</jk>).getAt(<jv>path</jv>, 
<jv>type</jv>)</c>
+ * or the RFC 6901 form 
<c>getContent().as(JsonMap.<jk>class</jk>).at(<jv>pointer</jv>)</c>.
+ *
  * <h5 class='section'>See Also:</h5><ul>
  *     <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/JuneauRestClientBasics";>juneau-rest-client
 Basics</a>
  * </ul>
@@ -761,41 +765,6 @@ public class ResponseContent implements HttpEntity {
                return asMatcher(Pattern.compile(regex, flags));
        }
 
-       /**
-        * Converts the output from the connection into an {@link JsonMap} and 
then wraps that in a {@link ObjectRest}.
-        *
-        * <p>
-        * Useful if you want to quickly retrieve a single value from inside of 
a larger JSON document.
-        *
-        * @return The parsed output wrapped in a {@link ObjectRest}.
-        * @throws RestCallException
-        *      <ul>
-        *              <li>If the input contains a syntax error or is 
malformed, or is not valid for the specified type.
-        *              <li>If a connection error occurred.
-        *      </ul>
-        */
-       public ObjectRest asObjectRest() throws RestCallException {
-               return asObjectRest(JsonMap.class);
-       }
-
-       /**
-        * Parses the output from the body into the specified type and then 
wraps that in a {@link ObjectRest}.
-        *
-        * <p>
-        * Useful if you want to quickly retrieve a single value from inside of 
a larger JSON document.
-        *
-        * @param innerType The class type of the POJO being wrapped.
-        * @return The parsed output wrapped in a {@link ObjectRest}.
-        * @throws RestCallException
-        *      <ul>
-        *              <li>If the input contains a syntax error or is 
malformed, or is not valid for the specified type.
-        *              <li>If a connection error occurred.
-        *      </ul>
-        */
-       public ObjectRest asObjectRest(Class<?> innerType) throws 
RestCallException {
-               return new ObjectRest(as(innerType));
-       }
-
        /**
         * Returns the HTTP response message body as a reader based on the 
charset on the <code>Content-Type</code> response header.
         *
diff --git 
a/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
 
b/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
index 710c3bc5ec..b0204d7a05 100644
--- 
a/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
+++ 
b/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
@@ -101,7 +101,6 @@ import org.apache.juneau.marshall.marshaller.*;
 import org.apache.juneau.marshall.marshaller.Uon;
 import org.apache.juneau.marshall.msgpack.*;
 import org.apache.juneau.marshall.oapi.*;
-import org.apache.juneau.marshall.objecttools.*;
 import org.apache.juneau.marshall.parquet.*;
 import org.apache.juneau.marshall.parser.*;
 import org.apache.juneau.marshall.parser.ParseException;
@@ -701,8 +700,6 @@ import org.apache.juneau.rest.client.classic.remote.*;
  *             <li class='jm'><c>{@link ResponseContent#asString() asString()} 
<jk>returns</jk> String</c>
  *             <li class='jm'><c>{@link ResponseContent#asStringFuture() 
asStringFuture()} <jk>returns</jk> Future&lt;String&gt;</c>
  *             <li class='jm'><c>{@link 
ResponseContent#asAbbreviatedString(int) asAbbreviatedString(int)} 
<jk>returns</jk> String</c>
- *             <li class='jm'><c>{@link ResponseContent#asObjectRest(Class) 
asObjectRest(Class&lt;?&gt;)} <jk>returns</jk> {@link ObjectRest}</c>
- *             <li class='jm'><c>{@link ResponseContent#asObjectRest() 
asObjectRest()} <jk>returns</jk> {@link ObjectRest}</c>
  *             <li class='jm'><c>{@link ResponseContent#asMatcher(Pattern) 
asMatcher(Pattern)} <jk>returns</jk> {@link Matcher}</c>
  *             <li class='jm'><c>{@link ResponseContent#asMatcher(String) 
asMatcher(String)} <jk>returns</jk> {@link Matcher}</c>
  *     </ul>
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/converter/Traversable.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/converter/Traversable.java
index d9a899b80e..bc15390ce4 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/converter/Traversable.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/converter/Traversable.java
@@ -25,7 +25,7 @@ import org.apache.juneau.marshall.parser.*;
 import org.apache.juneau.rest.server.*;
 
 /**
- * Converter for enabling of {@link ObjectRest} support on response objects 
returned by a <c>@RestOp</c>-annotated method.
+ * Converter for enabling path-addressing support on response objects returned 
by a <c>@RestOp</c>-annotated method.
  *
  * <p>
  * When enabled, objects in a POJO tree returned by the REST method can be 
addressed through additional URL path
@@ -40,7 +40,6 @@ import org.apache.juneau.rest.server.*;
  * </p>
  *
  * <h5 class='section'>See Also:</h5><ul>
- *     <li class='jc'>{@link ObjectRest} - Additional information on 
addressing elements in a POJO tree using URL notation.
  *     <li class='ja'>{@link 
org.apache.juneau.rest.server.RestOp#converters()} - Registering converters 
with REST resources.
  *     <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/Converters";>Converters</a>
  * </ul>
@@ -61,9 +60,9 @@ public class Traversable implements RestConverter {
                                if (nn(swap))
                                        o = swap.swap(bs, o);
                                ReaderParser rp = 
req.getContent().getParserMatch().map(ParserMatch::getParser).filter(ReaderParser.class::isInstance).map(ReaderParser.class::cast).orElse(null);
-                               var or = ObjectRest.create(o, rp);
+                               var or = PathTraversal.create(o, rp);
                                o = or.get(pathRemainder);
-                       } catch (ObjectRestException e) {
+                       } catch (PathTraversalException e) {
                                throw new BasicHttpException(e.getStatus(), 
null, e);
                        } catch (Exception t) {
                                throw new InternalServerError(t);
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/OpenApiVar.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/OpenApiVar.java
index 39e6d3cfa6..f5ec2e1b99 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/OpenApiVar.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/OpenApiVar.java
@@ -59,7 +59,7 @@ public class OpenApiVar extends MultipartResolvingVar {
        @Override /* Overridden from Var */
        public String resolve(VarResolverSession session, String key) {
                RestRequest req = 
session.getBean(RestRequest.class).orElseThrow(InternalServerError::new);
-               return new ObjectRest(req.getOpenApi()).getString(key);
+               return new PathTraversal(req.getOpenApi()).getString(key);
        }
 
        @Override /* Overridden from Var */
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/SwaggerVar.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/SwaggerVar.java
index 5a2b6452bc..14b76eca31 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/SwaggerVar.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/SwaggerVar.java
@@ -67,7 +67,7 @@ public class SwaggerVar extends MultipartResolvingVar {
        @Override /* Overridden from Var */
        public String resolve(VarResolverSession session, String key) {
                RestRequest req = 
session.getBean(RestRequest.class).orElseThrow(InternalServerError::new);
-               return new ObjectRest(req.getSwagger()).getString(key);
+               return new PathTraversal(req.getSwagger()).getString(key);
        }
 
        @Override /* Overridden from Var */

Reply via email to