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 aa8f207c57 Add support for setting serializer/parser session 
properties as HTTP headers and query parameters
aa8f207c57 is described below

commit aa8f207c5763e4fc62d0c411143c964a8ff18062
Author: James Bognar <[email protected]>
AuthorDate: Sat Apr 4 15:39:19 2026 -0400

    Add support for setting serializer/parser session properties as HTTP 
headers and query parameters
---
 .../juneau/commons/reflect/AnnotationInfo.java     |  21 +++
 .../juneau/commons/utils/CollectionUtils.java      |  80 ++++++++
 .../apache/juneau/commons/utils/StringUtils.java   |   3 +
 .../org/apache/juneau/rest/client/RestClient.java  | 133 ++++++++++++++
 .../org/apache/juneau/rest/client/RestRequest.java | 123 +++++++++++++
 .../apache/juneau/rest/RestSharedConstants.java    |  59 ++++++
 .../apache/juneau/rest/mock/MockRestClient.java    |  48 +++++
 .../apache/juneau/rest/mock/MockRestRequest.java   |  48 +++++
 .../java/org/apache/juneau/rest/RestContext.java   | 155 ++++++++++++++++
 .../java/org/apache/juneau/rest/RestOpContext.java | 104 +++++++++++
 .../java/org/apache/juneau/rest/RestRequest.java   | 202 +++++++++++++++++++++
 .../apache/juneau/rest/RestServerConstants.java    |  53 ++++++
 .../org/apache/juneau/rest/annotation/Rest.java    |  56 ++++++
 .../juneau/rest/annotation/RestAnnotation.java     |  57 ++++++
 .../apache/juneau/rest/annotation/RestDelete.java  |  50 +++++
 .../rest/annotation/RestDeleteAnnotation.java      |  57 ++++++
 .../org/apache/juneau/rest/annotation/RestGet.java |  50 +++++
 .../juneau/rest/annotation/RestGetAnnotation.java  |  57 ++++++
 .../org/apache/juneau/rest/annotation/RestOp.java  |  50 +++++
 .../juneau/rest/annotation/RestOpAnnotation.java   |  57 ++++++
 .../apache/juneau/rest/annotation/RestOptions.java |  50 +++++
 .../rest/annotation/RestOptionsAnnotation.java     |  57 ++++++
 .../apache/juneau/rest/annotation/RestPatch.java   |  50 +++++
 .../rest/annotation/RestPatchAnnotation.java       |  57 ++++++
 .../apache/juneau/rest/annotation/RestPost.java    |  50 +++++
 .../juneau/rest/annotation/RestPostAnnotation.java |  57 ++++++
 .../org/apache/juneau/rest/annotation/RestPut.java |  50 +++++
 .../juneau/rest/annotation/RestPutAnnotation.java  |  57 ++++++
 .../juneau/rest/config/BasicUniversalConfig.java   |  41 +++++
 .../juneau/rest/httppart/RequestContent.java       |   2 +-
 .../rest/processor/SerializedPojoProcessor.java    |   2 +-
 .../juneau/commons/utils/CollectionUtils_Test.java |  22 +++
 .../org/apache/juneau/rest/NoInherit_Test.java     | 168 +++++++++++++++++
 .../rest/RestRequest_SessionProperties_Test.java   | 158 ++++++++++++++++
 .../rest/annotation/RestAnnotation_Test.java       |  16 +-
 .../rest/annotation/RestDeleteAnnotation_Test.java |  16 +-
 .../rest/annotation/RestGetAnnotation_Test.java    |  16 +-
 .../rest/annotation/RestOpAnnotation_Test.java     |  16 +-
 .../rest/annotation/RestPostAnnotation_Test.java   |  16 +-
 .../rest/annotation/RestPutAnnotation_Test.java    |  16 +-
 .../rest/client/RestClient_Headers_Test.java       |  10 +-
 .../juneau/rest/client/RestClient_Query_Test.java  |  10 +
 42 files changed, 2385 insertions(+), 15 deletions(-)

diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationInfo.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationInfo.java
index 59e56cd831..2da9f91b57 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationInfo.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/reflect/AnnotationInfo.java
@@ -127,6 +127,27 @@ public class AnnotationInfo<T extends Annotation> {
                this.toString = memoize(this::findToString);
        }
 
+       /**
+        * Returns the annotatable object (class, method, field, etc.) where 
this annotation was found.
+        *
+        * @return The annotatable object where this annotation was found.
+        */
+       public Annotatable getAnnotatable() {
+               return annotatable;
+       }
+
+       /**
+        * Returns the annotatable object cast to the specified type.
+        *
+        * @param <A> The annotatable type.
+        * @param type The class to cast to.
+        * @return The annotatable element cast to the specified type.
+        * @throws ClassCastException If the annotatable element is not of the 
specified type.
+        */
+       public <A extends Annotatable> A getAnnotatable(Class<A> type) {
+               return type.cast(annotatable);
+       }
+
        /**
         * Returns the annotation type of this annotation.
         *
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
index 92d86c2ee6..2de555694a 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/CollectionUtils.java
@@ -129,6 +129,8 @@ import org.apache.juneau.commons.collections.*;
 public class CollectionUtils {
 
        // Argument name constants for assertArgNotNull
+       private static final String ARG_comparator = "comparator";
+       private static final String ARG_input = "input";
        private static final String ARG_value = "value";
        private static final String ARG_array = "array";
        private static final String ARG_arrays = "arrays";
@@ -869,6 +871,26 @@ public class CollectionUtils {
                return value;
        }
 
+       /**
+        * Returns <jk>true</jk> if the specified collection is null or empty.
+        *
+        * @param c The collection to check.
+        * @return <jk>true</jk> if the specified collection is null or empty.
+        */
+       public static boolean isEmpty(Collection<?> c) {
+               return c == null || c.isEmpty();
+       }
+
+       /**
+        * Returns <jk>true</jk> if the specified map is null or empty.
+        *
+        * @param m The map to check.
+        * @return <jk>true</jk> if the specified map is null or empty.
+        */
+       public static boolean isEmpty(Map<?,?> m) {
+               return m == null || m.isEmpty();
+       }
+
        /**
         * Returns <jk>true</jk> if the specified array is null or has a length 
of zero.
         *
@@ -2015,6 +2037,64 @@ public class CollectionUtils {
                return IntStream.range(0, value.size()).mapToObj(i -> 
value.get(value.size() - 1 - i));
        }
 
+       /**
+        * Removes negation tokens from a list.
+        *
+        * <p>
+        * A negation token is a string starting with <js>'-'</js> followed by 
at least one character.
+        * When encountered, it removes the first prior occurrence of the 
corresponding positive token.
+        *
+        * <h5 class='section'>Example:</h5>
+        * <p class='bjava'>
+        *      List&lt;String&gt; <jv>result</jv> = 
<jsm>removeNegations</jsm>(list(<js>"a"</js>, <js>"b"</js>, <js>"-a"</js>, 
<js>"c"</js>));
+        *      <jc>// Produces: ["b", "c"]</jc>
+        * </p>
+        *
+        * @param input The input list. Cannot be <jk>null</jk>.
+        * @return The list with negation tokens applied, or the original list 
if no negation tokens were present.
+        */
+       public static List<String> removeNegations(List<String> input) {
+               assertArgNotNull(ARG_input, input);
+               var hasNegation = false;
+               for (var token : input) {
+                       if (token != null && token.length() > 1 && 
token.charAt(0) == '-') {
+                               hasNegation = true;
+                               break;
+                       }
+               }
+               if (!hasNegation)
+                       return input;
+               var out = new ArrayList<String>(input.size());
+               for (var token : input) {
+                       if (token != null && token.length() > 1 && 
token.charAt(0) == '-')
+                               out.remove(token.substring(1));
+                       else
+                               out.add(token);
+               }
+               return out;
+       }
+
+       /**
+        * Creates a {@link TreeSet} with a custom comparator from a collection 
of elements.
+        *
+        * <p>
+        * Null elements in the collection are silently skipped.
+        *
+        * @param <E> The element type.
+        * @param comparator The comparator to use for ordering. Cannot be 
<jk>null</jk>.
+        * @param elements The initial elements. Can be <jk>null</jk> (treated 
as empty).
+        * @return A new {@link TreeSet} containing all non-null elements from 
the collection.
+        */
+       public static <E> SortedSet<E> treeSet(Comparator<? super E> 
comparator, Collection<? extends E> elements) {
+               assertArgNotNull(ARG_comparator, comparator);
+               var s = new TreeSet<E>(comparator);
+               if (elements != null)
+                       for (var e : elements)
+                               if (e != null)
+                                       s.add(e);
+               return s;
+       }
+
        /**
         * Shortcut for creating a modifiable set out of an array of values.
         *
diff --git 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/StringUtils.java
 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/StringUtils.java
index b6620512cf..47cd29bde7 100644
--- 
a/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/StringUtils.java
+++ 
b/juneau-core/juneau-commons/src/main/java/org/apache/juneau/commons/utils/StringUtils.java
@@ -77,6 +77,9 @@ public class StringUtils {
        /** Zero-length string constant. */
        public static final String EMPTY = "";
 
+       /** Zero-length string array constant. */
+       public static final String[] EMPTY_STRING_ARRAY = new String[0];
+
        /** Characters allowed at the beginning of a numeric literal. */
        public static final AsciiSet FIRST_NUMBER_CHARS = 
AsciiSet.of("+-.#0123456789");
 
diff --git 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index e5a2a49701..4b2b2496a8 100644
--- 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -29,6 +29,7 @@ import static org.apache.juneau.http.HttpHeaders.*;
 import static org.apache.juneau.http.HttpMethod.*;
 import static org.apache.juneau.http.HttpParts.*;
 import static org.apache.juneau.httppart.HttpPartType.*;
+import static org.apache.juneau.rest.RestSharedConstants.*;
 import static org.apache.juneau.rest.client.RestOperation.*;
 
 import java.io.*;
@@ -2552,6 +2553,72 @@ public class RestClient extends BeanContextable 
implements HttpClient, Closeable
                        return this;
                }
 
+               /**
+                * Sets a default request header with serializer session 
options (JSON5 map) for all requests.
+                *
+                * <p>
+                * This is a shortcut for <c>headersDefault(basicHeader({@link 
org.apache.juneau.rest.RestSharedConstants#HEADER_JuneauSerializerOptions}, 
<jv>json5</jv>))</c>.
+                *
+                * @param json5
+                *      Serializer session options in JSON5 form.
+                *      <br>Can be <jk>null</jk> (default is not set by this 
method).
+                * @return This object.
+                */
+               public Builder serializerSessionOptionsHeader(String json5) {
+                       if (json5 != null)
+                               
headersDefault(basicHeader(HEADER_JuneauSerializerOptions, json5));
+                       return this;
+               }
+
+               /**
+                * Sets a default request header with parser session options 
(JSON5 map) for all requests.
+                *
+                * <p>
+                * This is a shortcut for <c>headersDefault(basicHeader({@link 
org.apache.juneau.rest.RestSharedConstants#HEADER_JuneauParserOptions}, 
<jv>json5</jv>))</c>.
+                *
+                * @param json5
+                *      Parser session options in JSON5 form.
+                *      <br>Can be <jk>null</jk> (default is not set by this 
method).
+                * @return This object.
+                */
+               public Builder parserSessionOptionsHeader(String json5) {
+                       if (json5 != null)
+                               
headersDefault(basicHeader(HEADER_JuneauParserOptions, json5));
+                       return this;
+               }
+
+               /**
+                * Convenience for {@link 
#serializerSessionOptionsHeader(String)} using a map serialized with JSON5.
+                *
+                * @param properties
+                *      Property names and values for serializer session 
options.
+                *      <br>Can be <jk>null</jk> (treated as an empty map).
+                * @return This object.
+                */
+               public Builder serializerSessionOptionsHeader(Map<String,?> 
properties) {
+                       try {
+                               return 
serializerSessionOptionsHeader(isEmpty(properties) ? null : 
Json5.of(properties));
+                       } catch (SerializeException e) {
+                               throw rex(e, "Could not serialize serializer 
session options header");
+                       }
+               }
+
+               /**
+                * Convenience for {@link #parserSessionOptionsHeader(String)} 
using a map serialized with JSON5.
+                *
+                * @param properties
+                *      Property names and values for parser session options.
+                *      <br>Can be <jk>null</jk> (treated as an empty map).
+                * @return This object.
+                */
+               public Builder parserSessionOptionsHeader(Map<String,?> 
properties) {
+                       try {
+                               return 
parserSessionOptionsHeader(isEmpty(properties) ? null : Json5.of(properties));
+                       } catch (SerializeException e) {
+                               throw rex(e, "Could not serialize parser 
session options header");
+                       }
+               }
+
                /**
                 * Convenience method for specifying HTML as the marshalling 
transmission media type.
                 *
@@ -4639,6 +4706,72 @@ public class RestClient extends BeanContextable 
implements HttpClient, Closeable
                        return this;
                }
 
+               /**
+                * Sets a default query parameter with serializer session 
options (UON map) for all requests.
+                *
+                * <p>
+                * This is a shortcut for <c>queryDataDefault(stringPart({@link 
org.apache.juneau.rest.RestSharedConstants#QUERY_juneauSerializerOptions}, 
<jv>uon</jv>))</c>.
+                *
+                * @param uon
+                *      Serializer session options in UON form.
+                *      <br>Can be <jk>null</jk> (default is not set by this 
method).
+                * @return This object.
+                */
+               public Builder serializerSessionOptionsQueryDefault(String uon) 
{
+                       if (uon != null)
+                               
queryDataDefault(stringPart(QUERY_juneauSerializerOptions, uon));
+                       return this;
+               }
+
+               /**
+                * Sets a default query parameter with parser session options 
(UON map) for all requests.
+                *
+                * <p>
+                * This is a shortcut for <c>queryDataDefault(stringPart({@link 
org.apache.juneau.rest.RestSharedConstants#QUERY_juneauParserOptions}, 
<jv>uon</jv>))</c>.
+                *
+                * @param uon
+                *      Parser session options in UON form.
+                *      <br>Can be <jk>null</jk> (default is not set by this 
method).
+                * @return This object.
+                */
+               public Builder parserSessionOptionsQueryDefault(String uon) {
+                       if (uon != null)
+                               
queryDataDefault(stringPart(QUERY_juneauParserOptions, uon));
+                       return this;
+               }
+
+               /**
+                * Convenience for {@link 
#serializerSessionOptionsQueryDefault(String)} using {@link 
org.apache.juneau.marshaller.Uon#of(Object)}.
+                *
+                * @param properties
+                *      Property names and values for serializer session 
options.
+                *      <br>Can be <jk>null</jk> (treated as an empty map).
+                * @return This object.
+                */
+               public Builder 
serializerSessionOptionsQueryDefault(Map<String,?> properties) {
+                       try {
+                               return 
serializerSessionOptionsQueryDefault(isEmpty(properties) ? null : 
Uon.of(properties));
+                       } catch (SerializeException e) {
+                               throw rex(e, "Could not serialize serializer 
session options query default");
+                       }
+               }
+
+               /**
+                * Convenience for {@link 
#parserSessionOptionsQueryDefault(String)} using {@link 
org.apache.juneau.marshaller.Uon#of(Object)}.
+                *
+                * @param properties
+                *      Property names and values for parser session options.
+                *      <br>Can be <jk>null</jk> (treated as an empty map).
+                * @return This object.
+                */
+               public Builder parserSessionOptionsQueryDefault(Map<String,?> 
properties) {
+                       try {
+                               return 
parserSessionOptionsQueryDefault(isEmpty(properties) ? null : 
Uon.of(properties));
+                       } catch (SerializeException e) {
+                               throw rex(e, "Could not serialize parser 
session options query default");
+                       }
+               }
+
                /**
                 * <i><l>WriterSerializer</l> configuration property:&emsp;</i> 
 Quote character.
                 *
diff --git 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java
index f1d5799057..1d5bff26a2 100644
--- 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestRequest.java
@@ -18,6 +18,7 @@ package org.apache.juneau.rest.client;
 
 import static org.apache.juneau.commons.utils.AssertionUtils.*;
 import static org.apache.juneau.commons.utils.CollectionUtils.*;
+import org.apache.juneau.rest.RestSharedConstants;
 import static org.apache.juneau.commons.utils.IoUtils.*;
 import static org.apache.juneau.commons.utils.ThrowableUtils.*;
 import static org.apache.juneau.commons.utils.Utils.*;
@@ -1692,6 +1693,128 @@ public class RestRequest extends BeanSession implements 
HttpUriRequest, Configur
                return 
serializer(PlainTextSerializer.class).parser(PlainTextParser.class);
        }
 
+       /**
+        * Sets the {@code X-Juneau-Serializer-Options} header for this request.
+        *
+        * <p>
+        * The value is a JSON5 object whose keys are serializer session 
property names and values are the overrides.
+        * Example: <js>"{escapeSolidus:true,maxIndent:4}"</js>.
+        *
+        * @param json5
+        *      Serializer session options as JSON5. Can be <jk>null</jk> 
(header is not set).
+        * @return This object.
+        */
+       public RestRequest serializerSessionOptionsHeader(String json5) {
+               return 
header(RestSharedConstants.HEADER_JuneauSerializerOptions, json5);
+       }
+
+       /**
+        * Sets the {@code X-Juneau-Parser-Options} header for this request.
+        *
+        * <p>
+        * The value is a JSON5 object whose keys are parser session property 
names and values are the overrides.
+        *
+        * @param json5
+        *      Parser session options as JSON5. Can be <jk>null</jk> (header 
is not set).
+        * @return This object.
+        */
+       public RestRequest parserSessionOptionsHeader(String json5) {
+               return header(RestSharedConstants.HEADER_JuneauParserOptions, 
json5);
+       }
+
+       /**
+        * Convenience for {@link #serializerSessionOptionsHeader(String)} 
using a map serialized with {@link 
org.apache.juneau.marshaller.Json5#of(Object)}.
+        *
+        * @param properties
+        *      Property map for serializer session options. Can be 
<jk>null</jk> (treated as empty).
+        * @return This object.
+        */
+       public RestRequest serializerSessionOptionsHeader(Map<String,?> 
properties) {
+               if (isEmpty(properties))
+                       return this;
+               try {
+                       return 
header(RestSharedConstants.HEADER_JuneauSerializerOptions, 
org.apache.juneau.marshaller.Json5.of(properties));
+               } catch (org.apache.juneau.serializer.SerializeException e) {
+                       throw new RuntimeException("Could not serialize 
serializer session options header", e);
+               }
+       }
+
+       /**
+        * Convenience for {@link #parserSessionOptionsHeader(String)} using a 
map serialized with {@link org.apache.juneau.marshaller.Json5#of(Object)}.
+        *
+        * @param properties
+        *      Property map for parser session options. Can be <jk>null</jk> 
(treated as empty).
+        * @return This object.
+        */
+       public RestRequest parserSessionOptionsHeader(Map<String,?> properties) 
{
+               if (isEmpty(properties))
+                       return this;
+               try {
+                       return 
header(RestSharedConstants.HEADER_JuneauParserOptions, 
org.apache.juneau.marshaller.Json5.of(properties));
+               } catch (org.apache.juneau.serializer.SerializeException e) {
+                       throw new RuntimeException("Could not serialize parser 
session options header", e);
+               }
+       }
+
+       /**
+        * Sets the {@code juneauSerializerOptions} query parameter for this 
request (UON-encoded map).
+        *
+        * <p>
+        * Example: <js>"(escapeSolidus=true,maxIndent=4)"</js>.
+        *
+        * @param uon
+        *      Serializer session options in UON form. Can be <jk>null</jk> 
(parameter is not set).
+        * @return This object.
+        */
+       public RestRequest serializerSessionOptionsQuery(String uon) {
+               return 
queryData(RestSharedConstants.QUERY_juneauSerializerOptions, uon);
+       }
+
+       /**
+        * Sets the {@code juneauParserOptions} query parameter for this 
request (UON-encoded map).
+        *
+        * @param uon
+        *      Parser session options in UON form. Can be <jk>null</jk> 
(parameter is not set).
+        * @return This object.
+        */
+       public RestRequest parserSessionOptionsQuery(String uon) {
+               return queryData(RestSharedConstants.QUERY_juneauParserOptions, 
uon);
+       }
+
+       /**
+        * Convenience for {@link #serializerSessionOptionsQuery(String)} using 
{@link org.apache.juneau.marshaller.Uon#of(Object)}.
+        *
+        * @param properties
+        *      Property map for serializer session options. Can be 
<jk>null</jk> (treated as empty).
+        * @return This object.
+        */
+       public RestRequest serializerSessionOptionsQuery(Map<String,?> 
properties) {
+               if (isEmpty(properties))
+                       return this;
+               try {
+                       return 
serializerSessionOptionsQuery(org.apache.juneau.marshaller.Uon.of(properties));
+               } catch (org.apache.juneau.serializer.SerializeException e) {
+                       throw new RuntimeException("Could not serialize 
serializer session options query", e);
+               }
+       }
+
+       /**
+        * Convenience for {@link #parserSessionOptionsQuery(String)} using 
{@link org.apache.juneau.marshaller.Uon#of(Object)}.
+        *
+        * @param properties
+        *      Property map for parser session options. Can be <jk>null</jk> 
(treated as empty).
+        * @return This object.
+        */
+       public RestRequest parserSessionOptionsQuery(Map<String,?> properties) {
+               if (isEmpty(properties))
+                       return this;
+               try {
+                       return 
parserSessionOptionsQuery(org.apache.juneau.marshaller.Uon.of(properties));
+               } catch (org.apache.juneau.serializer.SerializeException e) {
+                       throw new RuntimeException("Could not serialize parser 
session options query", e);
+               }
+       }
+
        /**
         * Sets the protocol version for this request.
         *
diff --git 
a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/rest/RestSharedConstants.java
 
b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/rest/RestSharedConstants.java
new file mode 100644
index 0000000000..c6a7fc1c47
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/rest/RestSharedConstants.java
@@ -0,0 +1,59 @@
+/*
+ * 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.rest;
+
+/**
+ * Static constants shared across Juneau REST modules (for example {@code 
juneau-rest-server},
+ * {@code juneau-rest-client}, and {@code juneau-rest-mock}) so wire names and 
other cross-tier
+ * strings stay aligned.
+ *
+ * <p>
+ * Add more {@code public static final} fields here when server and client (or 
other REST modules)
+ * must agree on the same literal values. Server-only constants belong in
+ * {@code RestServerConstants} in {@code juneau-rest-server}.
+ */
+@SuppressWarnings({
+       "java:S115", // Names use HEADER_/QUERY_ + camelCase to mirror wire 
identifiers; not strict UPPER_SNAKE_CASE
+})
+public final class RestSharedConstants {
+
+       private RestSharedConstants() {}
+
+       /**
+        * Serializer session options request header (JSON5 object format).
+        *
+        * <p>
+        * HTTP header names are case-insensitive; use this spelling in OpenAPI 
and examples.
+        * Example value: <js>{escapeSolidus:true,maxIndent:4}</js>
+        */
+       public static final String HEADER_JuneauSerializerOptions = 
"X-Juneau-Serializer-Options";
+
+       /**
+        * Parser session options request header (JSON5 object format).
+        *
+        * <p>
+        * HTTP header names are case-insensitive; use this spelling in OpenAPI 
and examples.
+        * Example value: <js>{trimStrings:true}</js>
+        */
+       public static final String HEADER_JuneauParserOptions = 
"X-Juneau-Parser-Options";
+
+       /** Serializer session options query parameter (UON-encoded map 
format). Example: <js>(maxIndent=4,sortMaps=true)</js> */
+       public static final String QUERY_juneauSerializerOptions = 
"juneauSerializerOptions";
+
+       /** Parser session options query parameter (UON-encoded map format). 
Example: <js>(trimStrings=true)</js> */
+       public static final String QUERY_juneauParserOptions = 
"juneauParserOptions";
+}
diff --git 
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestClient.java
 
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestClient.java
index ee3204f257..62dea237a7 100644
--- 
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestClient.java
+++ 
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestClient.java
@@ -320,6 +320,54 @@ public class MockRestClient extends RestClient implements 
HttpClientConnection {
                        return this;
                }
 
+               @Override /* Overridden from Builder */
+               public Builder serializerSessionOptionsHeader(String json5) {
+                       super.serializerSessionOptionsHeader(json5);
+                       return this;
+               }
+
+               @Override /* Overridden from Builder */
+               public Builder 
serializerSessionOptionsHeader(java.util.Map<String,?> properties) {
+                       super.serializerSessionOptionsHeader(properties);
+                       return this;
+               }
+
+               @Override /* Overridden from Builder */
+               public Builder parserSessionOptionsHeader(String json5) {
+                       super.parserSessionOptionsHeader(json5);
+                       return this;
+               }
+
+               @Override /* Overridden from Builder */
+               public Builder 
parserSessionOptionsHeader(java.util.Map<String,?> properties) {
+                       super.parserSessionOptionsHeader(properties);
+                       return this;
+               }
+
+               @Override /* Overridden from Builder */
+               public Builder serializerSessionOptionsQueryDefault(String uon) 
{
+                       super.serializerSessionOptionsQueryDefault(uon);
+                       return this;
+               }
+
+               @Override /* Overridden from Builder */
+               public Builder 
serializerSessionOptionsQueryDefault(java.util.Map<String,?> properties) {
+                       super.serializerSessionOptionsQueryDefault(properties);
+                       return this;
+               }
+
+               @Override /* Overridden from Builder */
+               public Builder parserSessionOptionsQueryDefault(String uon) {
+                       super.parserSessionOptionsQueryDefault(uon);
+                       return this;
+               }
+
+               @Override /* Overridden from Builder */
+               public Builder 
parserSessionOptionsQueryDefault(java.util.Map<String,?> properties) {
+                       super.parserSessionOptionsQueryDefault(properties);
+                       return this;
+               }
+
                @Override /* Overridden from Builder */
                public Builder annotations(Annotation...values) {
                        super.annotations(values);
diff --git 
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestRequest.java
 
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestRequest.java
index 118465356f..5f750e067b 100644
--- 
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestRequest.java
+++ 
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock/MockRestRequest.java
@@ -806,6 +806,54 @@ public class MockRestRequest extends 
org.apache.juneau.rest.client.RestRequest {
                return this;
        }
 
+       @Override /* Overridden from RestRequest */
+       public MockRestRequest serializerSessionOptionsHeader(String json5) {
+               super.serializerSessionOptionsHeader(json5);
+               return this;
+       }
+
+       @Override /* Overridden from RestRequest */
+       public MockRestRequest serializerSessionOptionsHeader(Map<String,?> 
properties) {
+               super.serializerSessionOptionsHeader(properties);
+               return this;
+       }
+
+       @Override /* Overridden from RestRequest */
+       public MockRestRequest serializerSessionOptionsQuery(String uon) {
+               super.serializerSessionOptionsQuery(uon);
+               return this;
+       }
+
+       @Override /* Overridden from RestRequest */
+       public MockRestRequest serializerSessionOptionsQuery(Map<String,?> 
properties) {
+               super.serializerSessionOptionsQuery(properties);
+               return this;
+       }
+
+       @Override /* Overridden from RestRequest */
+       public MockRestRequest parserSessionOptionsHeader(String json5) {
+               super.parserSessionOptionsHeader(json5);
+               return this;
+       }
+
+       @Override /* Overridden from RestRequest */
+       public MockRestRequest parserSessionOptionsHeader(Map<String,?> 
properties) {
+               super.parserSessionOptionsHeader(properties);
+               return this;
+       }
+
+       @Override /* Overridden from RestRequest */
+       public MockRestRequest parserSessionOptionsQuery(String uon) {
+               super.parserSessionOptionsQuery(uon);
+               return this;
+       }
+
+       @Override /* Overridden from RestRequest */
+       public MockRestRequest parserSessionOptionsQuery(Map<String,?> 
properties) {
+               super.parserSessionOptionsQuery(properties);
+               return this;
+       }
+
        @Override /* Overridden from RestRequest */
        public MockRestRequest protocolVersion(ProtocolVersion version) {
                super.protocolVersion(version);
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 96640c41a9..c681f7cf71 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -23,6 +23,7 @@ import static 
org.apache.juneau.commons.utils.AssertionUtils.*;
 import static org.apache.juneau.commons.utils.ClassUtils.*;
 import static org.apache.juneau.commons.utils.CollectionUtils.*;
 import static org.apache.juneau.commons.utils.PredicateUtils.*;
+import static org.apache.juneau.rest.RestServerConstants.*;
 import static org.apache.juneau.commons.utils.IoUtils.*;
 import static org.apache.juneau.commons.utils.StringUtils.*;
 import static org.apache.juneau.commons.utils.Utils.*;
@@ -49,6 +50,7 @@ import org.apache.juneau.*;
 import org.apache.juneau.bean.swagger.Swagger;
 import org.apache.juneau.commons.collections.*;
 import org.apache.juneau.commons.collections.FluentMap;
+import org.apache.juneau.commons.function.Memoizer;
 import org.apache.juneau.commons.lang.*;
 import org.apache.juneau.commons.logging.Logger;
 import org.apache.juneau.commons.reflect.*;
@@ -5184,6 +5186,159 @@ public class RestContext extends Context {
                return RestSession.create(this);
        }
 
+       
//---------------------------------------------------------------------------------------------
+       // Memoized allowlist fields
+       
//---------------------------------------------------------------------------------------------
+
+       /**
+        * Memoized value of the {@code noInherit} annotation attribute from 
the nearest {@code @Rest} annotation.
+        *
+        * <p>
+        * {@code noInherit} itself is never inherited; it only applies to the 
{@code @Rest} that declares it.
+        */
+       private final Memoizer<SortedSet<String>> noInherit = memoizer(() ->
+               getRestAnnotation()
+                       .map(x -> 
x.getStringArray("noInherit").orElse(StringUtils.EMPTY_STRING_ARRAY))
+                       .map(x -> treeSet(String.CASE_INSENSITIVE_ORDER, 
resolveCdl(x).toList()))
+                       .orElseGet(Collections::emptySortedSet)
+       );
+
+       /**
+        * Memoized list of every {@link Rest} annotation on the resource class 
and its supertypes, in child-to-parent order.
+        */
+       private final Memoizer<List<AnnotationInfo<Rest>>> restAnnotations = 
memoizer(() ->
+               getAnnotationProvider().find(Rest.class, 
ClassInfo.of(getResourceClass()))
+       );
+
+       /**
+        * Memoized parser session-option keys from {@code 
@Rest(allowedParserOptions)}, after SVL resolution and comma expansion.
+        *
+        * <p>
+        * When inheritance is not blocked, keys from {@link #parentContext} 
are included first, then this resource's own tokens.
+        * Leading-hyphen tokens (e.g. {@code -foo}) remove earlier positive 
tokens.
+        */
+       private final Memoizer<SortedSet<String>> allowedParserOptions = 
memoizer(this::findAllowedParserOptions);
+
+       private SortedSet<String> findAllowedParserOptions() {
+               var l = new ArrayList<String>();
+               var p = PROPERTY_allowedParserOptions;
+               if (isInherited(p) && parentContext != null)
+                       l.addAll(parentContext.getAllowedParserOptions());
+               getRestAnnotationsForProperty(p).forEach(x -> 
resolveCdl(x.getStringArray(p)).forEach(l::add));
+               return 
Collections.unmodifiableSortedSet(treeSet(String.CASE_INSENSITIVE_ORDER, 
removeNegations(l)));
+       }
+
+       /**
+        * Memoized serializer session-option keys from {@code 
@Rest(allowedSerializerOptions)}, after SVL resolution and comma expansion.
+        *
+        * <p>
+        * When inheritance is not blocked, keys from {@link #parentContext} 
are included first, then this resource's own tokens.
+        * Leading-hyphen tokens remove earlier positive tokens.
+        */
+       private final Memoizer<SortedSet<String>> allowedSerializerOptions = 
memoizer(this::findAllowedSerializerOptions);
+
+       private SortedSet<String> findAllowedSerializerOptions() {
+               var l = new ArrayList<String>();
+               var p = PROPERTY_allowedSerializerOptions;
+               if (isInherited(p) && parentContext != null)
+                       l.addAll(parentContext.getAllowedSerializerOptions());
+               getRestAnnotationsForProperty(p).forEach(x -> 
resolveCdl(x.getStringArray(p)).forEach(l::add));
+               return 
Collections.unmodifiableSortedSet(treeSet(String.CASE_INSENSITIVE_ORDER, 
removeNegations(l)));
+       }
+
+       private Stream<AnnotationInfo<Rest>> 
getRestAnnotationsForProperty(String name) {
+               var annotations = getRestAnnotations();
+               var cutoff = annotations.size();
+               for (var i = 0; i < annotations.size(); i++) {
+                       if 
(resolveCdl(annotations.get(i).getStringArray(PROPERTY_noInherit)).anyMatch(name::equalsIgnoreCase))
 {
+                               cutoff = i + 1;
+                               break;
+                       }
+               }
+               return rstream(annotations.subList(0, cutoff));
+       }
+
+       /**
+        * Returns all {@link Rest} annotations on the resource class 
hierarchy, in child-to-parent order.
+        *
+        * @return An unmodifiable list of {@link AnnotationInfo} for {@link 
Rest}, never {@code null}.
+        */
+       protected List<AnnotationInfo<Rest>> getRestAnnotations() {
+               return restAnnotations.get();
+       }
+
+       /**
+        * Returns the nearest {@link Rest} annotation on this resource.
+        *
+        * @return An {@link Optional} containing the first (most-derived) 
{@link Rest} {@link AnnotationInfo}.
+        */
+       protected Optional<AnnotationInfo<Rest>> getRestAnnotation() {
+               return getRestAnnotations().stream().findFirst();
+       }
+
+       /**
+        * Returns {@code true} if values for the given annotation attribute 
may be inherited from {@link #parentContext}.
+        *
+        * <p>
+        * Inheritance is blocked when the nearest {@code @Rest(noInherit)} 
lists the property name (case-insensitive).
+        *
+        * @param property The annotation attribute name (e.g. {@code 
"allowedSerializerOptions"}).
+        * @return {@code true} if parent values should be included.
+        */
+       protected boolean isInherited(String property) {
+               return RestContext.this.parentContext != null && 
!noInherit.get().contains(property);
+       }
+
+       /**
+        * Resolves comma-delimited annotation values with SVL variable 
substitution.
+        *
+        * @param values Raw annotation attribute values.
+        * @return A stream of trimmed, non-blank tokens.
+        */
+       private Stream<String> resolveCdl(String...values) {
+               if (values == null || values.length == 0)
+                       return Stream.empty();
+               return Arrays.stream(values)
+                       .filter(Objects::nonNull)
+                       .map(this::resolve)
+                       .map(StringUtils::split)
+                       .flatMap(Collection::stream)
+                       .map(String::trim)
+                       .filter(StringUtils::isNotBlank);
+       }
+
+       private Stream<String> resolveCdl(Optional<String[]> values) {
+               return values.isEmpty() ? Stream.empty() : 
resolveCdl(values.get());
+       }
+
+       /**
+        * Resolves SVL variables in the given string.
+        *
+        * @param s The raw string. Can be {@code null}.
+        * @return The resolved string.
+        */
+       protected String resolve(String s) {
+               return getVarResolver().resolve(s);
+       }
+
+       /**
+        * Returns the parser session-option keys allowed for this resource.
+        *
+        * @return An unmodifiable case-insensitive sorted set, never {@code 
null}.
+        */
+       public SortedSet<String> getAllowedParserOptions() {
+               return allowedParserOptions.get();
+       }
+
+       /**
+        * Returns the serializer session-option keys allowed for this resource.
+        *
+        * @return An unmodifiable case-insensitive sorted set, never {@code 
null}.
+        */
+       public SortedSet<String> getAllowedSerializerOptions() {
+               return allowedSerializerOptions.get();
+       }
+
        /**
         * Called during servlet destruction to invoke all {@link RestDestroy} 
methods.
         */
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
index 2c0fabf8bb..5ba6e9b103 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestOpContext.java
@@ -22,6 +22,7 @@ import static 
org.apache.juneau.commons.utils.CollectionUtils.*;
 import static org.apache.juneau.commons.utils.StringUtils.*;
 import static org.apache.juneau.commons.utils.ThrowableUtils.*;
 import static org.apache.juneau.commons.utils.Utils.*;
+import static org.apache.juneau.rest.RestServerConstants.*;
 import static org.apache.juneau.http.HttpHeaders.*;
 import static org.apache.juneau.http.HttpParts.*;
 import static org.apache.juneau.httppart.HttpPartType.*;
@@ -32,12 +33,14 @@ import java.lang.reflect.Method;
 import java.nio.charset.*;
 import java.util.*;
 import java.util.concurrent.*;
+import java.util.stream.*;
 import java.util.function.*;
 import org.apache.http.*;
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.commons.collections.*;
 import org.apache.juneau.commons.collections.FluentMap;
+import org.apache.juneau.commons.function.Memoizer;
 import org.apache.juneau.commons.lang.*;
 import org.apache.juneau.commons.reflect.*;
 import org.apache.juneau.commons.utils.*;
@@ -2179,6 +2182,107 @@ public class RestOpContext extends Context implements 
Comparable<RestOpContext>
        protected final String httpMethod;
        protected final UrlPathMatcher[] pathMatchers;
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Memoized allowlist fields
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       /** Memoized all {@link RestOp}-group annotations on this method, 
child-to-parent order. */
+       private final Memoizer<List<AnnotationInfo<?>>> restOpAnnotations = 
memoizer(this::findRestOpAnnotations);
+
+       private List<AnnotationInfo<?>> findRestOpAnnotations() {
+               return context.getAnnotationProvider().find(mi, SELF, 
MATCHING_METHODS).stream()
+                       .filter(ai -> ai.isInGroup(RestOp.class))
+                       .toList();
+       }
+
+       /** Memoized aggregated {@code noInherit} keys from all RestOp-group 
annotations on this operation. */
+       private final Memoizer<SortedSet<String>> noInheritOp = 
memoizer(this::findNoInheritOp);
+
+       private SortedSet<String> findNoInheritOp() {
+               var l = getRestOpAnnotations().stream()
+                       .map(ai -> 
ai.getStringArray("noInherit").orElse(StringUtils.EMPTY_STRING_ARRAY))
+                       .flatMap(arr -> resolveCdl(arr))
+                       .toList();
+               return 
Collections.unmodifiableSortedSet(treeSet(String.CASE_INSENSITIVE_ORDER, l));
+       }
+
+       /** Memoized effective allowed parser option keys for this operation. */
+       private final Memoizer<SortedSet<String>> allowedParserOptions = 
memoizer(this::findAllowedParserOptions);
+
+       private SortedSet<String> findAllowedParserOptions() {
+               var l = new ArrayList<String>();
+               var p = PROPERTY_allowedParserOptions;
+               if (isInherited(p))
+                       l.addAll(context.getAllowedParserOptions());
+               getRestOpAnnotations().stream()
+                       .flatMap(ai -> 
resolveCdl(ai.getStringArray(p).orElse(new String[0])))
+                       .forEach(l::add);
+               return 
Collections.unmodifiableSortedSet(treeSet(String.CASE_INSENSITIVE_ORDER, 
removeNegations(l)));
+       }
+
+       /** Memoized effective allowed serializer option keys for this 
operation. */
+       private final Memoizer<SortedSet<String>> allowedSerializerOptions = 
memoizer(this::findAllowedSerializerOptions);
+
+       private SortedSet<String> findAllowedSerializerOptions() {
+               var l = new ArrayList<String>();
+               var p = PROPERTY_allowedSerializerOptions;
+               if (isInherited(p))
+                       l.addAll(context.getAllowedSerializerOptions());
+               getRestOpAnnotations().stream()
+                       .flatMap(ai -> 
resolveCdl(ai.getStringArray(p).orElse(new String[0])))
+                       .forEach(l::add);
+               return 
Collections.unmodifiableSortedSet(treeSet(String.CASE_INSENSITIVE_ORDER, 
removeNegations(l)));
+       }
+
+       private Stream<String> resolveCdl(String...values) {
+               if (values == null || values.length == 0)
+                       return Stream.empty();
+               return Arrays.stream(values)
+                       .filter(Objects::nonNull)
+                       .map(s -> 
RestOpContext.this.context.getVarResolver().resolve(s))
+                       .map(StringUtils::split)
+                       .flatMap(Collection::stream)
+                       .map(String::trim)
+                       .filter(StringUtils::isNotBlank);
+       }
+
+       /**
+        * Returns all {@link RestOp}-group annotations on this operation 
method, in child-to-parent order.
+        *
+        * @return An unmodifiable list, never {@code null}.
+        */
+       public List<AnnotationInfo<?>> getRestOpAnnotations() {
+               return restOpAnnotations.get();
+       }
+
+       /**
+        * Returns {@code true} if context-level values for the given property 
should be merged.
+        *
+        * @param property The annotation attribute name.
+        * @return {@code true} if {@code noInherit} does not contain this 
property.
+        */
+       protected boolean isInherited(String property) {
+               return !noInheritOp.get().contains(property);
+       }
+
+       /**
+        * Returns the parser session-option keys allowed for this operation.
+        *
+        * @return An unmodifiable case-insensitive sorted set, never {@code 
null}.
+        */
+       public SortedSet<String> getAllowedParserOptions() {
+               return allowedParserOptions.get();
+       }
+
+       /**
+        * Returns the serializer session-option keys allowed for this 
operation.
+        *
+        * @return An unmodifiable case-insensitive sorted set, never {@code 
null}.
+        */
+       public SortedSet<String> getAllowedSerializerOptions() {
+               return allowedSerializerOptions.get();
+       }
+
        /**
         * Context constructor.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
index 16feb2c4d5..05a9011442 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -18,6 +18,7 @@ package org.apache.juneau.rest;
 
 import static java.util.Optional.*;
 import static org.apache.juneau.commons.utils.CollectionUtils.*;
+import static org.apache.juneau.rest.RestSharedConstants.*;
 import static org.apache.juneau.commons.utils.IoUtils.*;
 import static org.apache.juneau.commons.utils.StringUtils.*;
 import static org.apache.juneau.commons.utils.ThrowableUtils.*;
@@ -31,6 +32,8 @@ import java.net.*;
 import java.nio.charset.*;
 import java.text.*;
 import java.util.*;
+import java.util.function.*;
+import java.util.stream.*;
 
 import org.apache.http.*;
 import org.apache.http.message.*;
@@ -57,6 +60,9 @@ import org.apache.juneau.rest.staticfile.*;
 import org.apache.juneau.rest.swagger.*;
 import org.apache.juneau.rest.util.*;
 import org.apache.juneau.svl.*;
+import org.apache.juneau.collections.*;
+import org.apache.juneau.marshaller.*;
+import org.apache.juneau.parser.ParseException;
 import org.apache.juneau.uon.*;
 
 import jakarta.servlet.*;
@@ -212,6 +218,8 @@ public class RestRequest extends HttpServletRequestWrapper {
        private Swagger swagger;
 
        private Charset charset;
+       private Map<String,Object> serializerSessionProperties;
+       private Map<String,Object> parserSessionProperties;
 
        /**
         * Constructor.
@@ -1427,6 +1435,200 @@ public class RestRequest extends 
HttpServletRequestWrapper {
                attrs.set(name, value);
        }
 
+       /**
+        * Sets a serializer session property programmatically.
+        *
+        * <p>
+        * Session properties override context-level properties and can be used 
to customize serializer behavior for this request only.
+        *
+        * @param name The property name (e.g. {@code "escapeSolidus"}).
+        * @param value The property value.
+        * @return This object.
+        */
+       public RestRequest setSerializerSessionProperty(String name, Object 
value) {
+               if (serializerSessionProperties == null)
+                       serializerSessionProperties = new LinkedHashMap<>();
+               serializerSessionProperties.put(name, value);
+               return this;
+       }
+
+       /**
+        * Sets a parser session property programmatically.
+        *
+        * <p>
+        * Session properties override context-level properties and can be used 
to customize parser behavior for this request only.
+        *
+        * @param name The property name (e.g. {@code "trimStrings"}).
+        * @param value The property value.
+        * @return This object.
+        */
+       public RestRequest setParserSessionProperty(String name, Object value) {
+               if (parserSessionProperties == null)
+                       parserSessionProperties = new LinkedHashMap<>();
+               parserSessionProperties.put(name, value);
+               return this;
+       }
+
+       /**
+        * Sets multiple serializer session properties programmatically.
+        *
+        * @param values The properties to set. Can be {@code null}.
+        * @return This object.
+        */
+       public RestRequest setSerializerSessionProperties(Map<String,Object> 
values) {
+               if (values != null && !values.isEmpty()) {
+                       if (serializerSessionProperties == null)
+                               serializerSessionProperties = new 
LinkedHashMap<>();
+                       serializerSessionProperties.putAll(values);
+               }
+               return this;
+       }
+
+       /**
+        * Sets multiple parser session properties programmatically.
+        *
+        * @param values The properties to set. Can be {@code null}.
+        * @return This object.
+        */
+       public RestRequest setParserSessionProperties(Map<String,Object> 
values) {
+               if (values != null && !values.isEmpty()) {
+                       if (parserSessionProperties == null)
+                               parserSessionProperties = new LinkedHashMap<>();
+                       parserSessionProperties.putAll(values);
+               }
+               return this;
+       }
+
+       /**
+        * Returns the merged serializer session property map for this request.
+        *
+        * <p>
+        * Merges (in order of increasing priority):
+        * <ol>
+        *      <li>Request attributes (from {@link #getAttributes()}, e.g. 
{@code defaultRequestAttributes})
+        *      <li>UON-encoded {@code juneauSerializerOptions} query parameter
+        *      <li>JSON5-encoded {@code X-Juneau-Serializer-Options} header
+        *      <li>Programmatically-set properties via {@link 
#setSerializerSessionProperty}
+        * </ol>
+        *
+        * <p>
+        * Client-supplied keys are validated against the allowlist from {@link 
RestOpContext#getAllowedSerializerOptions()}.
+        * Keys not in the allowlist cause a {@code 400 Bad Request} response.
+        *
+        * @return An unmodifiable map of session properties; may be empty but 
never {@code null}.
+        */
+       @SuppressWarnings({
+               "java:S3776" // cognitive complexity acceptable; sequential 
null-checks for 4 sources
+       })
+       public Map<String,Object> getSerializerSessionPropertyMap() {
+               var allowlist = opContext.getAllowedSerializerOptions();
+               Map<String,Object> m1 = null, m2 = null;
+
+               var q = 
getQueryParams().get(QUERY_juneauSerializerOptions).asString().orElse(null);
+               if (q != null && !q.isBlank()) {
+                       m1 = parseUonMap(q, e -> badRequest(e, "Could not parse 
UON session options from query parameter ''{0}''.", 
QUERY_juneauSerializerOptions));
+                       var invalid = m1.keySet().stream().filter(k -> 
!allowlist.contains(k)).collect(Collectors.joining(","));
+                       if (!invalid.isEmpty())
+                               badRequest("Invalid session options in query 
parameter ''{0}'': ''{1}''", QUERY_juneauSerializerOptions, invalid);
+               }
+
+               var h = 
getHeaderParam(HEADER_JuneauSerializerOptions).asString().orElse(null);
+               if (h != null && !h.isBlank()) {
+                       m2 = parseJsonMap(h, e -> badRequest(e, "Could not 
parse JSON5 session options from header ''{0}''.", 
HEADER_JuneauSerializerOptions));
+                       var invalid = m2.keySet().stream().filter(k -> 
!allowlist.contains(k)).collect(Collectors.joining(","));
+                       if (!invalid.isEmpty())
+                               badRequest("Invalid session options in header 
''{0}'': ''{1}''", HEADER_JuneauSerializerOptions, invalid);
+               }
+
+               return mergeSessionMaps(getAttributes().asMap(), m1, m2, 
serializerSessionProperties);
+       }
+
+       /**
+        * Returns the merged parser session property map for this request.
+        *
+        * <p>
+        * Merges (in order of increasing priority):
+        * <ol>
+        *      <li>Request attributes (from {@link #getAttributes()}, e.g. 
{@code defaultRequestAttributes})
+        *      <li>UON-encoded {@code juneauParserOptions} query parameter
+        *      <li>JSON5-encoded {@code X-Juneau-Parser-Options} header
+        *      <li>Programmatically-set properties via {@link 
#setParserSessionProperty}
+        * </ol>
+        *
+        * <p>
+        * Client-supplied keys are validated against the allowlist from {@link 
RestOpContext#getAllowedParserOptions()}.
+        * Keys not in the allowlist cause a {@code 400 Bad Request} response.
+        *
+        * @return An unmodifiable map of session properties; may be empty but 
never {@code null}.
+        */
+       @SuppressWarnings({
+               "java:S3776" // cognitive complexity acceptable; sequential 
null-checks for 4 sources
+       })
+       public Map<String,Object> getParserSessionPropertyMap() {
+               var allowlist = opContext.getAllowedParserOptions();
+               Map<String,Object> m1 = null, m2 = null;
+
+               var q = 
getQueryParams().get(QUERY_juneauParserOptions).asString().orElse(null);
+               if (q != null && !q.isBlank()) {
+                       m1 = parseUonMap(q, e -> badRequest(e, "Could not parse 
UON session options from query parameter ''{0}''.", QUERY_juneauParserOptions));
+                       var invalid = m1.keySet().stream().filter(k -> 
!allowlist.contains(k)).collect(Collectors.joining(","));
+                       if (!invalid.isEmpty())
+                               badRequest("Invalid session options in query 
parameter ''{0}'': ''{1}''", QUERY_juneauParserOptions, invalid);
+               }
+
+               var h = 
getHeaderParam(HEADER_JuneauParserOptions).asString().orElse(null);
+               if (h != null && !h.isBlank()) {
+                       m2 = parseJsonMap(h, e -> badRequest(e, "Could not 
parse JSON5 session options from header ''{0}''.", HEADER_JuneauParserOptions));
+                       var invalid = m2.keySet().stream().filter(k -> 
!allowlist.contains(k)).collect(Collectors.joining(","));
+                       if (!invalid.isEmpty())
+                               badRequest("Invalid session options in header 
''{0}'': ''{1}''", HEADER_JuneauParserOptions, invalid);
+               }
+
+               return mergeSessionMaps(getAttributes().asMap(), m1, m2, 
parserSessionProperties);
+       }
+
+       @SafeVarargs
+       private static Map<String,Object> 
mergeSessionMaps(Map<String,Object>...maps) {
+               Map<String,Object> result = null;
+               for (var map : maps) {
+                       if (map != null && !map.isEmpty()) {
+                               if (result == null)
+                                       result = new LinkedHashMap<>(map);
+                               else
+                                       result.putAll(map);
+                       }
+               }
+               return result != null ? Collections.unmodifiableMap(result) : 
Collections.emptyMap();
+       }
+
+       private static void badRequest(String msg, Object...args) {
+               throw new BadRequest(msg, args);
+       }
+
+       private static void badRequest(Exception causedBy, String msg, 
Object...args) {
+               throw new BadRequest(causedBy, msg, args);
+       }
+
+       private static Map<String,Object> parseUonMap(String input, 
Consumer<ParseException> onError) {
+               try {
+                       var m = Uon.DEFAULT.read(input, JsonMap.class);
+                       return m != null ? m : Collections.emptyMap();
+               } catch (ParseException e) {
+                       onError.accept(e);
+                       return Collections.emptyMap();
+               }
+       }
+
+       private static Map<String,Object> parseJsonMap(String input, 
Consumer<ParseException> onError) {
+               try {
+                       var m = Json5.DEFAULT.read(input, JsonMap.class);
+                       return m != null ? m : Collections.emptyMap();
+               } catch (ParseException e) {
+                       onError.accept(e);
+                       return Collections.emptyMap();
+               }
+       }
+
        /**
         * Sets the charset to expect on the request content.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServerConstants.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServerConstants.java
new file mode 100644
index 0000000000..bd9d96afa6
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestServerConstants.java
@@ -0,0 +1,53 @@
+/*
+ * 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.rest;
+
+/**
+ * Static literals for {@code juneau-rest-server}: annotation attribute name 
constants used for
+ * allowlist inheritance checks and other module-internal logic.
+ *
+ * <p>
+ * HTTP wire names shared with clients belong on {@link RestSharedConstants} 
instead.
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ *     <li class='jc'>{@link RestSharedConstants}
+ * </ul>
+ */
+@SuppressWarnings({
+       "java:S115", // PROPERTY_ + camelCase property name mirrors annotation 
attribute name; not strict UPPER_SNAKE_CASE
+})
+public final class RestServerConstants {
+
+       private RestServerConstants() {}
+
+       /**
+        * The {@code "allowedParserOptions"} annotation attribute name — used 
in {@code noInherit} matching.
+        *
+        * @see org.apache.juneau.rest.annotation.Rest#allowedParserOptions()
+        */
+       public static final String PROPERTY_allowedParserOptions = 
"allowedParserOptions";
+
+       /**
+        * The {@code "allowedSerializerOptions"} annotation attribute name — 
used in {@code noInherit} matching.
+        *
+        * @see 
org.apache.juneau.rest.annotation.Rest#allowedSerializerOptions()
+        */
+       public static final String PROPERTY_allowedSerializerOptions = 
"allowedSerializerOptions";
+
+       /** The {@code "noInherit"} annotation attribute name. */
+       public static final String PROPERTY_noInherit = "noInherit";
+}
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
index 8b5419ce2a..7368140180 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/Rest.java
@@ -287,6 +287,62 @@ public @interface Rest {
         */
        String[] consumes() default {};
 
+       /**
+        * Allowed serializer session option keys for this resource (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys (e.g. 
<js>"escapeSolidus,maxIndent"</js>) that clients may
+        * send via the <js>"X-Juneau-Serializer-Options"</js> header or 
<js>"juneauSerializerOptions"</js> query parameter.
+        * Keys not in the effective allowlist cause a {@code 400 Bad Request} 
response.
+        *
+        * <p>
+        * Entries are merged in application order. A leading hyphen removes a 
previously added key: <js>"-escapeSolidus"</js>.
+        * Method-level {@link 
org.apache.juneau.rest.annotation.RestGet#allowedSerializerOptions()} values 
are always merged on top.
+        * Use {@link #noInherit()} to prevent inheriting less-derived 
contributions.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedSerializerOptions() default {};
+
+       /**
+        * Allowed parser session option keys for this resource (ordered merge, 
prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Parser-Options"</js>
+        * header or <js>"juneauParserOptions"</js> query parameter. Parser 
options are ignored for operations without
+        * a request-body parser. Keys not in the effective allowlist cause a 
{@code 400 Bad Request} response.
+        *
+        * <p>
+        * Use {@link #noInherit()} to prevent inheriting less-derived 
contributions.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedParserOptions() default {};
+
+       /**
+        * Property names for which less-derived contributions are NOT 
inherited.
+        *
+        * <p>
+        * Accepted values: {@code "allowedSerializerOptions"}, {@code 
"allowedParserOptions"}.
+        * Each entry is SVL-resolved then comma-split. Prevents the named 
property from inheriting values from
+        * parent {@code @Rest} annotations (router hierarchy). The {@code 
noInherit} attribute itself is never inherited.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions";>Session Options</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] noInherit() default {};
+
        /**
         * Class-level response converters.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java
index 9040454277..bacf7e3f99 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestAnnotation.java
@@ -108,6 +108,9 @@ public class RestAnnotation {
                private String uriContext = "";
                private String uriRelativity = "";
                private String uriResolution = "";
+               private String[] allowedParserOptions = {};
+               private String[] allowedSerializerOptions = {};
+               private String[] noInherit = {};
                private String[] consumes = {};
                private String[] defaultRequestAttributes = {};
                private String[] defaultRequestHeaders = {};
@@ -245,6 +248,39 @@ public class RestAnnotation {
                        return this;
                }
 
+               /**
+                * Sets the {@link Rest#allowedSerializerOptions()} property on 
this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedSerializerOptions(String...value) {
+                       allowedSerializerOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Rest#allowedParserOptions()} property on 
this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedParserOptions(String...value) {
+                       allowedParserOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link Rest#noInherit()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder noInherit(String...value) {
+                       noInherit = value;
+                       return this;
+               }
+
                /**
                 * Sets the {@link Rest#converters()} property on this 
annotation.
                 *
@@ -802,6 +838,9 @@ public class RestAnnotation {
                private final String uriContext;
                private final String uriRelativity;
                private final String uriResolution;
+               private final String[] allowedParserOptions;
+               private final String[] allowedSerializerOptions;
+               private final String[] noInherit;
                private final String[] consumes;
                private final String[] produces;
                private final String[] defaultRequestAttributes;
@@ -825,6 +864,9 @@ public class RestAnnotation {
                        children = copyOf(b.children);
                        clientVersionHeader = b.clientVersionHeader;
                        config = b.config;
+                       allowedParserOptions = copyOf(b.allowedParserOptions);
+                       allowedSerializerOptions = 
copyOf(b.allowedSerializerOptions);
+                       noInherit = copyOf(b.noInherit);
                        consumes = copyOf(b.consumes);
                        converters = copyOf(b.converters);
                        debug = b.debug;
@@ -907,6 +949,21 @@ public class RestAnnotation {
                        return config;
                }
 
+               @Override /* Overridden from Rest */
+               public String[] allowedParserOptions() {
+                       return allowedParserOptions;
+               }
+
+               @Override /* Overridden from Rest */
+               public String[] allowedSerializerOptions() {
+                       return allowedSerializerOptions;
+               }
+
+               @Override /* Overridden from Rest */
+               public String[] noInherit() {
+                       return noInherit;
+               }
+
                @Override /* Overridden from Rest */
                public String[] consumes() {
                        return consumes;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestDelete.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestDelete.java
index e279038bec..35d886bd34 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestDelete.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestDelete.java
@@ -452,6 +452,56 @@ public @interface RestDelete {
         */
        String[] path() default {};
 
+       /**
+        * Allowed serializer session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Serializer-Options"</js>
+        * header or <js>"juneauSerializerOptions"</js> query parameter.
+        * Merged on top of resource-level {@link 
Rest#allowedSerializerOptions()} values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedSerializerOptions() default {};
+
+       /**
+        * Allowed parser session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Parser-Options"</js>
+        * header or <js>"juneauParserOptions"</js> query parameter.
+        * Merged on top of resource-level {@link Rest#allowedParserOptions()} 
values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedParserOptions() default {};
+
+       /**
+        * Property names for which less-derived contributions are NOT 
inherited.
+        *
+        * <p>
+        * Accepted values: {@code "allowedSerializerOptions"}, {@code 
"allowedParserOptions"}.
+        * Prevents the named property from inheriting values from the 
enclosing {@code @Rest} annotation.
+        * The {@code noInherit} attribute itself is never inherited.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions";>Session Options</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] noInherit() default {};
+
        /**
         * Role guard.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestDeleteAnnotation.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestDeleteAnnotation.java
index 7dffbb0001..fa9e2b6d60 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestDeleteAnnotation.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestDeleteAnnotation.java
@@ -77,6 +77,9 @@ public class RestDeleteAnnotation {
                private String[] defaultRequestAttributes = {};
                private String[] defaultRequestHeaders = {};
                private String[] defaultResponseHeaders = {};
+               private String[] allowedParserOptions = {};
+               private String[] allowedSerializerOptions = {};
+               private String[] noInherit = {};
                private String[] path = {};
 
                /**
@@ -241,6 +244,39 @@ public class RestDeleteAnnotation {
                        return this;
                }
 
+               /**
+                * Sets the {@link RestDelete#allowedSerializerOptions()} 
property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedSerializerOptions(String...value) {
+                       allowedSerializerOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestDelete#allowedParserOptions()} property 
on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedParserOptions(String...value) {
+                       allowedParserOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestDelete#noInherit()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder noInherit(String...value) {
+                       noInherit = value;
+                       return this;
+               }
+
                /**
                 * Sets the {@link RestDelete#roleGuard()} property on this 
annotation.
                 *
@@ -374,6 +410,9 @@ public class RestDeleteAnnotation {
                private final String[] defaultRequestAttributes;
                private final String[] defaultRequestHeaders;
                private final String[] defaultResponseHeaders;
+               private final String[] allowedParserOptions;
+               private final String[] allowedSerializerOptions;
+               private final String[] noInherit;
                private final String[] path;
 
                Object(RestDeleteAnnotation.Builder b) {
@@ -390,6 +429,9 @@ public class RestDeleteAnnotation {
                        encoders = copyOf(b.encoders);
                        guards = copyOf(b.guards);
                        matchers = copyOf(b.matchers);
+                       allowedParserOptions = copyOf(b.allowedParserOptions);
+                       allowedSerializerOptions = 
copyOf(b.allowedSerializerOptions);
+                       noInherit = copyOf(b.noInherit);
                        path = copyOf(b.path);
                        roleGuard = b.roleGuard;
                        rolesDeclared = b.rolesDeclared;
@@ -453,6 +495,21 @@ public class RestDeleteAnnotation {
                        return matchers;
                }
 
+               @Override /* Overridden from RestDelete */
+               public String[] allowedParserOptions() {
+                       return allowedParserOptions;
+               }
+
+               @Override /* Overridden from RestDelete */
+               public String[] allowedSerializerOptions() {
+                       return allowedSerializerOptions;
+               }
+
+               @Override /* Overridden from RestDelete */
+               public String[] noInherit() {
+                       return noInherit;
+               }
+
                @Override /* Overridden from RestDelete */
                public String[] path() {
                        return path;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestGet.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestGet.java
index 3e7fd50769..7ec04ef758 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestGet.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestGet.java
@@ -493,6 +493,56 @@ public @interface RestGet {
         */
        String[] produces() default {};
 
+       /**
+        * Allowed serializer session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Serializer-Options"</js>
+        * header or <js>"juneauSerializerOptions"</js> query parameter.
+        * Merged on top of resource-level {@link 
Rest#allowedSerializerOptions()} values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedSerializerOptions() default {};
+
+       /**
+        * Allowed parser session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Parser-Options"</js>
+        * header or <js>"juneauParserOptions"</js> query parameter.
+        * Merged on top of resource-level {@link Rest#allowedParserOptions()} 
values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedParserOptions() default {};
+
+       /**
+        * Property names for which less-derived contributions are NOT 
inherited.
+        *
+        * <p>
+        * Accepted values: {@code "allowedSerializerOptions"}, {@code 
"allowedParserOptions"}.
+        * Prevents the named property from inheriting values from the 
enclosing {@code @Rest} annotation.
+        * The {@code noInherit} attribute itself is never inherited.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions";>Session Options</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] noInherit() default {};
+
        /**
         * Role guard.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestGetAnnotation.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestGetAnnotation.java
index 7ec55bf2cc..b3ab32cded 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestGetAnnotation.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestGetAnnotation.java
@@ -81,6 +81,9 @@ public class RestGetAnnotation {
                private String[] defaultRequestAttributes = {};
                private String[] defaultRequestHeaders = {};
                private String[] defaultResponseHeaders = {};
+               private String[] allowedParserOptions = {};
+               private String[] allowedSerializerOptions = {};
+               private String[] noInherit = {};
                private String[] path = {};
                private String[] produces = {};
 
@@ -269,6 +272,39 @@ public class RestGetAnnotation {
                        return this;
                }
 
+               /**
+                * Sets the {@link RestGet#allowedSerializerOptions()} property 
on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedSerializerOptions(String...value) {
+                       allowedSerializerOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestGet#allowedParserOptions()} property on 
this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedParserOptions(String...value) {
+                       allowedParserOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestGet#noInherit()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder noInherit(String...value) {
+                       noInherit = value;
+                       return this;
+               }
+
                /**
                 * Sets the {@link RestGet#roleGuard()} property on this 
annotation.
                 *
@@ -419,6 +455,9 @@ public class RestGetAnnotation {
                private final String[] defaultRequestAttributes;
                private final String[] defaultRequestHeaders;
                private final String[] defaultResponseHeaders;
+               private final String[] allowedParserOptions;
+               private final String[] allowedSerializerOptions;
+               private final String[] noInherit;
                private final String[] path;
                private final String[] produces;
 
@@ -437,6 +476,9 @@ public class RestGetAnnotation {
                        encoders = copyOf(b.encoders);
                        guards = copyOf(b.guards);
                        matchers = copyOf(b.matchers);
+                       allowedParserOptions = copyOf(b.allowedParserOptions);
+                       allowedSerializerOptions = 
copyOf(b.allowedSerializerOptions);
+                       noInherit = copyOf(b.noInherit);
                        path = copyOf(b.path);
                        produces = copyOf(b.produces);
                        roleGuard = b.roleGuard;
@@ -507,6 +549,21 @@ public class RestGetAnnotation {
                        return matchers;
                }
 
+               @Override /* Overridden from RestGet */
+               public String[] allowedParserOptions() {
+                       return allowedParserOptions;
+               }
+
+               @Override /* Overridden from RestGet */
+               public String[] allowedSerializerOptions() {
+                       return allowedSerializerOptions;
+               }
+
+               @Override /* Overridden from RestGet */
+               public String[] noInherit() {
+                       return noInherit;
+               }
+
                @Override /* Overridden from RestGet */
                public String[] path() {
                        return path;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOp.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOp.java
index 3d1b0d0fa6..83f64572bf 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOp.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOp.java
@@ -726,6 +726,56 @@ public @interface RestOp {
         */
        String[] produces() default {};
 
+       /**
+        * Allowed serializer session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Serializer-Options"</js>
+        * header or <js>"juneauSerializerOptions"</js> query parameter.
+        * Merged on top of resource-level {@link 
Rest#allowedSerializerOptions()} values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedSerializerOptions() default {};
+
+       /**
+        * Allowed parser session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Parser-Options"</js>
+        * header or <js>"juneauParserOptions"</js> query parameter.
+        * Merged on top of resource-level {@link Rest#allowedParserOptions()} 
values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedParserOptions() default {};
+
+       /**
+        * Property names for which less-derived contributions are NOT 
inherited.
+        *
+        * <p>
+        * Accepted values: {@code "allowedSerializerOptions"}, {@code 
"allowedParserOptions"}.
+        * Prevents the named property from inheriting values from the 
enclosing {@code @Rest} annotation.
+        * The {@code noInherit} attribute itself is never inherited.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions";>Session Options</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] noInherit() default {};
+
        /**
         * Role guard.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOpAnnotation.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOpAnnotation.java
index 1ebfe22ae4..b6ee11ddf8 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOpAnnotation.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOpAnnotation.java
@@ -92,6 +92,9 @@ public class RestOpAnnotation {
                private String[] defaultResponseHeaders = {};
                private String[] path = {};
                private String[] produces = {};
+               private String[] allowedSerializerOptions = {};
+               private String[] allowedParserOptions = {};
+               private String[] noInherit = {};
 
                /**
                 * Constructor.
@@ -344,6 +347,39 @@ public class RestOpAnnotation {
                        return this;
                }
 
+               /**
+                * Sets the {@link RestOp#allowedSerializerOptions()} property 
on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedSerializerOptions(String...value) {
+                       allowedSerializerOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestOp#allowedParserOptions()} property on 
this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedParserOptions(String...value) {
+                       allowedParserOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestOp#noInherit()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder noInherit(String...value) {
+                       noInherit = value;
+                       return this;
+               }
+
                /**
                 * Sets the {@link RestOp#roleGuard()} property on this 
annotation.
                 *
@@ -517,6 +553,9 @@ public class RestOpAnnotation {
                private final String[] defaultResponseHeaders;
                private final String[] path;
                private final String[] produces;
+               private final String[] allowedSerializerOptions;
+               private final String[] allowedParserOptions;
+               private final String[] noInherit;
 
                Object(RestOpAnnotation.Builder b) {
                        super(b);
@@ -541,6 +580,9 @@ public class RestOpAnnotation {
                        parsers = copyOf(b.parsers);
                        path = copyOf(b.path);
                        produces = copyOf(b.produces);
+                       allowedSerializerOptions = 
copyOf(b.allowedSerializerOptions);
+                       allowedParserOptions = copyOf(b.allowedParserOptions);
+                       noInherit = copyOf(b.noInherit);
                        roleGuard = b.roleGuard;
                        rolesDeclared = b.rolesDeclared;
                        serializers = copyOf(b.serializers);
@@ -649,6 +691,21 @@ public class RestOpAnnotation {
                        return produces;
                }
 
+               @Override /* Overridden from RestOp */
+               public String[] allowedSerializerOptions() {
+                       return allowedSerializerOptions;
+               }
+
+               @Override /* Overridden from RestOp */
+               public String[] allowedParserOptions() {
+                       return allowedParserOptions;
+               }
+
+               @Override /* Overridden from RestOp */
+               public String[] noInherit() {
+                       return noInherit;
+               }
+
                @Override /* Overridden from RestOp */
                public String roleGuard() {
                        return roleGuard;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOptions.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOptions.java
index 789c500487..f6746461a8 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOptions.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOptions.java
@@ -493,6 +493,56 @@ public @interface RestOptions {
         */
        String[] produces() default {};
 
+       /**
+        * Allowed serializer session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Serializer-Options"</js>
+        * header or <js>"juneauSerializerOptions"</js> query parameter.
+        * Merged on top of resource-level {@link 
Rest#allowedSerializerOptions()} values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedSerializerOptions() default {};
+
+       /**
+        * Allowed parser session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Parser-Options"</js>
+        * header or <js>"juneauParserOptions"</js> query parameter.
+        * Merged on top of resource-level {@link Rest#allowedParserOptions()} 
values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedParserOptions() default {};
+
+       /**
+        * Property names for which less-derived contributions are NOT 
inherited.
+        *
+        * <p>
+        * Accepted values: {@code "allowedSerializerOptions"}, {@code 
"allowedParserOptions"}.
+        * Prevents the named property from inheriting values from the 
enclosing {@code @Rest} annotation.
+        * The {@code noInherit} attribute itself is never inherited.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions";>Session Options</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] noInherit() default {};
+
        /**
         * Role guard.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOptionsAnnotation.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOptionsAnnotation.java
index a66d111333..3df2f33cbd 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOptionsAnnotation.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestOptionsAnnotation.java
@@ -81,6 +81,9 @@ public class RestOptionsAnnotation {
                private String[] defaultRequestAttributes = {};
                private String[] defaultRequestHeaders = {};
                private String[] defaultResponseHeaders = {};
+               private String[] allowedParserOptions = {};
+               private String[] allowedSerializerOptions = {};
+               private String[] noInherit = {};
                private String[] path = {};
                private String[] produces = {};
 
@@ -269,6 +272,39 @@ public class RestOptionsAnnotation {
                        return this;
                }
 
+               /**
+                * Sets the {@link RestOptions#allowedSerializerOptions()} 
property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedSerializerOptions(String...value) {
+                       allowedSerializerOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestOptions#allowedParserOptions()} property 
on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedParserOptions(String...value) {
+                       allowedParserOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestOptions#noInherit()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder noInherit(String...value) {
+                       noInherit = value;
+                       return this;
+               }
+
                /**
                 * Sets the {@link RestOptions#roleGuard()} property on this 
annotation.
                 *
@@ -419,6 +455,9 @@ public class RestOptionsAnnotation {
                private final String[] defaultRequestAttributes;
                private final String[] defaultRequestHeaders;
                private final String[] defaultResponseHeaders;
+               private final String[] allowedParserOptions;
+               private final String[] allowedSerializerOptions;
+               private final String[] noInherit;
                private final String[] path;
                private final String[] produces;
 
@@ -437,6 +476,9 @@ public class RestOptionsAnnotation {
                        encoders = copyOf(b.encoders);
                        guards = copyOf(b.guards);
                        matchers = copyOf(b.matchers);
+                       allowedParserOptions = copyOf(b.allowedParserOptions);
+                       allowedSerializerOptions = 
copyOf(b.allowedSerializerOptions);
+                       noInherit = copyOf(b.noInherit);
                        path = copyOf(b.path);
                        produces = copyOf(b.produces);
                        roleGuard = b.roleGuard;
@@ -507,6 +549,21 @@ public class RestOptionsAnnotation {
                        return matchers;
                }
 
+               @Override /* Overridden from RestOptions */
+               public String[] allowedParserOptions() {
+                       return allowedParserOptions;
+               }
+
+               @Override /* Overridden from RestOptions */
+               public String[] allowedSerializerOptions() {
+                       return allowedSerializerOptions;
+               }
+
+               @Override /* Overridden from RestOptions */
+               public String[] noInherit() {
+                       return noInherit;
+               }
+
                @Override /* Overridden from RestOptions */
                public String[] path() {
                        return path;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPatch.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPatch.java
index 1d3e45ec71..d2c325dd6c 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPatch.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPatch.java
@@ -649,6 +649,56 @@ public @interface RestPatch {
         */
        String[] produces() default {};
 
+       /**
+        * Allowed serializer session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Serializer-Options"</js>
+        * header or <js>"juneauSerializerOptions"</js> query parameter.
+        * Merged on top of resource-level {@link 
Rest#allowedSerializerOptions()} values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedSerializerOptions() default {};
+
+       /**
+        * Allowed parser session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Parser-Options"</js>
+        * header or <js>"juneauParserOptions"</js> query parameter.
+        * Merged on top of resource-level {@link Rest#allowedParserOptions()} 
values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedParserOptions() default {};
+
+       /**
+        * Property names for which less-derived contributions are NOT 
inherited.
+        *
+        * <p>
+        * Accepted values: {@code "allowedSerializerOptions"}, {@code 
"allowedParserOptions"}.
+        * Prevents the named property from inheriting values from the 
enclosing {@code @Rest} annotation.
+        * The {@code noInherit} attribute itself is never inherited.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions";>Session Options</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] noInherit() default {};
+
        /**
         * Role guard.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPatchAnnotation.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPatchAnnotation.java
index 00b1d1f060..a444822bc8 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPatchAnnotation.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPatchAnnotation.java
@@ -86,6 +86,9 @@ public class RestPatchAnnotation {
                private String[] defaultRequestHeaders = {};
                private String[] defaultResponseHeaders = {};
                private String[] description = {};
+               private String[] allowedParserOptions = {};
+               private String[] allowedSerializerOptions = {};
+               private String[] noInherit = {};
                private String[] path = {};
                private String[] produces = {};
 
@@ -329,6 +332,39 @@ public class RestPatchAnnotation {
                        return this;
                }
 
+               /**
+                * Sets the {@link RestPatch#allowedSerializerOptions()} 
property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedSerializerOptions(String...value) {
+                       allowedSerializerOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestPatch#allowedParserOptions()} property 
on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedParserOptions(String...value) {
+                       allowedParserOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestPatch#noInherit()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder noInherit(String...value) {
+                       noInherit = value;
+                       return this;
+               }
+
                /**
                 * Sets the {@link RestPatch#roleGuard()} property on this 
annotation.
                 *
@@ -489,6 +525,9 @@ public class RestPatchAnnotation {
                private final String[] defaultRequestAttributes;
                private final String[] defaultRequestHeaders;
                private final String[] defaultResponseHeaders;
+               private final String[] allowedParserOptions;
+               private final String[] allowedSerializerOptions;
+               private final String[] noInherit;
                private final String[] path;
                private final String[] produces;
 
@@ -512,6 +551,9 @@ public class RestPatchAnnotation {
                        matchers = copyOf(b.matchers);
                        maxInput = b.maxInput;
                        parsers = copyOf(b.parsers);
+                       allowedParserOptions = copyOf(b.allowedParserOptions);
+                       allowedSerializerOptions = 
copyOf(b.allowedSerializerOptions);
+                       noInherit = copyOf(b.noInherit);
                        path = copyOf(b.path);
                        produces = copyOf(b.produces);
                        roleGuard = b.roleGuard;
@@ -607,6 +649,21 @@ public class RestPatchAnnotation {
                        return parsers;
                }
 
+               @Override /* Overridden from RestPatch */
+               public String[] allowedParserOptions() {
+                       return allowedParserOptions;
+               }
+
+               @Override /* Overridden from RestPatch */
+               public String[] allowedSerializerOptions() {
+                       return allowedSerializerOptions;
+               }
+
+               @Override /* Overridden from RestPatch */
+               public String[] noInherit() {
+                       return noInherit;
+               }
+
                @Override /* Overridden from RestPatch */
                public String[] path() {
                        return path;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPost.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPost.java
index e4189c1910..2e40b26efc 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPost.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPost.java
@@ -649,6 +649,56 @@ public @interface RestPost {
         */
        String[] produces() default {};
 
+       /**
+        * Allowed serializer session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Serializer-Options"</js>
+        * header or <js>"juneauSerializerOptions"</js> query parameter.
+        * Merged on top of resource-level {@link 
Rest#allowedSerializerOptions()} values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedSerializerOptions() default {};
+
+       /**
+        * Allowed parser session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Parser-Options"</js>
+        * header or <js>"juneauParserOptions"</js> query parameter.
+        * Merged on top of resource-level {@link Rest#allowedParserOptions()} 
values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedParserOptions() default {};
+
+       /**
+        * Property names for which less-derived contributions are NOT 
inherited.
+        *
+        * <p>
+        * Accepted values: {@code "allowedSerializerOptions"}, {@code 
"allowedParserOptions"}.
+        * Prevents the named property from inheriting values from the 
enclosing {@code @Rest} annotation.
+        * The {@code noInherit} attribute itself is never inherited.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions";>Session Options</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] noInherit() default {};
+
        /**
         * Role guard.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPostAnnotation.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPostAnnotation.java
index 0623c6e7bf..b415dbcf6b 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPostAnnotation.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPostAnnotation.java
@@ -86,6 +86,9 @@ public class RestPostAnnotation {
                private String[] defaultRequestAttributes = {};
                private String[] defaultRequestHeaders = {};
                private String[] defaultResponseHeaders = {};
+               private String[] allowedParserOptions = {};
+               private String[] allowedSerializerOptions = {};
+               private String[] noInherit = {};
                private String[] path = {};
                private String[] produces = {};
 
@@ -329,6 +332,39 @@ public class RestPostAnnotation {
                        return this;
                }
 
+               /**
+                * Sets the {@link RestPost#allowedSerializerOptions()} 
property on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedSerializerOptions(String...value) {
+                       allowedSerializerOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestPost#allowedParserOptions()} property on 
this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedParserOptions(String...value) {
+                       allowedParserOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestPost#noInherit()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder noInherit(String...value) {
+                       noInherit = value;
+                       return this;
+               }
+
                /**
                 * Sets the {@link RestPost#roleGuard()} property on this 
annotation.
                 *
@@ -489,6 +525,9 @@ public class RestPostAnnotation {
                private final String[] defaultRequestAttributes;
                private final String[] defaultRequestHeaders;
                private final String[] defaultResponseHeaders;
+               private final String[] allowedParserOptions;
+               private final String[] allowedSerializerOptions;
+               private final String[] noInherit;
                private final String[] path;
                private final String[] produces;
 
@@ -512,6 +551,9 @@ public class RestPostAnnotation {
                        matchers = copyOf(b.matchers);
                        maxInput = b.maxInput;
                        parsers = copyOf(b.parsers);
+                       allowedParserOptions = copyOf(b.allowedParserOptions);
+                       allowedSerializerOptions = 
copyOf(b.allowedSerializerOptions);
+                       noInherit = copyOf(b.noInherit);
                        path = copyOf(b.path);
                        produces = copyOf(b.produces);
                        roleGuard = b.roleGuard;
@@ -607,6 +649,21 @@ public class RestPostAnnotation {
                        return parsers;
                }
 
+               @Override /* Overridden from RestPost */
+               public String[] allowedParserOptions() {
+                       return allowedParserOptions;
+               }
+
+               @Override /* Overridden from RestPost */
+               public String[] allowedSerializerOptions() {
+                       return allowedSerializerOptions;
+               }
+
+               @Override /* Overridden from RestPost */
+               public String[] noInherit() {
+                       return noInherit;
+               }
+
                @Override /* Overridden from RestPost */
                public String[] path() {
                        return path;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPut.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPut.java
index 5ab32819a5..c34f2713e0 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPut.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPut.java
@@ -649,6 +649,56 @@ public @interface RestPut {
         */
        String[] produces() default {};
 
+       /**
+        * Allowed serializer session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Serializer-Options"</js>
+        * header or <js>"juneauSerializerOptions"</js> query parameter.
+        * Merged on top of resource-level {@link 
Rest#allowedSerializerOptions()} values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedSerializerOptions() default {};
+
+       /**
+        * Allowed parser session option keys for this operation (ordered 
merge, prefix {@code -key} removes a key).
+        *
+        * <p>
+        * Comma-delimited list of session property keys that clients may send 
via the <js>"X-Juneau-Parser-Options"</js>
+        * header or <js>"juneauParserOptions"</js> query parameter.
+        * Merged on top of resource-level {@link Rest#allowedParserOptions()} 
values.
+        * Use {@link #noInherit()} to replace rather than extend the 
resource-level list.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions#safe-properties";>Session
 Options - Safe Properties</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] allowedParserOptions() default {};
+
+       /**
+        * Property names for which less-derived contributions are NOT 
inherited.
+        *
+        * <p>
+        * Accepted values: {@code "allowedSerializerOptions"}, {@code 
"allowedParserOptions"}.
+        * Prevents the named property from inheriting values from the 
enclosing {@code @Rest} annotation.
+        * The {@code noInherit} attribute itself is never inherited.
+        *
+        * <h5 class='section'>See Also:</h5><ul>
+        *      <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/SessionOptions";>Session Options</a>
+        * </ul>
+        *
+        * @return The annotation value.
+        */
+       String[] noInherit() default {};
+
        /**
         * Role guard.
         *
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPutAnnotation.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPutAnnotation.java
index dbaff9eb03..c6b41c7b58 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPutAnnotation.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/RestPutAnnotation.java
@@ -86,6 +86,9 @@ public class RestPutAnnotation {
                private String[] defaultRequestAttributes = {};
                private String[] defaultRequestHeaders = {};
                private String[] defaultResponseHeaders = {};
+               private String[] allowedParserOptions = {};
+               private String[] allowedSerializerOptions = {};
+               private String[] noInherit = {};
                private String[] path = {};
                private String[] produces = {};
 
@@ -329,6 +332,39 @@ public class RestPutAnnotation {
                        return this;
                }
 
+               /**
+                * Sets the {@link RestPut#allowedSerializerOptions()} property 
on this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedSerializerOptions(String...value) {
+                       allowedSerializerOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestPut#allowedParserOptions()} property on 
this annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder allowedParserOptions(String...value) {
+                       allowedParserOptions = value;
+                       return this;
+               }
+
+               /**
+                * Sets the {@link RestPut#noInherit()} property on this 
annotation.
+                *
+                * @param value The new value for this property.
+                * @return This object.
+                */
+               public Builder noInherit(String...value) {
+                       noInherit = value;
+                       return this;
+               }
+
                /**
                 * Sets the {@link RestPut#roleGuard()} property on this 
annotation.
                 *
@@ -489,6 +525,9 @@ public class RestPutAnnotation {
                private final String[] defaultRequestAttributes;
                private final String[] defaultRequestHeaders;
                private final String[] defaultResponseHeaders;
+               private final String[] allowedParserOptions;
+               private final String[] allowedSerializerOptions;
+               private final String[] noInherit;
                private final String[] path;
                private final String[] produces;
 
@@ -512,6 +551,9 @@ public class RestPutAnnotation {
                        matchers = copyOf(b.matchers);
                        maxInput = b.maxInput;
                        parsers = copyOf(b.parsers);
+                       allowedParserOptions = copyOf(b.allowedParserOptions);
+                       allowedSerializerOptions = 
copyOf(b.allowedSerializerOptions);
+                       noInherit = copyOf(b.noInherit);
                        path = copyOf(b.path);
                        produces = copyOf(b.produces);
                        roleGuard = b.roleGuard;
@@ -607,6 +649,21 @@ public class RestPutAnnotation {
                        return parsers;
                }
 
+               @Override /* Overridden from RestPut */
+               public String[] allowedParserOptions() {
+                       return allowedParserOptions;
+               }
+
+               @Override /* Overridden from RestPut */
+               public String[] allowedSerializerOptions() {
+                       return allowedSerializerOptions;
+               }
+
+               @Override /* Overridden from RestPut */
+               public String[] noInherit() {
+                       return noInherit;
+               }
+
                @Override /* Overridden from RestPut */
                public String[] path() {
                        return path;
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/config/BasicUniversalConfig.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/config/BasicUniversalConfig.java
index 5a98c49109..8b97435d97 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/config/BasicUniversalConfig.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/config/BasicUniversalConfig.java
@@ -196,6 +196,47 @@ import org.apache.juneau.xml.*;
                ParquetParser.class,
                ProtoParser.class,
                MarkdownParser.class
+       },
+
+       // Serializer session properties that clients are allowed to override 
via HTTP headers/query parameters.
+       allowedSerializerOptions={
+               "useWhitespace",          // WriterSerializer - enable/disable 
whitespace
+               "maxIndent",              // WriterSerializer - max indentation 
level
+               "quoteChar",              // WriterSerializer - quote character
+               "keepNullProperties",     // SerializerSession - include null 
properties
+               "trimStrings",            // SerializerSession - trim string 
values
+               "addBeanTypes",           // SerializerSession - add bean type 
annotations
+               "addRootType",            // SerializerSession - add root type 
annotation
+               "sortCollections",        // SerializerSession - sort 
collections
+               "sortMaps",               // SerializerSession - sort maps
+               "trimEmptyCollections",   // SerializerSession - trim empty 
collections
+               "trimEmptyMaps",          // SerializerSession - trim empty maps
+               "binaryFormat",           // OutputStreamSerializer - binary 
encoding format
+               "escapeSolidus",          // JsonSerializerSession - escape 
forward slashes
+               "encoding",               // UonSerializerSession - URL-encode 
characters
+               "paramFormat",            // UonSerializerSession - parameter 
format style
+               "addNamespaceUrisToRoot", // XmlSerializerSession - add 
namespace URIs to root
+               "autoDetectNamespaces",   // XmlSerializerSession - auto-detect 
namespaces
+               "enableNamespaces",       // XmlSerializerSession - enable 
namespace support
+               "textNodeDelimiter",      // XmlSerializerSession - text node 
delimiter
+               "addKeyValueTableHeaders",// HtmlSerializerSession - add 
key/value table headers
+               "detectLabelParameters",  // HtmlSerializerSession - detect 
label parameters
+               "detectLinksInStrings",   // HtmlSerializerSession - detect 
links in strings
+               "labelParameter",         // HtmlSerializerSession - label 
parameter name
+               "uriAnchorText",          // HtmlSerializerSession - URI anchor 
text strategy
+               "byteArrayFormat",        // CsvSerializerSession - byte array 
encoding format
+               "allowNestedStructures"   // CsvSerializerSession - allow 
nested structures
+       },
+
+       // Parser session properties that clients are allowed to override via 
HTTP headers/query parameters.
+       allowedParserOptions={
+               "trimStrings",            // ParserSession - trim string values
+               "binaryFormat",           // InputStreamParser - binary 
decoding format
+               "validateEnd",            // JsonParserSession/UonParserSession 
- validate end of input
+               "decoding",               // UonParserSession - URL-decode 
characters
+               "expandedParams",         // UrlEncodingParserSession - 
expanded parameters
+               "preserveRootElement",    // XmlParserSession - preserve root 
element
+               "validating"              // XmlParserSession - validate XML
        }
 )
 public interface BasicUniversalConfig extends DefaultConfig, DefaultHtmlConfig 
{}
\ No newline at end of file
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestContent.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestContent.java
index 2b95e4c0e0..1de14c5d2f 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestContent.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/httppart/RequestContent.java
@@ -551,7 +551,7 @@ public class RequestContent {
                        // @formatter:off
                        var session = p
                                .createSession()
-                               .properties(req.getAttributes().asMap())
+                               .properties(req.getParserSessionPropertyMap())
                                .javaMethod(req.getOpContext().getJavaMethod())
                                .locale(locale)
                                .timeZone(timeZone.orElse(null))
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/processor/SerializedPojoProcessor.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/processor/SerializedPojoProcessor.java
index a00bf9a02f..e10b0eb419 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/processor/SerializedPojoProcessor.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/processor/SerializedPojoProcessor.java
@@ -72,7 +72,7 @@ public class SerializedPojoProcessor implements 
ResponseProcessor {
                                // @formatter:off
                                SerializerSession session = s
                                        .createSession()
-                                       .properties(req.getAttributes().asMap())
+                                       
.properties(req.getSerializerSessionPropertyMap())
                                        
.javaMethod(req.getOpContext().getJavaMethod())
                                        .locale(req.getLocale())
                                        
.timeZone(req.getTimeZone().orElse(null))
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/commons/utils/CollectionUtils_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/CollectionUtils_Test.java
index b3e1234657..45c999ae3f 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/commons/utils/CollectionUtils_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/commons/utils/CollectionUtils_Test.java
@@ -1659,4 +1659,26 @@ class CollectionUtils_Test extends TestBase {
                assertEquals(1, intSet.first());
                assertEquals(3, intSet.last());
        }
+
+       
//====================================================================================================
+       // isEmpty(Collection<?>)
+       
//====================================================================================================
+       @Test
+       void a096_isEmpty_collection() {
+               assertTrue(isEmpty((java.util.Collection<?>)null));
+               assertTrue(isEmpty(list()));
+               assertFalse(isEmpty(list("a")));
+               assertFalse(isEmpty(list("a", "b")));
+       }
+
+       
//====================================================================================================
+       // isEmpty(Map<?,?>)
+       
//====================================================================================================
+       @Test
+       void a097_isEmpty_map() {
+               assertTrue(isEmpty((java.util.Map<?,?>)null));
+               assertTrue(isEmpty(map()));
+               assertFalse(isEmpty(map("k", "v")));
+               assertFalse(isEmpty(map("k1", "v1", "k2", "v2")));
+       }
 }
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/NoInherit_Test.java 
b/juneau-utest/src/test/java/org/apache/juneau/rest/NoInherit_Test.java
new file mode 100644
index 0000000000..1aa0c2d4f8
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/rest/NoInherit_Test.java
@@ -0,0 +1,168 @@
+/*
+ * 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.rest;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.TestBase;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.servlet.BasicRestObject;
+import org.junit.jupiter.api.*;
+
+class NoInherit_Test extends TestBase {
+
+       private static RestContext restContext(Class<? extends BasicRestObject> 
c) throws Exception {
+               var o = c.getDeclaredConstructor().newInstance();
+               
+               // Check if class has a parent REST resource (not 
BasicRestObject)
+               RestContext parentContext = null;
+               var superClass = c.getSuperclass();
+               if (superClass != null && superClass != BasicRestObject.class 
&& BasicRestObject.class.isAssignableFrom(superClass)) {
+                       @SuppressWarnings("unchecked")
+                       var parentClass = (Class<? extends BasicRestObject>) 
superClass;
+                       parentContext = restContext(parentClass);
+               }
+               
+               return RestContext.create(c, parentContext, null).init(() -> 
o).build().postInit().postInitChildFirst();
+       }
+
+       @Rest(allowedSerializerOptions = "parentSer")
+       public static class ParentSer extends BasicRestObject {}
+
+       @Rest(allowedSerializerOptions = "childSer", noInherit = 
"allowedSerializerOptions")
+       public static class ChildSer extends ParentSer {}
+
+       @Test
+       void a01_classNoInherit_skipsParentSerializerAllowlist() throws 
Exception {
+               var ctx = restContext(ChildSer.class);
+               var keys = ctx.getAllowedSerializerOptions();
+
+               assertTrue(keys.contains("childSer"));
+               assertFalse(keys.contains("parentSer"));
+       }
+
+       @Rest(allowedSerializerOptions = "pBoth")
+       public static class ParentBoth extends BasicRestObject {}
+
+       @Rest(allowedSerializerOptions = "cBoth")
+       public static class ChildBoth extends ParentBoth {}
+
+       @Test
+       void a02_withoutNoInherit_mergesParentSerializerAllowlist() throws 
Exception {
+               var ctx = restContext(ChildBoth.class);
+               var keys = ctx.getAllowedSerializerOptions();
+
+               assertTrue(keys.contains("pBoth"));
+               assertTrue(keys.contains("cBoth"));
+       }
+
+       public static class ParentM extends BasicRestObject {
+               @RestGet(allowedSerializerOptions = "parentM")
+               public void get() {
+                       // Intentionally empty - method only used for 
annotation metadata testing
+               }
+       }
+
+       public static class ChildM extends ParentM {
+               @RestGet(allowedSerializerOptions = "childM", noInherit = 
"allowedSerializerOptions")
+               @Override
+               public void get() {
+                       // Intentionally empty - method only used for 
annotation metadata testing
+               }
+       }
+
+       @Test
+       void a03_methodNoInherit_stillInheritsParentMethodSerializerAllowlist() 
throws Exception {
+               var ctx = restContext(ChildM.class);
+               var op = ctx.getRestOperations().getOpContexts().stream()
+                       .filter(o -> 
ChildM.class.equals(o.getJavaMethod().getDeclaringClass()) && 
"get".equals(o.getJavaMethod().getName()))
+                       .findFirst()
+                       .orElseThrow();
+               var keys = op.getAllowedSerializerOptions();
+
+               // Method annotations ALWAYS inherit from parent methods, 
regardless of noInherit
+               // noInherit only blocks inheritance from the REST class context
+               assertTrue(keys.contains("childM"));
+               assertTrue(keys.contains("parentM"));
+       }
+       
+       // Test case for aggregated noInherit: parent has noInherit="prop1", 
child has noInherit="prop2"
+       // The aggregated noInherit should be {"prop1", "prop2"}, but 
allowedSerializerOptions should
+       // include values from both parent and child since neither blocks 
allowedSerializerOptions
+       public static class ParentAggregated extends BasicRestObject {
+               @RestGet(allowedSerializerOptions = "parentOpt", noInherit = 
"prop1")
+               public void get() {
+                       // Intentionally empty - method only used for 
annotation metadata testing
+               }
+       }
+
+       public static class ChildAggregated extends ParentAggregated {
+               @RestGet(allowedSerializerOptions = "childOpt", noInherit = 
"prop2")
+               @Override
+               public void get() {
+                       // Intentionally empty - method only used for 
annotation metadata testing
+               }
+       }
+
+       @Test
+       void a04_aggregatedNoInherit_includesBothParentAndChild() throws 
Exception {
+               var ctx = restContext(ChildAggregated.class);
+               var op = ctx.getRestOperations().getOpContexts().stream()
+                       .filter(o -> 
ChildAggregated.class.equals(o.getJavaMethod().getDeclaringClass()) && 
"get".equals(o.getJavaMethod().getName()))
+                       .findFirst()
+                       .orElseThrow();
+               var keys = op.getAllowedSerializerOptions();
+
+               // Both parent and child options should be present since 
neither has allowedSerializerOptions in noInherit
+               assertTrue(keys.contains("childOpt"));
+               assertTrue(keys.contains("parentOpt"));
+       }
+
+       // Test that noInherit blocks REST class context inheritance but NOT 
parent method inheritance
+       @Rest(allowedSerializerOptions = "classLevel")
+       public static class ParentWithClassLevel extends BasicRestObject {
+               @RestGet(allowedSerializerOptions = "parentMethod")
+               public void get() {
+                       // Intentionally empty - method only used for 
annotation metadata testing
+               }
+       }
+
+       public static class ChildBlocksClassInheritance extends 
ParentWithClassLevel {
+               @RestGet(allowedSerializerOptions = "childMethod", noInherit = 
"allowedSerializerOptions")
+               @Override
+               public void get() {
+                       // Intentionally empty - method only used for 
annotation metadata testing
+               }
+       }
+
+       @Test
+       void a05_methodNoInherit_blocksClassLevelButNotParentMethod() throws 
Exception {
+               var ctx = restContext(ChildBlocksClassInheritance.class);
+               var op = ctx.getRestOperations().getOpContexts().stream()
+                       .filter(o -> 
ChildBlocksClassInheritance.class.equals(o.getJavaMethod().getDeclaringClass()) 
&& "get".equals(o.getJavaMethod().getName()))
+                       .findFirst()
+                       .orElseThrow();
+               var keys = op.getAllowedSerializerOptions();
+
+               // Should include method-level keys from both child and parent 
methods
+               assertTrue(keys.contains("childMethod"));
+               assertTrue(keys.contains("parentMethod"));
+               
+               // Should NOT include class-level key because of noInherit
+               assertFalse(keys.contains("classLevel"));
+       }
+}
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/RestRequest_SessionProperties_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/RestRequest_SessionProperties_Test.java
new file mode 100644
index 0000000000..4fcc05674d
--- /dev/null
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/RestRequest_SessionProperties_Test.java
@@ -0,0 +1,158 @@
+/*
+ * 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.rest;
+
+import java.util.*;
+
+import org.apache.juneau.TestBase;
+import org.apache.juneau.json.*;
+import org.apache.juneau.rest.annotation.*;
+import org.apache.juneau.rest.mock.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Tests for {@link RestRequest} session property methods.
+ */
+class RestRequest_SessionProperties_Test extends TestBase {
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // Basic setter tests
+       
//------------------------------------------------------------------------------------------------------------------
+
+       @Rest(serializers=JsonSerializer.class, parsers=JsonParser.class)
+       public static class A {
+               @RestGet(path="/test")
+               public String test(RestRequest req) {
+                       req.setSerializerSessionProperty("key1", "value1");
+                       var map = req.getSerializerSessionPropertyMap();
+                       return map.get("key1") != null ? "ok" : "fail";
+               }
+       }
+
+       @Test
+       void a01_setSerializerSessionProperty_single() throws Exception {
+               MockRestClient.create(A.class).plainText().build()
+                       .get("/test")
+                       .run()
+                       .assertContent("ok");
+       }
+
+       @Rest(serializers=JsonSerializer.class, parsers=JsonParser.class)
+       public static class B {
+               @RestPost(path="/test")
+               public String test(RestRequest req) {
+                       req.setParserSessionProperty("key1", "value1");
+                       var map = req.getParserSessionPropertyMap();
+                       return map.get("key1") != null ? "ok" : "fail";
+               }
+       }
+
+       @Test
+       void a02_setParserSessionProperty_single() throws Exception {
+               MockRestClient.create(B.class).plainText().build()
+                       .post("/test", "")
+                       .run()
+                       .assertContent("ok");
+       }
+
+       @Rest(serializers=JsonSerializer.class, parsers=JsonParser.class)
+       public static class C {
+               @RestGet(path="/test")
+               public String test(RestRequest req) {
+                       var props = Map.<String,Object>of("key1", "value1", 
"key2", "value2");
+                       req.setSerializerSessionProperties(props);
+                       var map = req.getSerializerSessionPropertyMap();
+                       return map.get("key1") != null && map.get("key2") != 
null ? "ok" : "fail";
+               }
+       }
+
+       @Test
+       void a03_setSerializerSessionProperties_bulk() throws Exception {
+               MockRestClient.create(C.class).plainText().build()
+                       .get("/test")
+                       .run()
+                       .assertContent("ok");
+       }
+
+       @Rest(serializers=JsonSerializer.class, parsers=JsonParser.class)
+       public static class D {
+               @RestPost(path="/test")
+               public String test(RestRequest req) {
+                       var props = Map.<String,Object>of("key1", "value1", 
"key2", "value2");
+                       req.setParserSessionProperties(props);
+                       var map = req.getParserSessionPropertyMap();
+                       return map.get("key1") != null && map.get("key2") != 
null ? "ok" : "fail";
+               }
+       }
+
+       @Test
+       void a04_setParserSessionProperties_bulk() throws Exception {
+               MockRestClient.create(D.class).plainText().build()
+                       .post("/test", "")
+                       .run()
+                       .assertContent("ok");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // Method chaining
+       
//------------------------------------------------------------------------------------------------------------------
+
+       @Rest(serializers=JsonSerializer.class, parsers=JsonParser.class)
+       public static class E {
+               @RestGet(path="/test")
+               public String test(RestRequest req) {
+                       req.setSerializerSessionProperty("key1", "value1")
+                               .setSerializerSessionProperty("key2", "value2");
+                       var map = req.getSerializerSessionPropertyMap();
+                       return map.get("key1") != null && map.get("key2") != 
null ? "ok" : "fail";
+               }
+       }
+
+       @Test
+       void b01_methodChaining_serializer() throws Exception {
+               MockRestClient.create(E.class).plainText().build()
+                       .get("/test")
+                       .run()
+                       .assertContent("ok");
+       }
+
+       
//------------------------------------------------------------------------------------------------------------------
+       // Integration with HTTP options (query/header) and programmatic 
override
+       
//------------------------------------------------------------------------------------------------------------------
+
+       @Rest(serializers=JsonSerializer.class, parsers=JsonParser.class)
+       public static class G {
+               @RestGet(path="/test", 
allowedSerializerOptions="useWhitespace,customKey", 
allowedParserOptions="strict,customKey")
+               public String test(RestRequest req) {
+                       req.setSerializerSessionProperty("customKey", 
"customValue");
+                       req.setParserSessionProperty("customKey", 
"customValue");
+                       
+                       var smap = req.getSerializerSessionPropertyMap();
+                       var pmap = req.getParserSessionPropertyMap();
+                       
+                       return "s:" + smap.get("customKey") + ",p:" + 
pmap.get("customKey");
+               }
+       }
+
+       @Test
+       void c01_integration_programmatic_override() throws Exception {
+               MockRestClient.create(G.class).plainText().build()
+                       .get("/test")
+                       .run()
+                       .assertContent("s:customValue,p:customValue");
+       }
+}
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestAnnotation_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestAnnotation_Test.java
index afc4ef9fd1..e4eefba02a 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestAnnotation_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestAnnotation_Test.java
@@ -49,6 +49,9 @@ class RestAnnotation_Test extends TestBase {
        
//------------------------------------------------------------------------------------------------------------------
 
        Rest a1 = RestAnnotation.create()
+               .allowedParserOptions("e1")
+               .allowedSerializerOptions("e2")
+               .noInherit("e3")
                .disableContentParam("a")
                .allowedHeaderParams("b")
                .allowedMethodHeaders("c")
@@ -100,6 +103,9 @@ class RestAnnotation_Test extends TestBase {
                .build();
 
        Rest a2 = RestAnnotation.create()
+               .allowedParserOptions("e1")
+               .allowedSerializerOptions("e2")
+               .noInherit("e3")
                .disableContentParam("a")
                .allowedHeaderParams("b")
                .allowedMethodHeaders("c")
@@ -152,8 +158,8 @@ class RestAnnotation_Test extends TestBase {
 
        @Test void a01_basic() {
                assertBean(a1,
-                       
"allowedHeaderParams,allowedMethodHeaders,allowedMethodParams,beanStore,callLogger,children,clientVersionHeader,config,consumes,converters,debug,debugEnablement,debugOn,defaultAccept,defaultCharset,defaultContentType,defaultRequestAttributes,defaultRequestHeaders,defaultResponseHeaders,description,disableContentParam,encoders,guards,maxInput,messages,on,onClass,parsers,partParser,partSerializer,path,produces,renderResponseStackTraces,responseProcessors,restChildrenClass,restOpArgs,rol
 [...]
-                       
"b,c,d,BasicBeanStore,CallLogger,[RestAnnotation_Test],e,f,[g],[RestConverter],h,DebugEnablement,i,j,k,l,[m],[n],[o],[p],a,[Encoder],[RestGuard],q,r,[s],[RestAnnotation_Test],[Parser],HttpPartParser,HttpPartSerializer,t,[u],v,[ResponseProcessor],RestChildren,[RestOpArg],w,x,[Serializer],y,StaticFiles,{{[],,,},[],{[],},{[],,},[],[],[],[],},BasicSwaggerProvider,[z],aa,bb,cc,dd");
+                       
"allowedHeaderParams,allowedMethodHeaders,allowedMethodParams,allowedParserOptions,allowedSerializerOptions,beanStore,callLogger,children,clientVersionHeader,config,consumes,converters,debug,debugEnablement,debugOn,defaultAccept,defaultCharset,defaultContentType,defaultRequestAttributes,defaultRequestHeaders,defaultResponseHeaders,description,disableContentParam,encoders,guards,maxInput,messages,noInherit,on,onClass,parsers,partParser,partSerializer,path,produces,renderResponseStackTr
 [...]
+                       
"b,c,d,[e1],[e2],BasicBeanStore,CallLogger,[RestAnnotation_Test],e,f,[g],[RestConverter],h,DebugEnablement,i,j,k,l,[m],[n],[o],[p],a,[Encoder],[RestGuard],q,r,[e3],[s],[RestAnnotation_Test],[Parser],HttpPartParser,HttpPartSerializer,t,[u],v,[ResponseProcessor],RestChildren,[RestOpArg],w,x,[Serializer],y,StaticFiles,{{[],,,},[],{[],},{[],,},[],[],[],[],},BasicSwaggerProvider,[z],aa,bb,cc,dd");
        }
 
        @Test void a02_testEquivalency() {
@@ -198,6 +204,9 @@ class RestAnnotation_Test extends TestBase {
        
//------------------------------------------------------------------------------------------------------------------
 
        @Rest(
+               allowedParserOptions="e1",
+               allowedSerializerOptions="e2",
+               noInherit="e3",
                disableContentParam="a",
                allowedHeaderParams="b",
                allowedMethodHeaders="c",
@@ -251,6 +260,9 @@ class RestAnnotation_Test extends TestBase {
        Rest d1 = D1.class.getAnnotationsByType(Rest.class)[0];
 
        @Rest(
+               allowedParserOptions="e1",
+               allowedSerializerOptions="e2",
+               noInherit="e3",
                disableContentParam="a",
                allowedHeaderParams="b",
                allowedMethodHeaders="c",
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestDeleteAnnotation_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestDeleteAnnotation_Test.java
index c8d9f9e4d6..41b07ac84d 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestDeleteAnnotation_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestDeleteAnnotation_Test.java
@@ -38,6 +38,9 @@ class RestDeleteAnnotation_Test extends TestBase {
        
//------------------------------------------------------------------------------------------------------------------
 
        RestDelete a1 = RestDeleteAnnotation.create()
+               .allowedParserOptions("p1")
+               .allowedSerializerOptions("p2")
+               .noInherit("p3")
                .clientVersion("a")
                .debug("b")
                .defaultAccept("c")
@@ -60,6 +63,9 @@ class RestDeleteAnnotation_Test extends TestBase {
                .build();
 
        RestDelete a2 = RestDeleteAnnotation.create()
+               .allowedParserOptions("p1")
+               .allowedSerializerOptions("p2")
+               .noInherit("p3")
                .clientVersion("a")
                .debug("b")
                .defaultAccept("c")
@@ -83,8 +89,8 @@ class RestDeleteAnnotation_Test extends TestBase {
 
        @Test void a01_basic() {
                assertBean(a1,
-                       
"clientVersion,debug,defaultAccept,defaultCharset,defaultRequestAttributes,defaultRequestHeaders,defaultRequestQueryData,defaultResponseHeaders,description,encoders,guards,matchers,on,path,roleGuard,rolesDeclared,summary,swagger{consumes,deprecated,description,externalDocs{description,url},operationId,parameters,produces,responses,schemes,summary,tags,value},value",
-                       
"a,b,c,d,[f],[g],[e],[h],[i],[Encoder],[RestGuard],[RestMatcher],[j],[k],l,m,n,{[],,[],{[],},,[],[],[],[],[],[],[]},o");
+                       
"allowedParserOptions,allowedSerializerOptions,clientVersion,debug,defaultAccept,defaultCharset,defaultRequestAttributes,defaultRequestHeaders,defaultRequestQueryData,defaultResponseHeaders,description,encoders,guards,matchers,noInherit,on,path,roleGuard,rolesDeclared,summary,swagger{consumes,deprecated,description,externalDocs{description,url},operationId,parameters,produces,responses,schemes,summary,tags,value},value",
+                       
"[p1],[p2],a,b,c,d,[f],[g],[e],[h],[i],[Encoder],[RestGuard],[RestMatcher],[p3],[j],[k],l,m,n,{[],,[],{[],},,[],[],[],[],[],[],[]},o");
        }
 
        @Test void a02_testEquivalency() {
@@ -129,6 +135,9 @@ class RestDeleteAnnotation_Test extends TestBase {
        public interface D1 {
 
                @RestDelete(
+                       allowedParserOptions="p1",
+                       allowedSerializerOptions="p2",
+                       noInherit="p3",
                        clientVersion="a",
                        debug="b",
                        defaultAccept="c",
@@ -152,6 +161,9 @@ class RestDeleteAnnotation_Test extends TestBase {
                void m1();
 
                @RestDelete(
+                       allowedParserOptions="p1",
+                       allowedSerializerOptions="p2",
+                       noInherit="p3",
                        clientVersion="a",
                        debug="b",
                        defaultAccept="c",
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestGetAnnotation_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestGetAnnotation_Test.java
index 39007815c3..98fc623d72 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestGetAnnotation_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestGetAnnotation_Test.java
@@ -40,6 +40,9 @@ class RestGetAnnotation_Test extends TestBase {
        
//------------------------------------------------------------------------------------------------------------------
 
        RestGet a1 = RestGetAnnotation.create()
+               .allowedParserOptions("q1")
+               .allowedSerializerOptions("q2")
+               .noInherit("q3")
                .clientVersion("a")
                .converters(RestConverter.class)
                .debug("b")
@@ -65,6 +68,9 @@ class RestGetAnnotation_Test extends TestBase {
                .build();
 
        RestGet a2 = RestGetAnnotation.create()
+               .allowedParserOptions("q1")
+               .allowedSerializerOptions("q2")
+               .noInherit("q3")
                .clientVersion("a")
                .converters(RestConverter.class)
                .debug("b")
@@ -91,8 +97,8 @@ class RestGetAnnotation_Test extends TestBase {
 
        @Test void a01_basic() {
                assertBean(a1,
-                       
"clientVersion,converters,debug,defaultAccept,defaultCharset,defaultRequestAttributes,defaultRequestHeaders,defaultRequestQueryData,defaultResponseHeaders,description,encoders,guards,matchers,on,path,produces,roleGuard,rolesDeclared,serializers,summary,swagger{consumes,deprecated,description,externalDocs{description,url},operationId,parameters,produces,responses,schemes,summary,tags,value},value",
-                       
"a,[RestConverter],b,c,d,[f],[g],[e],[h],[i],[Encoder],[RestGuard],[RestMatcher],[j],[k],[l],m,n,[Serializer],o,{[],,[],{[],},,[],[],[],[],[],[],[]},p");
+                       
"allowedParserOptions,allowedSerializerOptions,clientVersion,converters,debug,defaultAccept,defaultCharset,defaultRequestAttributes,defaultRequestHeaders,defaultRequestQueryData,defaultResponseHeaders,description,encoders,guards,matchers,noInherit,on,path,produces,roleGuard,rolesDeclared,serializers,summary,swagger{consumes,deprecated,description,externalDocs{description,url},operationId,parameters,produces,responses,schemes,summary,tags,value},value",
+                       
"[q1],[q2],a,[RestConverter],b,c,d,[f],[g],[e],[h],[i],[Encoder],[RestGuard],[RestMatcher],[q3],[j],[k],[l],m,n,[Serializer],o,{[],,[],{[],},,[],[],[],[],[],[],[]},p");
        }
 
        @Test void a02_testEquivalency() {
@@ -137,6 +143,9 @@ class RestGetAnnotation_Test extends TestBase {
        public interface D1 {
 
                @RestGet(
+                       allowedParserOptions="q1",
+                       allowedSerializerOptions="q2",
+                       noInherit="q3",
                        clientVersion="a",
                        converters=RestConverter.class,
                        debug="b",
@@ -163,6 +172,9 @@ class RestGetAnnotation_Test extends TestBase {
                void m1();
 
                @RestGet(
+                       allowedParserOptions="q1",
+                       allowedSerializerOptions="q2",
+                       noInherit="q3",
                        clientVersion="a",
                        converters=RestConverter.class,
                        debug="b",
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestOpAnnotation_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestOpAnnotation_Test.java
index e44ed91c05..2fec081b9b 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestOpAnnotation_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestOpAnnotation_Test.java
@@ -41,6 +41,9 @@ class RestOpAnnotation_Test extends TestBase {
        
//------------------------------------------------------------------------------------------------------------------
 
        RestOp a1 = RestOpAnnotation.create()
+               .allowedParserOptions("v1")
+               .allowedSerializerOptions("v2")
+               .noInherit("v3")
                .clientVersion("a")
                .consumes("b")
                .converters(RestConverter.class)
@@ -72,6 +75,9 @@ class RestOpAnnotation_Test extends TestBase {
                .build();
 
        RestOp a2 = RestOpAnnotation.create()
+               .allowedParserOptions("v1")
+               .allowedSerializerOptions("v2")
+               .noInherit("v3")
                .clientVersion("a")
                .consumes("b")
                .converters(RestConverter.class)
@@ -104,8 +110,8 @@ class RestOpAnnotation_Test extends TestBase {
 
        @Test void a01_basic() {
                assertBean(a1,
-                       
"clientVersion,consumes,converters,debug,defaultAccept,defaultCharset,defaultContentType,defaultRequestAttributes,defaultRequestFormData,defaultRequestHeaders,defaultRequestQueryData,defaultResponseHeaders,description,encoders,guards,matchers,maxInput,method,on,parsers,path,produces,roleGuard,rolesDeclared,serializers,summary,swagger{consumes,deprecated,description,externalDocs{description,url},operationId,parameters,produces,responses,schemes,summary,tags,value},value",
-                       
"a,[b],[RestConverter],c,d,e,f,[i],[g],[j],[h],[k],[l],[Encoder],[RestGuard],[RestMatcher],m,n,[o],[Parser],[p],[q],r,s,[Serializer],t,{[],,[],{[],},,[],[],[],[],[],[],[]},u");
+                       
"allowedParserOptions,allowedSerializerOptions,clientVersion,consumes,converters,debug,defaultAccept,defaultCharset,defaultContentType,defaultRequestAttributes,defaultRequestFormData,defaultRequestHeaders,defaultRequestQueryData,defaultResponseHeaders,description,encoders,guards,matchers,maxInput,method,noInherit,on,parsers,path,produces,roleGuard,rolesDeclared,serializers,summary,swagger{consumes,deprecated,description,externalDocs{description,url},operationId,parameters,produces,res
 [...]
+                       
"[v1],[v2],a,[b],[RestConverter],c,d,e,f,[i],[g],[j],[h],[k],[l],[Encoder],[RestGuard],[RestMatcher],m,n,[v3],[o],[Parser],[p],[q],r,s,[Serializer],t,{[],,[],{[],},,[],[],[],[],[],[],[]},u");
        }
 
        @Test void a02_testEquivalency() {
@@ -150,6 +156,9 @@ class RestOpAnnotation_Test extends TestBase {
        public interface D1 {
 
                @RestOp(
+                       allowedParserOptions="v1",
+                       allowedSerializerOptions="v2",
+                       noInherit="v3",
                        clientVersion="a",
                        consumes="b",
                        converters=RestConverter.class,
@@ -182,6 +191,9 @@ class RestOpAnnotation_Test extends TestBase {
                void m1();
 
                @RestOp(
+                       allowedParserOptions="v1",
+                       allowedSerializerOptions="v2",
+                       noInherit="v3",
                        clientVersion="a",
                        consumes="b",
                        converters=RestConverter.class,
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestPostAnnotation_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestPostAnnotation_Test.java
index 8ed6188fd7..54b27b2e05 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestPostAnnotation_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestPostAnnotation_Test.java
@@ -41,6 +41,9 @@ class RestPostAnnotation_Test extends TestBase {
        
//------------------------------------------------------------------------------------------------------------------
 
        RestPost a1 = RestPostAnnotation.create()
+               .allowedParserOptions("u1")
+               .allowedSerializerOptions("u2")
+               .noInherit("u3")
                .clientVersion("a")
                .consumes("b")
                .converters(RestConverter.class)
@@ -71,6 +74,9 @@ class RestPostAnnotation_Test extends TestBase {
                .build();
 
        RestPost a2 = RestPostAnnotation.create()
+               .allowedParserOptions("u1")
+               .allowedSerializerOptions("u2")
+               .noInherit("u3")
                .clientVersion("a")
                .consumes("b")
                .converters(RestConverter.class)
@@ -102,8 +108,8 @@ class RestPostAnnotation_Test extends TestBase {
 
        @Test void a01_basic() {
                assertBean(a1,
-                       
"clientVersion,consumes,converters,debug,defaultAccept,defaultCharset,defaultContentType,defaultRequestAttributes,defaultRequestFormData,defaultRequestHeaders,defaultRequestQueryData,defaultResponseHeaders,description,encoders,guards,matchers,maxInput,on,parsers,path,produces,roleGuard,rolesDeclared,serializers,summary,swagger{consumes,deprecated,description,externalDocs{description,url},operationId,parameters,produces,responses,schemes,summary,tags,value},value",
-                       
"a,[b],[RestConverter],c,d,e,f,[i],[g],[j],[h],[k],[l],[Encoder],[RestGuard],[RestMatcher],m,[n],[Parser],[o],[p],q,r,[Serializer],s,{[],,[],{[],},,[],[],[],[],[],[],[]},t");
+                       
"allowedParserOptions,allowedSerializerOptions,clientVersion,consumes,converters,debug,defaultAccept,defaultCharset,defaultContentType,defaultRequestAttributes,defaultRequestFormData,defaultRequestHeaders,defaultRequestQueryData,defaultResponseHeaders,description,encoders,guards,matchers,maxInput,noInherit,on,parsers,path,produces,roleGuard,rolesDeclared,serializers,summary,swagger{consumes,deprecated,description,externalDocs{description,url},operationId,parameters,produces,responses,
 [...]
+                       
"[u1],[u2],a,[b],[RestConverter],c,d,e,f,[i],[g],[j],[h],[k],[l],[Encoder],[RestGuard],[RestMatcher],m,[u3],[n],[Parser],[o],[p],q,r,[Serializer],s,{[],,[],{[],},,[],[],[],[],[],[],[]},t");
        }
 
        @Test void a02_testEquivalency() {
@@ -148,6 +154,9 @@ class RestPostAnnotation_Test extends TestBase {
        public interface D1 {
 
                @RestPost(
+                       allowedParserOptions="u1",
+                       allowedSerializerOptions="u2",
+                       noInherit="u3",
                        clientVersion="a",
                        consumes="b",
                        converters=RestConverter.class,
@@ -179,6 +188,9 @@ class RestPostAnnotation_Test extends TestBase {
                void m1();
 
                @RestPost(
+                       allowedParserOptions="u1",
+                       allowedSerializerOptions="u2",
+                       noInherit="u3",
                        clientVersion="a",
                        consumes="b",
                        converters=RestConverter.class,
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestPutAnnotation_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestPutAnnotation_Test.java
index 97bafdd1a2..b6f1a433b2 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestPutAnnotation_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/annotation/RestPutAnnotation_Test.java
@@ -41,6 +41,9 @@ class RestPutAnnotation_Test extends TestBase {
        
//------------------------------------------------------------------------------------------------------------------
 
        RestPut a1 = RestPutAnnotation.create()
+               .allowedParserOptions("u1")
+               .allowedSerializerOptions("u2")
+               .noInherit("u3")
                .clientVersion("a")
                .consumes("b")
                .converters(RestConverter.class)
@@ -71,6 +74,9 @@ class RestPutAnnotation_Test extends TestBase {
                .build();
 
        RestPut a2 = RestPutAnnotation.create()
+               .allowedParserOptions("u1")
+               .allowedSerializerOptions("u2")
+               .noInherit("u3")
                .clientVersion("a")
                .consumes("b")
                .converters(RestConverter.class)
@@ -102,8 +108,8 @@ class RestPutAnnotation_Test extends TestBase {
 
        @Test void a01_basic() {
                assertBean(a1,
-                       
"clientVersion,consumes,converters,debug,defaultAccept,defaultCharset,defaultContentType,defaultRequestAttributes,defaultRequestFormData,defaultRequestHeaders,defaultRequestQueryData,defaultResponseHeaders,description,encoders,guards,matchers,maxInput,on,parsers,path,produces,roleGuard,rolesDeclared,serializers,summary,swagger{consumes,deprecated,description,externalDocs{description,url},operationId,parameters,produces,responses,schemes,summary,tags,value},value",
-                       
"a,[b],[RestConverter],c,d,e,f,[i],[g],[j],[h],[k],[l],[Encoder],[RestGuard],[RestMatcher],m,[n],[Parser],[o],[p],q,r,[Serializer],s,{[],,[],{[],},,[],[],[],[],[],[],[]},t");
+                       
"allowedParserOptions,allowedSerializerOptions,clientVersion,consumes,converters,debug,defaultAccept,defaultCharset,defaultContentType,defaultRequestAttributes,defaultRequestFormData,defaultRequestHeaders,defaultRequestQueryData,defaultResponseHeaders,description,encoders,guards,matchers,maxInput,noInherit,on,parsers,path,produces,roleGuard,rolesDeclared,serializers,summary,swagger{consumes,deprecated,description,externalDocs{description,url},operationId,parameters,produces,responses,
 [...]
+                       
"[u1],[u2],a,[b],[RestConverter],c,d,e,f,[i],[g],[j],[h],[k],[l],[Encoder],[RestGuard],[RestMatcher],m,[u3],[n],[Parser],[o],[p],q,r,[Serializer],s,{[],,[],{[],},,[],[],[],[],[],[],[]},t");
        }
 
        @Test void a02_testEquivalency() {
@@ -148,6 +154,9 @@ class RestPutAnnotation_Test extends TestBase {
        public interface D1 {
 
                @RestPut(
+                       allowedParserOptions="u1",
+                       allowedSerializerOptions="u2",
+                       noInherit="u3",
                        clientVersion="a",
                        consumes="b",
                        converters=RestConverter.class,
@@ -179,6 +188,9 @@ class RestPutAnnotation_Test extends TestBase {
                void m1();
 
                @RestPut(
+                       allowedParserOptions="u1",
+                       allowedSerializerOptions="u2",
+                       noInherit="u3",
                        clientVersion="a",
                        consumes="b",
                        converters=RestConverter.class,
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Headers_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Headers_Test.java
index d8280b9edf..99e8caed29 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Headers_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Headers_Test.java
@@ -22,8 +22,10 @@ import static org.apache.juneau.TestUtils.*;
 import static org.apache.juneau.commons.utils.CollectionUtils.*;
 import static org.apache.juneau.http.HttpHeaders.*;
 import static org.apache.juneau.httppart.HttpPartSchema.*;
+import static org.apache.juneau.rest.RestSharedConstants.*;
 
 import java.time.*;
+import java.util.Map;
 
 import org.apache.juneau.*;
 import org.apache.juneau.http.header.*;
@@ -69,7 +71,7 @@ public class RestClient_Headers_Test extends TestBase {
 
        @Rest(callLogger=CaptureLogger.class)
        public static class A extends BasicRestObject {
-               @RestGet
+               @RestGet(allowedSerializerOptions="simple", 
allowedParserOptions="addBeanTypes")
                public String[] headers(org.apache.juneau.rest.RestRequest req) 
{
                        return 
req.getHeaders().getAll(req.getHeaderParam("Check").orElse(null)).stream().map(RequestHeader::getValue).toArray(String[]::new);
                }
@@ -82,6 +84,12 @@ public class RestClient_Headers_Test extends TestBase {
        // Method tests
        
//------------------------------------------------------------------------------------------------------------------
 
+       @Test void a00_sessionOptionHeaders() throws Exception {
+               
checkFooClient().serializerSessionOptionsHeader("{simple:true}").build().get("/headers").header("Check",
 HEADER_JuneauSerializerOptions).run().assertContent("[\"{simple:true}\"]");
+               
checkFooClient().parserSessionOptionsHeader("{addBeanTypes:false}").build().get("/headers").header("Check",
 HEADER_JuneauParserOptions).run().assertContent("[\"{addBeanTypes:false}\"]");
+               
checkFooClient().build().get("/headers").serializerSessionOptionsHeader(Map.of("simple",
 true)).header("Check", 
HEADER_JuneauSerializerOptions).run().assertContent().asString().isContains("simple");
+       }
+
        @Test void a01_header_String_Object() throws Exception {
                
checkFooClient().header("Foo","bar").build().get("/headers").run().assertContent("[\"bar\"]");
                
checkFooClient().build().get("/headers").header("Foo","baz").run().assertContent("[\"baz\"]");
diff --git 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Query_Test.java
 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Query_Test.java
index a8b5fc2964..ef3de9484b 100644
--- 
a/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Query_Test.java
+++ 
b/juneau-utest/src/test/java/org/apache/juneau/rest/client/RestClient_Query_Test.java
@@ -20,6 +20,7 @@ import static org.apache.juneau.TestUtils.*;
 import static org.apache.juneau.commons.utils.CollectionUtils.*;
 import static org.apache.juneau.http.HttpParts.*;
 import static org.apache.juneau.httppart.HttpPartSchema.*;
+import static org.apache.juneau.rest.RestSharedConstants.*;
 
 import java.io.*;
 
@@ -50,6 +51,15 @@ class RestClient_Query_Test extends TestBase {
        // Method tests
        
//------------------------------------------------------------------------------------------------------------------
 
+       @Test void a00_sessionOptionQuery() throws Exception {
+               
client().serializerSessionOptionsQueryDefault("(simple=true)").build().get("/query").run().assertContent().asString().asUrlDecode()
+                       .isContains(QUERY_juneauSerializerOptions + 
"=(simple=true)");
+               
client().parserSessionOptionsQueryDefault("(strict=true)").build().get("/query").run().assertContent().asString().asUrlDecode()
+                       .isContains(QUERY_juneauParserOptions + 
"=(strict=true)");
+               
client().build().get("/query").serializerSessionOptionsQuery("(a=1)").run().assertContent().asString().asUrlDecode()
+                       .isContains(QUERY_juneauSerializerOptions + "=(a=1)");
+       }
+
        @Test void a01_query_String_Object() throws Exception {
                client().queryData("foo","bar").queryData(part("foo",new 
StringBuilder("baz"),null)).build().get("/query").run().assertContent("foo=bar&foo=baz");
                
client().build().get("/query").queryData("foo","bar").run().assertContent().isContains("foo=bar");


Reply via email to