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 ecd9319a19 Add Optional-returning accessors to 
SerializerSet/ParserSet; opt/opte cleanup; harden petstore Spring Boot test
ecd9319a19 is described below

commit ecd9319a1925c3d82c4e1eaaf0c5e221c857029e
Author: James Bognar <[email protected]>
AuthorDate: Mon Jun 29 08:00:17 2026 -0400

    Add Optional-returning accessors to SerializerSet/ParserSet; opt/opte 
cleanup; harden petstore Spring Boot test
    
    - TODO-190: SerializerSet/ParserSet and RestClient expose Optional-based
      getSerializerMatch/getParserMatch accessors.
    - Convert remaining Optional.empty()/Optional.ofNullable(...) to 
opte()/opt() helpers.
    - Add cold-start warm-up + generous timeouts to PetstoreSpringboot_Test to 
fix CI flake.
    
    Co-authored-by: Cursor <[email protected]>
---
 .../apache/juneau/marshall/parser/ParserSet.java   | 38 ++++-----
 .../juneau/marshall/serializer/SerializerSet.java  | 60 +++++++-------
 .../juneau/marshall/parser/ParserSet_Test.java     | 48 +++++------
 .../marshall/serializer/SerializerSet_Test.java    | 82 +++++++++----------
 .../juneau/marshall/transforms/ObjectSwapTest.java |  6 +-
 .../juneau/rest/client/RemoteClient_Test.java      |  5 +-
 .../client/RemoteProxy_FeatureParity_Test.java     | 69 ++++++++++++----
 .../client/RemoteProxy_NextGenParityGaps_Test.java |  4 +-
 .../rest/client/RestClientFeatures_Test.java       |  3 +
 .../mixin/MixinInheritance_NoInherit_Test.java     |  8 +-
 .../rest/mixin/MixinInheritance_Parsers_Test.java  | 18 ++--
 .../mixin/MixinInheritance_Serializers_Test.java   | 10 +--
 .../springboot/PetstoreSpringboot_Test.java        | 52 +++++++++++-
 .../juneau/rest/client/classic/RestClient.java     |  4 +-
 .../apache/juneau/rest/client/ResponseBody.java    | 17 ++--
 .../org/apache/juneau/rest/client/RestClient.java  | 88 ++++++++++++--------
 .../org/apache/juneau/rest/client/RestRequest.java | 10 ++-
 .../juneau/rest/client/remote/RemoteClient.java    | 34 ++++----
 .../rest/client/ResponseBody_Cursor_Test.java      |  4 +-
 .../rest/client/RestClient_Negotiation_Test.java   | 95 +++++++++++++++-------
 .../apache/juneau/rest/mock/MockRestClient.java    |  2 +
 .../rest/mock/NextGenContentNegotiation_Test.java  |  3 +-
 .../mock/RemoteCursorBinding_NextGen_Test.java     | 29 ++++---
 .../apache/juneau/rest/server/RestResponse.java    |  2 +-
 .../rest/server/httppart/RequestContent.java       |  2 +-
 .../swagger/BasicSwaggerProviderSession.java       |  2 +-
 .../rest/server/vars/SerializedRequestAttrVar.java |  2 +-
 .../rest/server/vars/RestServerVars_Test.java      |  5 +-
 28 files changed, 433 insertions(+), 269 deletions(-)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/parser/ParserSet.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/parser/ParserSet.java
index db1becf055..bac451b243 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/parser/ParserSet.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/parser/ParserSet.java
@@ -70,7 +70,7 @@ import org.apache.juneau.marshall.*;
  *             .build();
  *
  *     <jc>// Find the appropriate parser by Content-Type</jc>
- *     ReaderParser <jv>parser</jv> = 
(ReaderParser)<jv>parsers</jv>.getParser(<js>"text/json"</js>);
+ *     ReaderParser <jv>parser</jv> = 
(ReaderParser)<jv>parsers</jv>.getParser(<js>"text/json"</js>).orElseThrow();  
<jc>// Throws if no parser matched the Content-Type.</jc>
  *
  *     <jc>// Parse a bean from JSON</jc>
  *     String <jv>json</jv> = <js>"{...}"</js>;
@@ -561,33 +561,31 @@ public class ParserSet {
        /**
         * Same as {@link #getParserMatch(MediaType)} but returns just the 
matched parser.
         *
-        * @param mediaType The HTTP media type.
-        * @return The parser that matched the media type, or <jk>null</jk> if 
no match was made.
+        * @param mediaType The HTTP media type.  Can be <jk>null</jk>.
+        * @return The parser that matched the media type, or {@link 
Optional#empty()} if no match was made.
         */
-       public Parser getParser(MediaType mediaType) {
-               ParserMatch pm = getParserMatch(mediaType);
-               return pm == null ? null : pm.getParser();
+       public Optional<Parser> getParser(MediaType mediaType) {
+               return getParserMatch(mediaType).map(ParserMatch::getParser);
        }
 
        /**
         * Same as {@link #getParserMatch(String)} but returns just the matched 
parser.
         *
         * @param contentTypeHeader The HTTP <l>Content-Type</l> header string.
-        * @return The parser that matched the content type header, or 
<jk>null</jk> if no match was made.
+        * @return The parser that matched the content type header, or {@link 
Optional#empty()} if no match was made.
         */
-       public Parser getParser(String contentTypeHeader) {
-               ParserMatch pm = getParserMatch(contentTypeHeader);
-               return pm == null ? null : pm.getParser();
+       public Optional<Parser> getParser(String contentTypeHeader) {
+               return 
getParserMatch(contentTypeHeader).map(ParserMatch::getParser);
        }
 
        /**
         * Same as {@link #getParserMatch(String)} but matches using a {@link 
MediaType} instance.
         *
-        * @param mediaType The HTTP <l>Content-Type</l> header value as a 
media type.
-        * @return The parser and media type that matched the media type, or 
<jk>null</jk> if no match was made.
+        * @param mediaType The HTTP <l>Content-Type</l> header value as a 
media type.  Can be <jk>null</jk>.
+        * @return The parser and media type that matched the media type, or 
{@link Optional#empty()} if no match was made.
         */
-       public ParserMatch getParserMatch(MediaType mediaType) {
-               return getParserMatch(mediaType.toString());
+       public Optional<ParserMatch> getParserMatch(MediaType mediaType) {
+               return mediaType == null ? opte() : 
getParserMatch(mediaType.toString());
        }
 
        /**
@@ -596,13 +594,15 @@ public class ParserSet {
         * <p>
         * The returned object includes both the parser and media type that 
matched.
         *
-        * @param contentTypeHeader The HTTP <l>Content-Type</l> header value.
-        * @return The parser and media type that matched the content type 
header, or <jk>null</jk> if no match was made.
+        * @param contentTypeHeader The HTTP <l>Content-Type</l> header value.  
Can be <jk>null</jk>.
+        * @return The parser and media type that matched the content type 
header, or {@link Optional#empty()} if no match was made.
         */
-       public ParserMatch getParserMatch(String contentTypeHeader) {
+       public Optional<ParserMatch> getParserMatch(String contentTypeHeader) {
+               if (contentTypeHeader == null)
+                       return opte();
                ParserMatch pm = cache.get(contentTypeHeader);
                if (nn(pm))
-                       return pm;
+                       return opt(pm);
 
                var ct = MediaType.of(contentTypeHeader);
                int match = ct.match(l(mediaTypes));
@@ -612,7 +612,7 @@ public class ParserSet {
                        cache.putIfAbsent(contentTypeHeader, pm);
                }
 
-               return cache.get(contentTypeHeader);
+               return opt(cache.get(contentTypeHeader));
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/SerializerSet.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/SerializerSet.java
index cb6c30bed8..ce53466f28 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/SerializerSet.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/SerializerSet.java
@@ -69,7 +69,8 @@ import org.apache.juneau.marshall.*;
  *
  *     <jc>// Find the appropriate serializer by Accept type</jc>
  *     WriterSerializer <jv>serializer</jv> = <jv>serializers</jv>
- *             .getWriterSerializer(<js>"text/foo, text/json;q=0.8, 
text/*;q:0.6, *\/*;q=0.0"</js>);
+ *             .getWriterSerializer(<js>"text/foo, text/json;q=0.8, 
text/*;q:0.6, *\/*;q=0.0"</js>)
+ *             .orElseThrow();  <jc>// Throws if no serializer matched the 
Accept header.</jc>
  *
  *     <jc>// Serialize a bean to JSON text </jc>
  *     AddressBook <jv>addressBook</jv> = <jk>new</jk> AddressBook();  <jc>// 
Bean to serialize.</jc>
@@ -567,33 +568,30 @@ public class SerializerSet {
         * Same as {@link #getSerializerMatch(MediaType)} but returns just the 
matched serializer.
         *
         * @param mediaType The HTTP media type.
-        * @return The serializer that matched the accept header, or 
<jk>null</jk> if no match was made.
+        * @return The serializer that matched the accept header, or {@link 
Optional#empty()} if no match was made.
         */
-       public Serializer getSerializer(MediaType mediaType) {
-               if (mediaType == null)
-                       return null;
-               return getSerializer(mediaType.toString());
+       public Optional<Serializer> getSerializer(MediaType mediaType) {
+               return 
getSerializerMatch(mediaType).map(SerializerMatch::getSerializer);
        }
 
        /**
         * Same as {@link #getSerializerMatch(String)} but returns just the 
matched serializer.
         *
         * @param acceptHeader The HTTP <l>Accept</l> header string.
-        * @return The serializer that matched the accept header, or 
<jk>null</jk> if no match was made.
+        * @return The serializer that matched the accept header, or {@link 
Optional#empty()} if no match was made.
         */
-       public Serializer getSerializer(String acceptHeader) {
-               SerializerMatch sm = getSerializerMatch(acceptHeader);
-               return sm == null ? null : sm.getSerializer();
+       public Optional<Serializer> getSerializer(String acceptHeader) {
+               return 
getSerializerMatch(acceptHeader).map(SerializerMatch::getSerializer);
        }
 
        /**
         * Same as {@link #getSerializerMatch(String)} but matches using a 
{@link MediaType} instance.
         *
-        * @param mediaType The HTTP media type.
-        * @return The serializer and media type that matched the media type, 
or <jk>null</jk> if no match was made.
+        * @param mediaType The HTTP media type.  Can be <jk>null</jk>.
+        * @return The serializer and media type that matched the media type, 
or {@link Optional#empty()} if no match was made.
         */
-       public SerializerMatch getSerializerMatch(MediaType mediaType) {
-               return getSerializerMatch(mediaType.toString());
+       public Optional<SerializerMatch> getSerializerMatch(MediaType 
mediaType) {
+               return mediaType == null ? opte() : 
getSerializerMatch(mediaType.toString());
        }
 
        /**
@@ -617,14 +615,14 @@ public class SerializerSet {
         * The returned object includes both the serializer and media type that 
matched.
         *
         * @param acceptHeader The HTTP <l>Accept</l> header string.
-        * @return The serializer and media type that matched the accept 
header, or <jk>null</jk> if no match was made.
+        * @return The serializer and media type that matched the accept 
header, or {@link Optional#empty()} if no match was made.
         */
-       public SerializerMatch getSerializerMatch(String acceptHeader) {
+       public Optional<SerializerMatch> getSerializerMatch(String 
acceptHeader) {
                if (acceptHeader == null)
-                       return null;
+                       return opte();
                SerializerMatch sm = cache.get(acceptHeader);
                if (nn(sm))
-                       return sm;
+                       return opt(sm);
 
                var a = MediaRanges.of(acceptHeader);
                int match = a.match(mediaRangesList);
@@ -633,7 +631,7 @@ public class SerializerSet {
                        cache.putIfAbsent(acceptHeader, sm);
                }
 
-               return cache.get(acceptHeader);
+               return opt(cache.get(acceptHeader));
        }
 
        /**
@@ -647,20 +645,20 @@ public class SerializerSet {
         * Same as {@link #getSerializer(MediaType)}, but casts it to a {@link 
OutputStreamSerializer}.
         *
         * @param mediaType The HTTP media type.
-        * @return The serializer that matched the accept header, or 
<jk>null</jk> if no match was made.
+        * @return The serializer that matched the accept header, or {@link 
Optional#empty()} if no match was made.
         */
-       public OutputStreamSerializer getStreamSerializer(MediaType mediaType) {
-               return (OutputStreamSerializer)getSerializer(mediaType);
+       public Optional<OutputStreamSerializer> getStreamSerializer(MediaType 
mediaType) {
+               return 
getSerializer(mediaType).map(OutputStreamSerializer.class::cast);
        }
 
        /**
         * Same as {@link #getSerializer(String)}, but casts it to an {@link 
OutputStreamSerializer}.
         *
         * @param acceptHeader The HTTP <l>Accept</l> header string.
-        * @return The serializer that matched the accept header, or 
<jk>null</jk> if no match was made.
+        * @return The serializer that matched the accept header, or {@link 
Optional#empty()} if no match was made.
         */
-       public OutputStreamSerializer getStreamSerializer(String acceptHeader) {
-               return (OutputStreamSerializer)getSerializer(acceptHeader);
+       public Optional<OutputStreamSerializer> getStreamSerializer(String 
acceptHeader) {
+               return 
getSerializer(acceptHeader).map(OutputStreamSerializer.class::cast);
        }
 
        /**
@@ -677,20 +675,20 @@ public class SerializerSet {
         * Same as {@link #getSerializer(MediaType)}, but casts it to a {@link 
WriterSerializer}.
         *
         * @param mediaType The HTTP media type.
-        * @return The serializer that matched the accept header, or 
<jk>null</jk> if no match was made.
+        * @return The serializer that matched the accept header, or {@link 
Optional#empty()} if no match was made.
         */
-       public WriterSerializer getWriterSerializer(MediaType mediaType) {
-               return (WriterSerializer)getSerializer(mediaType);
+       public Optional<WriterSerializer> getWriterSerializer(MediaType 
mediaType) {
+               return 
getSerializer(mediaType).map(WriterSerializer.class::cast);
        }
 
        /**
         * Same as {@link #getSerializer(String)}, but casts it to a {@link 
WriterSerializer}.
         *
         * @param acceptHeader The HTTP <l>Accept</l> header string.
-        * @return The serializer that matched the accept header, or 
<jk>null</jk> if no match was made.
+        * @return The serializer that matched the accept header, or {@link 
Optional#empty()} if no match was made.
         */
-       public WriterSerializer getWriterSerializer(String acceptHeader) {
-               return (WriterSerializer)getSerializer(acceptHeader);
+       public Optional<WriterSerializer> getWriterSerializer(String 
acceptHeader) {
+               return 
getSerializer(acceptHeader).map(WriterSerializer.class::cast);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/parser/ParserSet_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/parser/ParserSet_Test.java
index ac7d23ac14..182ccafe32 100755
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/parser/ParserSet_Test.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/parser/ParserSet_Test.java
@@ -38,20 +38,20 @@ class ParserSet_Test extends TestBase {
        @Test void a01_parserGroupMatching() {
 
                var s = ParserSet.create().add(Parser1.class, Parser2.class, 
Parser3.class).build();
-               assertInstanceOf(Parser1.class, s.getParser("text/foo"));
-               assertInstanceOf(Parser1.class, s.getParser("text/foo_a"));
-               assertInstanceOf(Parser1.class, s.getParser("text/foo_a+xxx"));
-               assertInstanceOf(Parser1.class, s.getParser("text/xxx+foo_a"));
-               assertInstanceOf(Parser2.class, s.getParser("text/foo+bar"));
-               assertInstanceOf(Parser2.class, s.getParser("text/foo+bar_a"));
-               assertInstanceOf(Parser2.class, s.getParser("text/bar+foo"));
-               assertInstanceOf(Parser2.class, 
s.getParser("text/bar+foo+xxx"));
-               assertInstanceOf(Parser3.class, s.getParser("text/baz"));
-               assertInstanceOf(Parser3.class, s.getParser("text/baz_a"));
-               assertInstanceOf(Parser3.class, s.getParser("text/baz+yyy"));
-               assertInstanceOf(Parser3.class, s.getParser("text/baz_a+yyy"));
-               assertInstanceOf(Parser3.class, s.getParser("text/yyy+baz"));
-               assertInstanceOf(Parser3.class, s.getParser("text/yyy+baz_a"));
+               assertInstanceOf(Parser1.class, 
s.getParser("text/foo").orElseThrow());
+               assertInstanceOf(Parser1.class, 
s.getParser("text/foo_a").orElseThrow());
+               assertInstanceOf(Parser1.class, 
s.getParser("text/foo_a+xxx").orElseThrow());
+               assertInstanceOf(Parser1.class, 
s.getParser("text/xxx+foo_a").orElseThrow());
+               assertInstanceOf(Parser2.class, 
s.getParser("text/foo+bar").orElseThrow());
+               assertInstanceOf(Parser2.class, 
s.getParser("text/foo+bar_a").orElseThrow());
+               assertInstanceOf(Parser2.class, 
s.getParser("text/bar+foo").orElseThrow());
+               assertInstanceOf(Parser2.class, 
s.getParser("text/bar+foo+xxx").orElseThrow());
+               assertInstanceOf(Parser3.class, 
s.getParser("text/baz").orElseThrow());
+               assertInstanceOf(Parser3.class, 
s.getParser("text/baz_a").orElseThrow());
+               assertInstanceOf(Parser3.class, 
s.getParser("text/baz+yyy").orElseThrow());
+               assertInstanceOf(Parser3.class, 
s.getParser("text/baz_a+yyy").orElseThrow());
+               assertInstanceOf(Parser3.class, 
s.getParser("text/yyy+baz").orElseThrow());
+               assertInstanceOf(Parser3.class, 
s.getParser("text/yyy+baz_a").orElseThrow());
        }
 
        public static class Parser1 extends JsonParser { public 
Parser1(JsonParser.Builder<?> b) { super(b.consumes("text/foo,text/foo_a")); }}
@@ -94,7 +94,7 @@ class ParserSet_Test extends TestBase {
        @Test void b01_builder_addInstancesDirectly() {
                var instance = new P1(JsonParser.create().consumes("text/1"));
                var s = ParserSet.create().add(instance).build();
-               assertInstanceOf(P1.class, s.getParser("text/1"));
+               assertInstanceOf(P1.class, s.getParser("text/1").orElseThrow());
        }
 
        @Test void b02_builder_clear_removesAllEntries() {
@@ -113,9 +113,9 @@ class ParserSet_Test extends TestBase {
                var sb = ParserSet.create().add(P1.class, P2.class);
                sb.set(ParserSet.Inherit.class, P3.class);
                var s = sb.build();
-               assertInstanceOf(P1.class, s.getParser("text/1"));
-               assertInstanceOf(P2.class, s.getParser("text/2"));
-               assertInstanceOf(P3.class, s.getParser("text/3"));
+               assertInstanceOf(P1.class, s.getParser("text/1").orElseThrow());
+               assertInstanceOf(P2.class, s.getParser("text/2").orElseThrow());
+               assertInstanceOf(P3.class, s.getParser("text/3").orElseThrow());
        }
 
        @Test void b05_builder_setWithInvalidClassThrows() {
@@ -160,8 +160,8 @@ class ParserSet_Test extends TestBase {
                var sb1 = ParserSet.create().add(P1.class);
                var sb2 = sb1.copy();
                sb2.add(P2.class);
-               assertTrue(sb2.build().getParser("text/2") instanceof P2);
-               assertNull(sb1.build().getParser("text/2"));
+               assertTrue(sb2.build().getParser("text/2").orElseThrow() 
instanceof P2);
+               assertTrue(sb1.build().getParser("text/2").isEmpty());
        }
 
        @Test void b13_builder_beanContext_propagatesToBuilders() {
@@ -216,18 +216,18 @@ class ParserSet_Test extends TestBase {
        @Test void b20_parserSet_copy_returnsNewBuilder() {
                var s = ParserSet.create().add(P1.class).build();
                var copy = s.copy();
-               assertInstanceOf(P1.class, copy.build().getParser("text/1"));
+               assertInstanceOf(P1.class, 
copy.build().getParser("text/1").orElseThrow());
        }
 
        @Test void b21_getParser_byMediaType() {
                var s = ParserSet.create().add(P1.class).build();
-               assertInstanceOf(P1.class, s.getParser(MediaType.of("text/1")));
-               assertNull(s.getParser(MediaType.of("text/unknown")));
+               assertInstanceOf(P1.class, 
s.getParser(MediaType.of("text/1")).orElseThrow());
+               assertTrue(s.getParser(MediaType.of("text/unknown")).isEmpty());
        }
 
        @Test void b22_add_parserWithNoArgConstructor_instantiatesDirectly() {
                var s = ParserSet.create().add(SimpleParser.class).build();
-               assertInstanceOf(SimpleParser.class, 
s.getParser("text/simple"));
+               assertInstanceOf(SimpleParser.class, 
s.getParser("text/simple").orElseThrow());
        }
 
        @Test void b23_copy_withParserInstance_coversNonBuilderBranch() {
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/serializer/SerializerSet_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/serializer/SerializerSet_Test.java
index 72fd45c182..3a3b8acb91 100755
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/serializer/SerializerSet_Test.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/serializer/SerializerSet_Test.java
@@ -38,25 +38,25 @@ class SerializerSet_Test extends TestBase {
        @Test void a01_serializerGroupMatching() {
 
                var sg = SerializerSet.create().add(SA1.class, SA2.class, 
SA3.class).build();
-               assertInstanceOf(SA1.class, sg.getSerializer("text/foo"));
-               assertInstanceOf(SA1.class, sg.getSerializer("text/foo_a"));
-               assertInstanceOf(SA1.class, sg.getSerializer("text/xxx+foo_a"));
-               assertInstanceOf(SA1.class, sg.getSerializer("text/foo_a+xxx"));
-               assertInstanceOf(SA2.class, sg.getSerializer("text/foo+bar"));
-               assertInstanceOf(SA2.class, sg.getSerializer("text/foo+bar_a"));
-               assertInstanceOf(SA2.class, sg.getSerializer("text/bar+foo"));
-               assertInstanceOf(SA2.class, sg.getSerializer("text/bar_a+foo"));
-               assertInstanceOf(SA2.class, 
sg.getSerializer("text/bar+foo+xxx"));
-               assertInstanceOf(SA2.class, 
sg.getSerializer("text/bar_a+foo+xxx"));
-               assertInstanceOf(SA3.class, sg.getSerializer("text/baz"));
-               assertInstanceOf(SA3.class, sg.getSerializer("text/baz_a"));
-               assertInstanceOf(SA3.class, sg.getSerializer("text/baz+yyy"));
-               assertInstanceOf(SA3.class, sg.getSerializer("text/baz_a+yyy"));
-               assertInstanceOf(SA3.class, sg.getSerializer("text/yyy+baz"));
-               assertInstanceOf(SA3.class, sg.getSerializer("text/yyy+baz_a"));
-
-               assertInstanceOf(SA1.class, 
sg.getSerializer("text/foo;q=0.9,text/foo+bar;q=0.8"));
-               assertInstanceOf(SA2.class, 
sg.getSerializer("text/foo;q=0.8,text/foo+bar;q=0.9"));
+               assertInstanceOf(SA1.class, 
sg.getSerializer("text/foo").orElseThrow());
+               assertInstanceOf(SA1.class, 
sg.getSerializer("text/foo_a").orElseThrow());
+               assertInstanceOf(SA1.class, 
sg.getSerializer("text/xxx+foo_a").orElseThrow());
+               assertInstanceOf(SA1.class, 
sg.getSerializer("text/foo_a+xxx").orElseThrow());
+               assertInstanceOf(SA2.class, 
sg.getSerializer("text/foo+bar").orElseThrow());
+               assertInstanceOf(SA2.class, 
sg.getSerializer("text/foo+bar_a").orElseThrow());
+               assertInstanceOf(SA2.class, 
sg.getSerializer("text/bar+foo").orElseThrow());
+               assertInstanceOf(SA2.class, 
sg.getSerializer("text/bar_a+foo").orElseThrow());
+               assertInstanceOf(SA2.class, 
sg.getSerializer("text/bar+foo+xxx").orElseThrow());
+               assertInstanceOf(SA2.class, 
sg.getSerializer("text/bar_a+foo+xxx").orElseThrow());
+               assertInstanceOf(SA3.class, 
sg.getSerializer("text/baz").orElseThrow());
+               assertInstanceOf(SA3.class, 
sg.getSerializer("text/baz_a").orElseThrow());
+               assertInstanceOf(SA3.class, 
sg.getSerializer("text/baz+yyy").orElseThrow());
+               assertInstanceOf(SA3.class, 
sg.getSerializer("text/baz_a+yyy").orElseThrow());
+               assertInstanceOf(SA3.class, 
sg.getSerializer("text/yyy+baz").orElseThrow());
+               assertInstanceOf(SA3.class, 
sg.getSerializer("text/yyy+baz_a").orElseThrow());
+
+               assertInstanceOf(SA1.class, 
sg.getSerializer("text/foo;q=0.9,text/foo+bar;q=0.8").orElseThrow());
+               assertInstanceOf(SA2.class, 
sg.getSerializer("text/foo;q=0.8,text/foo+bar;q=0.9").orElseThrow());
        }
 
        public static class SA1 extends JsonSerializer {
@@ -130,9 +130,9 @@ class SerializerSet_Test extends TestBase {
        @Test void a03_mediaTypesWithMetaCharacters() {
                var gb = SerializerSet.create().add(SC1.class, SC2.class, 
SC3.class);
                var g = gb.build();
-               assertInstanceOf(SC1.class, g.getSerializer("text/foo"));
-               assertInstanceOf(SC2.class, g.getSerializer("foo/json"));
-               assertInstanceOf(SC3.class, g.getSerializer("foo/foo"));
+               assertInstanceOf(SC1.class, 
g.getSerializer("text/foo").orElseThrow());
+               assertInstanceOf(SC2.class, 
g.getSerializer("foo/json").orElseThrow());
+               assertInstanceOf(SC3.class, 
g.getSerializer("foo/foo").orElseThrow());
        }
 
        public static class SC1 extends JsonSerializer {
@@ -166,7 +166,7 @@ class SerializerSet_Test extends TestBase {
        @Test void b01_builder_addInstancesDirectly() {
                var instance = new 
SB1(JsonSerializer.create().accept("text/1"));
                var s = SerializerSet.create().add(instance).build();
-               assertInstanceOf(SB1.class, s.getSerializer("text/1"));
+               assertInstanceOf(SB1.class, 
s.getSerializer("text/1").orElseThrow());
        }
 
        @Test void b02_builder_addInvalidClassThrows() {
@@ -178,9 +178,9 @@ class SerializerSet_Test extends TestBase {
                var sb = SerializerSet.create().add(SB1.class, SB2.class);
                sb.set(SerializerSet.Inherit.class, SB3.class);
                var s = sb.build();
-               assertInstanceOf(SB1.class, s.getSerializer("text/1"));
-               assertInstanceOf(SB2.class, s.getSerializer("text/2"));
-               assertInstanceOf(SB3.class, s.getSerializer("text/3"));
+               assertInstanceOf(SB1.class, 
s.getSerializer("text/1").orElseThrow());
+               assertInstanceOf(SB2.class, 
s.getSerializer("text/2").orElseThrow());
+               assertInstanceOf(SB3.class, 
s.getSerializer("text/3").orElseThrow());
        }
 
        @Test void b04_builder_setWithInvalidClassThrows() {
@@ -225,8 +225,8 @@ class SerializerSet_Test extends TestBase {
                var sb1 = SerializerSet.create().add(SB1.class);
                var sb2 = sb1.copy();
                sb2.add(SB2.class);
-               assertNotNull(sb2.build().getSerializer("text/2"));
-               assertNull(sb1.build().getSerializer("text/2"));
+               assertTrue(sb2.build().getSerializer("text/2").isPresent());
+               assertTrue(sb1.build().getSerializer("text/2").isEmpty());
        }
 
        @Test void b12_builder_beanContext_propagatesToBuilders() {
@@ -280,18 +280,18 @@ class SerializerSet_Test extends TestBase {
        @Test void b19_serializerSet_copy_returnsNewBuilder() {
                var s = SerializerSet.create().add(SB1.class).build();
                var copy = s.copy();
-               assertInstanceOf(SB1.class, 
copy.build().getSerializer("text/1"));
+               assertInstanceOf(SB1.class, 
copy.build().getSerializer("text/1").orElseThrow());
        }
 
        @Test void b20_getSerializer_byMediaType() {
                var s = SerializerSet.create().add(SB1.class).build();
-               assertInstanceOf(SB1.class, 
s.getSerializer(MediaType.of("text/1")));
-               assertNull(s.getSerializer(MediaType.of("text/unknown")));
+               assertInstanceOf(SB1.class, 
s.getSerializer(MediaType.of("text/1")).orElseThrow());
+               
assertTrue(s.getSerializer(MediaType.of("text/unknown")).isEmpty());
        }
 
-       @Test void b21_getSerializer_nullMediaType_returnsNull() {
+       @Test void b21_getSerializer_nullMediaType_returnsEmpty() {
                var s = SerializerSet.create().add(SB1.class).build();
-               assertNull(s.getSerializer((MediaType) null));
+               assertTrue(s.getSerializer((MediaType) null).isEmpty());
        }
 
        @Test void b22_builder_clear_removesAllEntries() {
@@ -303,22 +303,22 @@ class SerializerSet_Test extends TestBase {
 
        @Test void b23_getSerializerMatch_byMediaType() {
                var s = SerializerSet.create().add(SB1.class).build();
-               assertNotNull(s.getSerializerMatch(MediaType.of("text/1")));
+               
assertTrue(s.getSerializerMatch(MediaType.of("text/1")).isPresent());
        }
 
-       @Test void b24_getSerializerMatch_nullString_returnsNull() {
+       @Test void b24_getSerializerMatch_nullString_returnsEmpty() {
                var s = SerializerSet.create().add(SB1.class).build();
-               assertNull(s.getSerializerMatch((String) null));
+               assertTrue(s.getSerializerMatch((String) null).isEmpty());
        }
 
        @Test void b25_getWriterSerializer_byMediaType() {
                var s = SerializerSet.create().add(SB1.class).build();
-               assertInstanceOf(SB1.class, 
s.getWriterSerializer(MediaType.of("text/1")));
+               assertInstanceOf(SB1.class, 
s.getWriterSerializer(MediaType.of("text/1")).orElseThrow());
        }
 
        @Test void 
b26_add_serializerWithNoArgConstructor_instantiatesDirectly() {
                var s = 
SerializerSet.create().add(SimpleSerializer.class).build();
-               assertInstanceOf(SimpleSerializer.class, 
s.getSerializer("text/simple"));
+               assertInstanceOf(SimpleSerializer.class, 
s.getSerializer("text/simple").orElseThrow());
        }
 
        @Test void b27_copy_withSerializerInstance_coversNonBuilderBranch() {
@@ -328,9 +328,9 @@ class SerializerSet_Test extends TestBase {
                assertFalse(copy.inner().isEmpty());
        }
 
-       @Test void b28_getStreamSerializer_withNoMatch_returnsNull() {
+       @Test void b28_getStreamSerializer_withNoMatch_returnsEmpty() {
                var s = SerializerSet.create().add(SB1.class).build();
-               assertNull(s.getStreamSerializer(MediaType.of("text/unknown")));
-               assertNull(s.getStreamSerializer("text/unknown"));
+               
assertTrue(s.getStreamSerializer(MediaType.of("text/unknown")).isEmpty());
+               assertTrue(s.getStreamSerializer("text/unknown").isEmpty());
        }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/transforms/ObjectSwapTest.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/transforms/ObjectSwapTest.java
index 3a33cc75a2..c727179876 100644
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/transforms/ObjectSwapTest.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/transforms/ObjectSwapTest.java
@@ -87,13 +87,13 @@ class ObjectSwapTest extends TestBase {
 
                var myPojo = new MyPojo();
 
-               var json = s.getWriterSerializer("text/json").serialize(myPojo);
+               var json = 
s.getWriterSerializer("text/json").orElseThrow().serialize(myPojo);
                assertEquals("'It\\'s JSON!'", json);
 
-               var xml = s.getWriterSerializer("text/xml").serialize(myPojo);
+               var xml = 
s.getWriterSerializer("text/xml").orElseThrow().serialize(myPojo);
                assertEquals("<string>It's XML!</string>", xml);
 
-               var html = s.getWriterSerializer("text/html").serialize(myPojo);
+               var html = 
s.getWriterSerializer("text/html").orElseThrow().serialize(myPojo);
                assertEquals("<string>It's something else!</string>", html);
        }
 }
\ No newline at end of file
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteClient_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteClient_Test.java
index e174186e78..54e7cee561 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteClient_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteClient_Test.java
@@ -24,6 +24,7 @@ import java.util.*;
 import org.apache.juneau.http.*;
 import org.apache.juneau.http.entity.*;
 import org.apache.juneau.http.remote.*;
+import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.rest.client.remote.*;
 import org.apache.juneau.rest.mock.*;
 import org.junit.jupiter.api.*;
@@ -695,7 +696,9 @@ class RemoteClient_Test {
        }
 
        @Test void t02_object_return_falls_through_to_string() throws Exception 
{
-               try (var client = 
RestClient.builder().transport(MockHttpTransport.of(200, 
"hello")).rootUrl("http://x.com";).build()) {
+               // Object-return fallthrough: the negotiated parser fails to 
parse the plain body, so the raw string is returned.
+               // Requires a parser to be resolvable — configure JSON as the 
default (the next-gen client has no implicit one).
+               try (var client = 
RestClient.builder().transport(MockHttpTransport.of(200, 
"hello")).rootUrl("http://x.com";).defaultParser(JsonParser.DEFAULT).build()) {
                        var svc = 
client.remote(T01_VoidBoxedReturnService.class);
                        assertEquals("hello", svc.getObject());
                }
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteProxy_FeatureParity_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteProxy_FeatureParity_Test.java
index 2cbcb1a55d..f1c0330ff9 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteProxy_FeatureParity_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteProxy_FeatureParity_Test.java
@@ -28,6 +28,7 @@ import org.apache.juneau.http.header.*;
 import org.apache.juneau.http.part.*;
 import org.apache.juneau.http.remote.*;
 import org.apache.juneau.http.response.*;
+import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.rest.mock.*;
 import org.apache.juneau.rest.server.*;
 import org.junit.jupiter.api.*;
@@ -238,6 +239,23 @@ class RemoteProxy_FeatureParity_Test {
                @RestGet("/rest/remainder/*") public String 
remainder(@PathRemainder String r) { return "r=" + r; }
        }
 
+       /**
+        * JSON-negotiating fixture for the response-deserialization rows 
(F15&ndash;F19, G11).  Unlike
+        * {@link A_ParityResource} (which returns pre-serialized raw strings 
and runs serializer-less), this resource
+        * configures a JSON serializer/parser and returns <i>typed</i> 
objects, so the next-gen client &mdash; which now
+        * advertises {@code Accept: application/json} once a parser is 
configured and has no implicit JSON fallback &mdash;
+        * negotiates a real {@code application/json} round-trip rather than 
relying on the removed implicit default.
+        */
+       @Rest(serializers=JsonSerializer.class, parsers=JsonParser.class)
+       public static class A_ParityJsonResource {
+               @RestGet("/rest/bean")  public A_Bean bean() { var b = new 
A_Bean(); b.setName("na44"); return b; }
+               @RestGet("/rest/list")  public List<A_Bean> list() { var b = 
new A_Bean(); b.setName("na44"); return List.of(b); }
+               @RestGet("/rest/map")   public Map<String,String> map() { 
return Map.of("k", "v"); }
+               @RestPatch("/rest/count") public Integer count(@Content String 
body) { return 5; }
+               @RestGet("/rest/object") public Object object() { return 123; }
+               @RestPost("/rest/beanContent") public String 
beanContent(@Content A_Bean bean) { return "posted:" + bean.getName(); }
+       }
+
        // 
=================================================================================================================
        // Shared helpers
        // 
=================================================================================================================
@@ -308,6 +326,20 @@ class RemoteProxy_FeatureParity_Test {
                        return mrc.getClient().remote(A_ParityClient.class);
                }
 
+               @SuppressWarnings({
+                       "resource" // The negotiating client shares the 
MockRestClient's (root-mounted) transport, closed by mrc; not closed separately.
+               })
+               private A_ParityClient parsingProxy(MockRestClient mrc) {
+                       // Response-body deserialization needs a resolvable 
parser; the next-gen client has no implicit JSON default,
+                       // so configure JSON serializer + default parser 
explicitly over the mock's (A_ParityJsonResource) transport.
+                       return RestClient.builder()
+                               .transport(mrc.getClient().getTransport())
+                               .defaultSerializer(JsonSerializer.DEFAULT)
+                               .defaultParser(JsonParser.DEFAULT)
+                               .build()
+                               .remote(A_ParityClient.class);
+               }
+
                // ---- Active cells 
-------------------------------------------------------------------------------------------
 
                @Test void b01_get_F2_F20() throws Exception {
@@ -414,35 +446,35 @@ class RemoteProxy_FeatureParity_Test {
                }
 
                @Test void b23_returnBean_parsed_F15() throws Exception {
-                       try (var mrc = 
MockRestClient.create(A_ParityResource.class)) {
-                               var bean = proxy(mrc).getBean();
+                       try (var mrc = 
MockRestClient.create(A_ParityJsonResource.class)) {
+                               var bean = parsingProxy(mrc).getBean();
                                assertEquals("na44", bean.getName());
                        }
                }
 
                @Test void b24_returnList_parsed_F16() throws Exception {
-                       try (var mrc = 
MockRestClient.create(A_ParityResource.class)) {
-                               var l = proxy(mrc).getList();
+                       try (var mrc = 
MockRestClient.create(A_ParityJsonResource.class)) {
+                               var l = parsingProxy(mrc).getList();
                                assertEquals(1, l.size());
                                assertEquals("na44", l.get(0).getName());
                        }
                }
 
                @Test void b25_returnMap_parsed_F17() throws Exception {
-                       try (var mrc = 
MockRestClient.create(A_ParityResource.class)) {
-                               assertEquals("v", proxy(mrc).getMap().get("k"));
+                       try (var mrc = 
MockRestClient.create(A_ParityJsonResource.class)) {
+                               assertEquals("v", 
parsingProxy(mrc).getMap().get("k"));
                        }
                }
 
                @Test void b26_returnInteger_parsed_F18() throws Exception {
-                       try (var mrc = 
MockRestClient.create(A_ParityResource.class)) {
-                               assertEquals(Integer.valueOf(5), 
proxy(mrc).patchCount("x"));
+                       try (var mrc = 
MockRestClient.create(A_ParityJsonResource.class)) {
+                               assertEquals(Integer.valueOf(5), 
parsingProxy(mrc).patchCount("x"));
                        }
                }
 
                @Test void b27_returnObject_parsed_F19() throws Exception {
-                       try (var mrc = 
MockRestClient.create(A_ParityResource.class)) {
-                               assertEquals(123, proxy(mrc).getObject());
+                       try (var mrc = 
MockRestClient.create(A_ParityJsonResource.class)) {
+                               assertEquals(123, 
parsingProxy(mrc).getObject());
                        }
                }
 
@@ -472,8 +504,8 @@ class RemoteProxy_FeatureParity_Test {
                }
 
                @Test void b32_optionalReturn_G11() throws Exception {
-                       try (var mrc = 
MockRestClient.create(A_ParityResource.class)) {
-                               
assertTrue(proxy(mrc).getBeanOptional().isPresent());
+                       try (var mrc = 
MockRestClient.create(A_ParityJsonResource.class)) {
+                               
assertTrue(parsingProxy(mrc).getBeanOptional().isPresent());
                        }
                }
 
@@ -570,7 +602,7 @@ class RemoteProxy_FeatureParity_Test {
        @Nested class C_RealTransportColumn {
 
                private RestClient client(List<TransportRequest> captured) {
-                       return 
RestClient.builder().transport(captureTransport(captured).build()).rootUrl("http://x.com";).build();
+                       return 
RestClient.builder().transport(captureTransport(captured).build()).rootUrl("http://x.com";).defaultSerializer(JsonSerializer.DEFAULT).build();
                }
 
                // ---- Active cells 
-------------------------------------------------------------------------------------------
@@ -797,7 +829,8 @@ class RemoteProxy_FeatureParity_Test {
                        var t = MockHttpTransport.builder()
                                .fallback(req -> 
TransportResponse.builder().statusCode(200).body(new 
ByteArrayInputStream("[{\"name\":\"na44\"}]".getBytes())).build())
                                .build();
-                       try (var c = 
RestClient.builder().transport(t).rootUrl("http://x.com";).build()) {
+                       // Response carries no Content-Type, so resolve via the 
explicit default parser (no implicit JSON fallback).
+                       try (var c = 
RestClient.builder().transport(t).rootUrl("http://x.com";).defaultParser(JsonParser.DEFAULT).build())
 {
                                var f = 
c.remote(A_ParityClient.class).getListAsync();
                                var list = f.get();
                                assertNotNull(list);
@@ -836,9 +869,13 @@ class RemoteProxy_FeatureParity_Test {
                        }
                }
 
+               @SuppressWarnings({
+                       "resource" // The negotiating client shares the 
MockRestClient's (root-mounted) transport, closed by mrc.
+               })
                @Test void c43_jsonRoundTrip_F23() throws Exception {
-                       try (var mrc = 
MockRestClient.create(A_ParityResource.class)) {
-                               var svc = 
mrc.getClient().remote(A_ParityClient.class);
+                       try (var mrc = 
MockRestClient.create(A_ParityJsonResource.class);
+                                       var c = 
RestClient.builder().transport(mrc.getClient().getTransport()).defaultSerializer(JsonSerializer.DEFAULT).defaultParser(JsonParser.DEFAULT).build())
 {
+                               var svc = c.remote(A_ParityClient.class);
                                var b = new A_Bean(); b.setName("na44");
                                assertTrue(svc.postBean(b).contains("na44"));
                                assertEquals("na44", svc.getBean().getName());
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteProxy_NextGenParityGaps_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteProxy_NextGenParityGaps_Test.java
index c4eb9a30d0..aabba808d7 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteProxy_NextGenParityGaps_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RemoteProxy_NextGenParityGaps_Test.java
@@ -199,7 +199,7 @@ class RemoteProxy_NextGenParityGaps_Test {
        }
 
        private static RestClient captureClient(List<TransportRequest> 
captured) {
-               return 
RestClient.builder().transport(captureTransport(captured).build()).rootUrl("http://x.com";).build();
+               return 
RestClient.builder().transport(captureTransport(captured).build()).rootUrl("http://x.com";).defaultSerializer(JsonSerializer.DEFAULT).build();
        }
 
        private static String readBody(TransportRequest req) throws IOException 
{
@@ -1367,7 +1367,7 @@ class RemoteProxy_NextGenParityGaps_Test {
        }
 
        private static RestClient captureClient(List<TransportRequest> 
captured, SerializerSet serializers, ParserSet parsers) {
-               var b = 
RestClient.builder().transport(captureTransport(captured).build()).rootUrl("http://x.com";);
+               var b = 
RestClient.builder().transport(captureTransport(captured).build()).rootUrl("http://x.com";).defaultSerializer(JsonSerializer.DEFAULT);
                if (serializers != null)
                        b.serializers(serializers);
                if (parsers != null)
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RestClientFeatures_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RestClientFeatures_Test.java
index 9cfdc32fc7..8c25fabf99 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RestClientFeatures_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/client/RestClientFeatures_Test.java
@@ -26,6 +26,7 @@ import java.util.concurrent.atomic.*;
 
 import org.apache.juneau.http.*;
 import org.apache.juneau.http.entity.*;
+import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.rest.mock.*;
 import org.junit.jupiter.api.*;
 
@@ -768,6 +769,7 @@ class RestClientFeatures_Test {
                try (var client = RestClient.builder()
                                .transport(transport)
                                .rootUrl("http://x.com";)
+                               .defaultSerializer(JsonSerializer.DEFAULT)
                                .bodyConverters() // empty converter list
                                .build()) {
                        try (var r = 
client.post("/").body("no-converter").run()) {
@@ -815,6 +817,7 @@ class RestClientFeatures_Test {
                try (var client = RestClient.builder()
                                .transport(transport)
                                .rootUrl("http://x.com";)
+                               .defaultSerializer(JsonSerializer.DEFAULT)
                                .bodyConverters() // no converters
                                .build()) {
                        try (var r = client.post("/").body(new 
ByteArrayInputStream(new byte[0])).run()) {
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_NoInherit_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_NoInherit_Test.java
index f16053abc5..9b7ec41929 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_NoInherit_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_NoInherit_Test.java
@@ -72,9 +72,9 @@ class MixinInheritance_NoInherit_Test extends TestBase {
                var mixinCtx = 
hostCtx.getMixinContexts().get(M_MixinS1Only.class);
                assertNotNull(mixinCtx);
 
-               
assertNotNull(mixinCtx.getSerializers().getSerializer("text/mixin-s1"),
+               
assertTrue(mixinCtx.getSerializers().getSerializer("text/mixin-s1").isPresent(),
                        "Mixin's own MixinS1 must be present");
-               
assertNull(mixinCtx.getSerializers().getSerializer("text/host-s1"),
+               
assertTrue(mixinCtx.getSerializers().getSerializer("text/host-s1").isEmpty(),
                        "Mixin with noInherit=\"serializers\" must NOT see the 
host's HostS1 (parent walk blocked)");
        }
 
@@ -82,9 +82,9 @@ class MixinInheritance_NoInherit_Test extends TestBase {
                MockRestClient.buildLax(Host.class);
                var hostCtx = RestContext.getGlobalRegistry().get(Host.class);
 
-               
assertNotNull(hostCtx.getSerializers().getSerializer("text/host-s1"),
+               
assertTrue(hostCtx.getSerializers().getSerializer("text/host-s1").isPresent(),
                        "Host must retain its declared HostS1 serializer 
regardless of mixin's noInherit");
-               
assertNull(hostCtx.getSerializers().getSerializer("text/mixin-s1"),
+               
assertTrue(hostCtx.getSerializers().getSerializer("text/mixin-s1").isEmpty(),
                        "Host must NOT pick up MixinS1 from a 
noInherit-isolated mixin");
        }
 }
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_Parsers_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_Parsers_Test.java
index df9c19a717..3821b89809 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_Parsers_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_Parsers_Test.java
@@ -83,9 +83,9 @@ class MixinInheritance_Parsers_Test extends TestBase {
                var mixinCtx = hostCtx.getMixinContexts().get(M_Empty.class);
                assertNotNull(mixinCtx);
 
-               
assertNotNull(hostCtx.getParsers().getParser(MediaType.of("text/host-p1")),
+               
assertTrue(hostCtx.getParsers().getParser(MediaType.of("text/host-p1")).isPresent(),
                        "Host must register its declared HostP1 parser");
-               
assertNotNull(mixinCtx.getParsers().getParser(MediaType.of("text/host-p1")),
+               
assertTrue(mixinCtx.getParsers().getParser(MediaType.of("text/host-p1")).isPresent(),
                        "Mixin with no parser overrides must inherit the host's 
HostP1 parser");
        }
 
@@ -95,11 +95,11 @@ class MixinInheritance_Parsers_Test extends TestBase {
                var mixinCtx = 
hostCtx.getMixinContexts().get(M_AppendsMixinP1.class);
                assertNotNull(mixinCtx);
 
-               
assertNull(hostCtx.getParsers().getParser(MediaType.of("text/mixin-p1")),
+               
assertTrue(hostCtx.getParsers().getParser(MediaType.of("text/mixin-p1")).isEmpty(),
                        "Host endpoint must NOT have MixinP1 — mixin 
contributions are scoped to the mixin context");
-               
assertNotNull(mixinCtx.getParsers().getParser(MediaType.of("text/mixin-p1")),
+               
assertTrue(mixinCtx.getParsers().getParser(MediaType.of("text/mixin-p1")).isPresent(),
                        "Mixin endpoint must have MixinP1 via the mixin's own 
@Rest(parsers=)");
-               
assertNotNull(mixinCtx.getParsers().getParser(MediaType.of("text/host-p1")),
+               
assertTrue(mixinCtx.getParsers().getParser(MediaType.of("text/host-p1")).isPresent(),
                        "Mixin endpoint must still have the host's HostP1 
(inheritance walk)");
        }
 
@@ -109,14 +109,14 @@ class MixinInheritance_Parsers_Test extends TestBase {
                var mixinCtx = 
hostCtx.getMixinContexts().get(M_NoInheritP1.class);
                assertNotNull(mixinCtx);
 
-               
assertNotNull(mixinCtx.getParsers().getParser(MediaType.of("text/mixin-p1")),
+               
assertTrue(mixinCtx.getParsers().getParser(MediaType.of("text/mixin-p1")).isPresent(),
                        "Mixin's own MixinP1 must be present");
-               
assertNull(mixinCtx.getParsers().getParser(MediaType.of("text/host-p1")),
+               
assertTrue(mixinCtx.getParsers().getParser(MediaType.of("text/host-p1")).isEmpty(),
                        "Mixin with noInherit=\"parsers\" must NOT see the 
host's HostP1 (parent walk blocked)");
 
-               
assertNotNull(hostCtx.getParsers().getParser(MediaType.of("text/host-p1")),
+               
assertTrue(hostCtx.getParsers().getParser(MediaType.of("text/host-p1")).isPresent(),
                        "Host must retain its HostP1 regardless of mixin's 
noInherit");
-               
assertNull(hostCtx.getParsers().getParser(MediaType.of("text/mixin-p1")),
+               
assertTrue(hostCtx.getParsers().getParser(MediaType.of("text/mixin-p1")).isEmpty(),
                        "Host must NOT pick up MixinP1 from a 
noInherit-isolated mixin");
        }
 }
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_Serializers_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_Serializers_Test.java
index dda7df7ba8..d28401a478 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_Serializers_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/mixin/MixinInheritance_Serializers_Test.java
@@ -80,9 +80,9 @@ class MixinInheritance_Serializers_Test extends TestBase {
                var mixinCtx = hostCtx.getMixinContexts().get(M_Empty.class);
                assertNotNull(mixinCtx);
 
-               
assertNotNull(hostCtx.getSerializers().getSerializer("text/host-s1"),
+               
assertTrue(hostCtx.getSerializers().getSerializer("text/host-s1").isPresent(),
                        "Host must register its declared HostS1 serializer");
-               
assertNotNull(mixinCtx.getSerializers().getSerializer("text/host-s1"),
+               
assertTrue(mixinCtx.getSerializers().getSerializer("text/host-s1").isPresent(),
                        "Mixin with no serializer overrides must inherit the 
host's HostS1 serializer");
        }
 
@@ -92,11 +92,11 @@ class MixinInheritance_Serializers_Test extends TestBase {
                var mixinCtx = 
hostCtx.getMixinContexts().get(M_AppendsMixinS1.class);
                assertNotNull(mixinCtx);
 
-               
assertNull(hostCtx.getSerializers().getSerializer("text/mixin-s1"),
+               
assertTrue(hostCtx.getSerializers().getSerializer("text/mixin-s1").isEmpty(),
                        "Host endpoint must NOT have MixinS1 — mixin 
contributions are scoped to the mixin context");
-               
assertNotNull(mixinCtx.getSerializers().getSerializer("text/mixin-s1"),
+               
assertTrue(mixinCtx.getSerializers().getSerializer("text/mixin-s1").isPresent(),
                        "Mixin endpoint must have MixinS1 via the mixin's own 
@Rest(serializers=)");
-               
assertNotNull(mixinCtx.getSerializers().getSerializer("text/host-s1"),
+               
assertTrue(mixinCtx.getSerializers().getSerializer("text/host-s1").isPresent(),
                        "Mixin endpoint must still have the host's HostS1 
(inheritance walk)");
        }
 }
diff --git 
a/juneau-petstore/juneau-petstore-springboot/src/test/java/org/apache/juneau/petstore/springboot/PetstoreSpringboot_Test.java
 
b/juneau-petstore/juneau-petstore-springboot/src/test/java/org/apache/juneau/petstore/springboot/PetstoreSpringboot_Test.java
index c7e4030858..9f1af7f582 100644
--- 
a/juneau-petstore/juneau-petstore-springboot/src/test/java/org/apache/juneau/petstore/springboot/PetstoreSpringboot_Test.java
+++ 
b/juneau-petstore/juneau-petstore-springboot/src/test/java/org/apache/juneau/petstore/springboot/PetstoreSpringboot_Test.java
@@ -43,6 +43,10 @@ import org.springframework.boot.test.web.server.*;
  */
 @org.apache.juneau.testing.annotations.SpringbootTest
 @SpringBootTest(classes = App.class, webEnvironment = 
WebEnvironment.RANDOM_PORT)
+@SuppressWarnings({
+       "java:S8692", // warmUpServer() polls a real HTTP server against a 
genuine wall-clock deadline; a fixed clock would break the retry loop.
+       "java:S2925" // warmUpServer() readiness loop needs a back-off between 
retries; without it a ConnectException would busy-spin. No event/latch to await 
and Awaitility isn't on the test classpath.
+})
 class PetstoreSpringboot_Test {
 
        @LocalServerPort
@@ -53,10 +57,50 @@ class PetstoreSpringboot_Test {
                .followRedirects(HttpClient.Redirect.NEVER)
                .build();
 
+       private static volatile boolean warmedUp;
+
+       /**
+        * Primes the root endpoint before the timed test methods run.
+        *
+        * <p>
+        * Spring Boot's {@code RANDOM_PORT} environment only waits for the 
Tomcat connector to bind — not for the Juneau
+        * REST servlet to initialize. The first request to the group resource 
({@code /}) forces one-time
+        * {@code RestContext} setup of the root <i>and all of its child 
resources</i> (serializer/parser metadata,
+        * {@code HtmlDocSerializer} construction) in a single request. Under a 
loaded CI agent this cold start can exceed
+        * the per-request timeout the test methods use (it timed out at 
exactly 10s in build #2472). Absorbing that
+        * startup cost here once (with a generous budget + retry) removes the 
race from {@code a01} while keeping the
+        * per-test timeouts tight. Mirrors the same guard in {@code 
PetstoreJetty_Test}.
+        */
+       @BeforeEach
+       void warmUpServer() throws Exception {
+               if (warmedUp)
+                       return;
+               var deadline = Instant.now().plusSeconds(30);
+               Exception last = null;
+               while (Instant.now().isBefore(deadline)) {
+                       try {
+                               var req = HttpRequest.newBuilder()
+                                       .uri(URI.create("http://localhost:"; + 
port + "/"))
+                                       .timeout(Duration.ofSeconds(20))
+                                       .header("Accept", "text/html")
+                                       .GET()
+                                       .build();
+                               if (HTTP.send(req, 
BodyHandlers.ofString()).statusCode() == 200) {
+                                       warmedUp = true;
+                                       return;
+                               }
+                       } catch (HttpTimeoutException | ConnectException e) {
+                               last = e;
+                       }
+                       Thread.sleep(250);
+               }
+               throw new IllegalStateException("Petstore Spring Boot server 
did not become ready within 30s", last);
+       }
+
        private HttpResponse<String> get(String path, String accept) throws 
Exception {
                var req = HttpRequest.newBuilder()
                        .uri(URI.create("http://localhost:"; + port + path))
-                       .timeout(Duration.ofSeconds(10))
+                       .timeout(Duration.ofSeconds(30))
                        .header("Accept", accept)
                        .GET()
                        .build();
@@ -66,7 +110,7 @@ class PetstoreSpringboot_Test {
        private HttpResponse<String> getWithAuth(String path, String accept, 
String authValue) throws Exception {
                var req = HttpRequest.newBuilder()
                        .uri(URI.create("http://localhost:"; + port + path))
-                       .timeout(Duration.ofSeconds(10))
+                       .timeout(Duration.ofSeconds(30))
                        .header("Accept", accept)
                        .header("Authorization", authValue)
                        .GET()
@@ -77,7 +121,7 @@ class PetstoreSpringboot_Test {
        private HttpResponse<String> post(String path, String contentType, 
String body) throws Exception {
                var req = HttpRequest.newBuilder()
                        .uri(URI.create("http://localhost:"; + port + path))
-                       .timeout(Duration.ofSeconds(10))
+                       .timeout(Duration.ofSeconds(30))
                        .header("Content-Type", contentType)
                        .header("Accept", "application/json")
                        .POST(HttpRequest.BodyPublishers.ofString(body))
@@ -88,7 +132,7 @@ class PetstoreSpringboot_Test {
        private HttpResponse<String> delete(String path) throws Exception {
                var req = HttpRequest.newBuilder()
                        .uri(URI.create("http://localhost:"; + port + path))
-                       .timeout(Duration.ofSeconds(10))
+                       .timeout(Duration.ofSeconds(30))
                        .DELETE()
                        .build();
                return HTTP.send(req, BodyHandlers.ofString());
diff --git 
a/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
 
b/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
index fb04214008..6e1b7096f2 100644
--- 
a/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
+++ 
b/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
@@ -8020,7 +8020,7 @@ public class RestClient extends MarshallingContextable 
implements HttpClient, Cl
                if (parsers.isEmpty())
                        return null;
                if (nn(mediaType)) {
-                       var p = parsers.getParser(mediaType);
+                       var p = parsers.getParser(mediaType).orElse(null);
                        if (nn(p))
                                return p;
                }
@@ -8037,7 +8037,7 @@ public class RestClient extends MarshallingContextable 
implements HttpClient, Cl
                if (serializers.isEmpty())
                        return null;
                if (nn(mediaType)) {
-                       var s = serializers.getSerializer(mediaType);
+                       var s = 
serializers.getSerializer(mediaType).orElse(null);
                        if (nn(s))
                                return s;
                }
diff --git 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
index 8256852f5b..e03ae16f5a 100644
--- 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/ResponseBody.java
@@ -21,6 +21,7 @@ import static 
org.apache.juneau.commons.utils.AssertionUtils.*;
 import java.io.*;
 import java.nio.charset.*;
 
+import org.apache.juneau.http.response.*;
 import org.apache.juneau.marshall.parser.*;
 import org.apache.juneau.marshall.stream.*;
 
@@ -128,8 +129,9 @@ public final class ResponseBody {
         *
         * <p>
         * Equivalent to {@link #asCursor(Parser, Class) 
asCursor(negotiatedParser, type)} where the parser is selected
-        * from the response {@code Content-Type} header (defaulting to JSON 
when no match is found); use
-        * {@link #asCursor(Parser, Class)} to read with an explicit parser.
+        * from the response {@code Content-Type} header; when no registered 
parser matches and no default parser is
+        * configured, a <c>415 Unsupported Media Type</c> is thrown.  Use 
{@link #asCursor(Parser, Class)} to read with an
+        * explicit parser.
         *
         * @param <T> The cursor type ({@link RecordReader}, {@link 
TokenReader}, or a concrete subtype).
         * @param type The declared cursor type. Must not be <jk>null</jk>.
@@ -142,11 +144,12 @@ public final class ResponseBody {
        }
 
        /**
-        * Parses the response body to {@code type} using the parser negotiated 
from the response {@code Content-Type}
-        * (defaulting to JSON when the header is absent, or the client has no 
matching/configured parser).
+        * Parses the response body to {@code type} using the parser negotiated 
from the response {@code Content-Type}.
         *
         * <p>
-        * Use {@link #as(Parser, Class)} to force a specific parser, bypassing 
content negotiation.
+        * When the header is absent or matches no registered parser and no 
default parser is configured, a
+        * <c>415 Unsupported Media Type</c> is thrown.  Use {@link #as(Parser, 
Class)} to force a specific parser,
+        * bypassing content negotiation.
         *
         * @param <T> The type to parse to.
         * @param type The type to parse to. Must not be <jk>null</jk>.
@@ -188,7 +191,9 @@ public final class ResponseBody {
 
        private Parser negotiatedParser() {
                var h = response.getFirstHeader("Content-Type");
-               return response.getClient().getMatchingParser(h == null ? null 
: h.value());
+               var ct = h == null ? null : h.value();
+               return 
response.getClient().getMatchingParser(ct).orElseThrow(() -> new 
UnsupportedMediaType(
+                       "No parser matched the response Content-Type ''{0}'' 
and no default parser is configured on the client.", ct));
        }
 
        /**
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 94621b24f4..7c6ce46742 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
@@ -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 static org.apache.juneau.commons.utils.Utils.*;
 
 import java.io.*;
 import java.util.*;
@@ -30,7 +31,6 @@ import org.apache.juneau.http.entity.*;
 import org.apache.juneau.http.header.*;
 import org.apache.juneau.http.part.*;
 import org.apache.juneau.http.remote.*;
-import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.marshall.parser.*;
 import org.apache.juneau.marshall.serializer.*;
 import org.apache.juneau.rest.client.remote.*;
@@ -218,7 +218,12 @@ public final class RestClient implements Closeable {
        }
 
        /**
-        * Returns the serializer used for outbound bodies when the format is 
not otherwise discernable.
+        * Returns the explicitly-configured default serializer used for 
outbound bodies when the format is not otherwise
+        * discernable.
+        *
+        * <p>
+        * Only an explicit default set via {@link 
Builder#defaultSerializer(Serializer)} is honored — there is no implicit
+        * JSON fallback and no lone-registered-entry fallback.  A 
fully-unconfigured client returns {@link Optional#empty()}.
         *
         * <p>
         * <b>Beta — API subject to change:</b> This type is part of the 
next-generation REST client and HTTP stack
@@ -226,18 +231,20 @@ public final class RestClient implements Closeable {
         * It is not API-frozen: binary- and source-incompatible changes may 
appear in the <b>next major</b> Juneau release
         * (and possibly earlier).
         *
-        * @return The default serializer. Never <jk>null</jk>.
+        * @return The configured default serializer, or {@link 
Optional#empty()} if none was set.
         */
-       public Serializer getDefaultSerializer() {
-               if (defaultSerializer != null)
-                       return defaultSerializer;
-               if (serializers != null && ! serializers.isEmpty())
-                       return serializers.getSerializers().get(0);
-               return JsonSerializer.DEFAULT;
+       public Optional<Serializer> getDefaultSerializer() {
+               return opt(defaultSerializer);
        }
 
        /**
-        * Returns the parser matching the given response {@code Content-Type}, 
falling back to the default/JSON.
+        * Returns the parser matching the given response {@code Content-Type}, 
or the explicitly-configured default parser.
+        *
+        * <p>
+        * Resolution precedence: an exact media-type match against the 
client's {@link ParserSet} wins; otherwise the
+        * explicitly-configured {@link Builder#defaultParser(Parser) default 
parser} is used; otherwise
+        * {@link Optional#empty()} is returned.  There is no implicit JSON 
fallback and no lone-registered-entry fallback,
+        * so a fully-unconfigured client resolves to empty (callers throw 
<c>415 Unsupported Media Type</c>).
         *
         * <p>
         * <b>Beta — API subject to change:</b> This type is part of the 
next-generation REST client and HTTP stack
@@ -246,62 +253,58 @@ public final class RestClient implements Closeable {
         * (and possibly earlier).
         *
         * @param contentType The response {@code Content-Type} header value. 
May be <jk>null</jk>.
-        * @return The matching parser. Never <jk>null</jk>.
+        * @return The matching parser, or {@link Optional#empty()} if nothing 
matched and no default parser is configured.
         */
-       public Parser getMatchingParser(String contentType) {
+       public Optional<Parser> getMatchingParser(String contentType) {
                if (parsers != null && contentType != null) {
                        var p = parsers.getParser(contentType);
-                       if (p != null)
+                       if (p.isPresent())
                                return p;
                }
-               if (defaultParser != null)
-                       return defaultParser;
-               if (parsers != null && ! parsers.isEmpty())
-                       return parsers.getParsers().get(0);
-               return JsonParser.DEFAULT;
+               return opt(defaultParser);
        }
 
        /**
-        * Returns the registered request serializer matching the given media 
type, or <jk>null</jk> if none matches.
+        * Returns the registered request serializer matching the given media 
type, or {@link Optional#empty()} if none matches.
         *
         * <p>
         * Unlike {@link #getDefaultSerializer()}, this method performs 
media-type-driven <i>selection</i> against the
-        * client's {@link SerializerSet} (via {@link 
SerializerSet#getSerializer(String)}) and returns <jk>null</jk> on no
-        * match so the caller can apply the locked no-match fallback (use the 
default serializer but still send the
-        * overridden {@code Content-Type} label).  An explicitly-configured 
single default serializer is also consulted
-        * when no set is registered.
+        * client's {@link SerializerSet} (via {@link 
SerializerSet#getSerializer(String)}) and returns
+        * {@link Optional#empty()} on no match so the caller can apply the 
locked no-match fallback (use the default
+        * serializer but still send the overridden {@code Content-Type} 
label).  An explicitly-configured single default
+        * serializer is also consulted when no set is registered.
         *
         * <p>
         * <b>Beta — API subject to change:</b> This type is part of the 
next-generation REST client and HTTP stack
         * ({@code org.apache.juneau.marshall.ng.*}).
         *
         * @param mediaType The desired request media type. May be 
<jk>null</jk>/empty.
-        * @return The matching serializer, or <jk>null</jk> if no registered 
serializer matches.
+        * @return The matching serializer, or {@link Optional#empty()} if no 
registered serializer matches.
         */
-       public Serializer getSerializerForMediaType(String mediaType) {
+       public Optional<Serializer> getSerializerForMediaType(String mediaType) 
{
                if (mediaType == null || mediaType.isEmpty() || serializers == 
null)
-                       return null;
+                       return opte();
                return serializers.getSerializer(mediaType);
        }
 
        /**
-        * Returns the registered parser matching the given media type, or 
<jk>null</jk> if none matches.
+        * Returns the registered parser matching the given media type, or 
{@link Optional#empty()} if none matches.
         *
         * <p>
         * Used by the next-gen {@code RemoteClient} as the {@code accept} 
fallback parser: it is consulted only when the
         * response {@code Content-Type} matched no registered parser (or the 
response was unlabeled).  Returns
-        * <jk>null</jk> on no match so the caller can fall back to the default 
parser.
+        * {@link Optional#empty()} on no match so the caller can fall back to 
the default parser.
         *
         * <p>
         * <b>Beta — API subject to change:</b> This type is part of the 
next-generation REST client and HTTP stack
         * ({@code org.apache.juneau.marshall.ng.*}).
         *
         * @param mediaType The desired parse media type. May be 
<jk>null</jk>/empty.
-        * @return The matching parser, or <jk>null</jk> if no registered 
parser matches.
+        * @return The matching parser, or {@link Optional#empty()} if no 
registered parser matches.
         */
-       public Parser getParserForMediaType(String mediaType) {
+       public Optional<Parser> getParserForMediaType(String mediaType) {
                if (mediaType == null || mediaType.isEmpty() || parsers == null)
-                       return null;
+                       return opte();
                return parsers.getParser(mediaType);
        }
 
@@ -314,7 +317,8 @@ public final class RestClient implements Closeable {
         * It is not API-frozen: binary- and source-incompatible changes may 
appear in the <b>next major</b> Juneau release
         * (and possibly earlier).
         *
-        * @return The default {@code Accept} header value. Never <jk>null</jk>.
+        * @return The default {@code Accept} header value, or <jk>null</jk> if 
no parsers and no default parser are
+        *      configured (no implicit {@code application/json} fallback).
         */
        public String getDefaultAccept() {
                if (parsers != null) {
@@ -327,7 +331,7 @@ public final class RestClient implements Closeable {
                        if (! mts.isEmpty())
                                return 
mts.stream().map(MediaType::toString).collect(Collectors.joining(", "));
                }
-               return "application/json";
+               return null;
        }
 
        /**
@@ -567,7 +571,14 @@ public final class RestClient implements Closeable {
                }
 
                /**
-                * Designates the default serializer used when the outbound 
format is not otherwise discernable.
+                * Designates the default serializer used for outbound bodies 
when no registered serializer matches the
+                * requested media type.
+                *
+                * <p>
+                * This is the explicit, opt-in way to restore a fallback 
serializer.  Without it, a request that cannot be
+                * matched to a registered serializer (and any body requiring 
serialization on a serializer-less client) fails
+                * rather than silently defaulting to JSON.  Set {@code 
defaultSerializer(JsonSerializer.DEFAULT)} to recover
+                * the pre-10.0 implicit-JSON behavior.
                 *
                 * @param value The default serializer. May be <jk>null</jk>.
                 * @return This object.
@@ -578,7 +589,14 @@ public final class RestClient implements Closeable {
                }
 
                /**
-                * Designates the default parser used when the response {@code 
Content-Type} is absent or unmatched.
+                * Designates the default parser used when the response {@code 
Content-Type} is absent or matches no registered
+                * parser.
+                *
+                * <p>
+                * This is the explicit, opt-in way to restore a fallback 
parser.  Without it, a response whose
+                * {@code Content-Type} matches no registered parser fails with 
<c>415 Unsupported Media Type</c> rather than
+                * silently parsing as JSON.  Set {@code 
defaultParser(JsonParser.DEFAULT)} to recover the pre-10.0
+                * implicit-JSON behavior.
                 *
                 * @param value The default parser. May be <jk>null</jk>.
                 * @return This object.
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 85e76309c3..ba098126dd 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
@@ -244,12 +244,15 @@ public final class RestRequest {
         * Custom converters can be registered on the builder.
         *
         * <p>
-        * If no converter matches, the object is serialized with the client's 
default serializer
-        * ({@link RestClient#getDefaultSerializer()}) and sent as a string 
body using the serializer's content type.
+        * If no converter matches, the object is serialized with the client's 
explicitly-configured default serializer
+        * ({@link RestClient#getDefaultSerializer()}) and sent as a string 
body using the serializer's content type.  When
+        * no default serializer is configured, an {@link 
IllegalStateException} is raised (there is no implicit JSON
+        * fallback) — configure one via {@link 
RestClient.Builder#defaultSerializer(org.apache.juneau.marshall.serializer.Serializer)}.
         *
         * @param value The body object. May be <jk>null</jk> to clear the body.
         * @return This object.
         * @throws IOException If a converter fails or the default serializer 
fails.
+        * @throws IllegalStateException If no converter matches and no default 
serializer is configured on the client.
         * @throws IllegalArgumentException If the default serializer produces 
output that is neither text nor {@code byte[]}.
         *      Binary ({@code byte[]}) serializer output is sent as a binary 
body using the serializer's media type
         *      (falling back to {@code application/octet-stream} when the 
media type is <jk>null</jk>).
@@ -271,7 +274,8 @@ public final class RestRequest {
                // Mirrors classic SerializedEntity — the serializer writes 
straight to the transport output stream during
                // run() rather than being pre-materialized into a 
String/byte[].  Repeatable (re-serializes on resend), so a
                // future auto-retry can resend it; streaming bodies 
(InputStream/Reader) remain non-repeatable.
-               var s = client.getDefaultSerializer();
+               var s = client.getDefaultSerializer().orElseThrow(() -> new 
IllegalStateException(
+                       "No default serializer is configured on the client.  
Configure one via RestClient.Builder.defaultSerializer(...)."));
                var mt = s.getResponseContentType();
                body = SerializerBody.of(s, value, mt != null ? mt.toString() : 
"application/json");
                convertedBody = null;
diff --git 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteClient.java
 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteClient.java
index 818597d01e..1d77235375 100644
--- 
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteClient.java
+++ 
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/remote/RemoteClient.java
@@ -265,21 +265,22 @@ public final class RemoteClient {
                 *
                 * <p>
                 * When a {@code contentType} media type is in effect, the 
matching registered request serializer is selected
-                * from the client's serializer set ({@link 
RestClient#getSerializerForMediaType(String)}).  Per the locked
-                * no-match fallback, if no registered serializer matches, the 
client's default serializer is used to write the
-                * body but the overridden media type is still sent as the 
{@code Content-Type} label (supporting vendor media
-                * types such as {@code application/vnd.foo+json} whose bytes 
are really the default format).
+                * from the client's serializer set ({@link 
RestClient#getSerializerForMediaType(String)}).  If no registered
+                * serializer matches, the client's explicitly-configured 
default serializer is used to write the body but the
+                * overridden media type is still sent as the {@code 
Content-Type} label (supporting vendor media types such as
+                * {@code application/vnd.foo+json} whose bytes are really the 
default format).  If neither a match nor a default
+                * serializer is available, an {@link IllegalStateException} is 
raised (there is no implicit JSON fallback).
                 *
                 * @param methodMeta The method metadata.
                 * @return The resolved body format; {@link BodyFormat#NONE} 
when no {@code contentType} attribute is in effect.
+                * @throws IllegalStateException If no serializer matches and 
no default serializer is configured on the client.
                 */
                private BodyFormat resolveBodyFormat(RrpcInterfaceMethodMeta 
methodMeta) {
                        var contentType = 
firstNonEmpty(methodMeta.getContentType(), meta.getContentType());
                        if (isEmpty(contentType))
                                return BodyFormat.NONE;
-                       var serializer = 
client.getSerializerForMediaType(contentType);
-                       if (serializer == null)
-                               serializer = client.getDefaultSerializer();  // 
No-match fallback: default bytes, overridden label.
+                       var serializer = 
client.getSerializerForMediaType(contentType).or(client::getDefaultSerializer).orElseThrow(()
 ->
+                               new IllegalStateException("No serializer 
matched Content-Type '" + contentType + "' and no default serializer is 
configured on the client.  Configure one via 
RestClient.Builder.defaultSerializer(...)."));
                        return new BodyFormat(serializer, contentType);
                }
 
@@ -1171,7 +1172,8 @@ public final class RemoteClient {
                        if (isScalarPart(arg))
                                return StringBody.of(arg.toString(), 
firstNonEmpty(contentType, "text/plain; charset=UTF-8"));
                        // Bean part: stream it through the client's default 
serializer (no full in-memory materialization).
-                       var s = client.getDefaultSerializer();
+                       var s = client.getDefaultSerializer().orElseThrow(() -> 
new IllegalStateException(
+                               "No default serializer is configured on the 
client.  Configure one via RestClient.Builder.defaultSerializer(...)."));
                        return contentType != null ? SerializerBody.of(s, arg, 
contentType) : SerializerBody.of(s, arg);
                }
 
@@ -1249,25 +1251,29 @@ public final class RemoteClient {
                 *      <li>If the response {@code Content-Type} matches a 
registered parser, use it.
                 *      <li>Otherwise (response unlabeled or its type matched 
nothing), use the parser matching the {@code accept}
                 *              media type, if one is registered.
-                *      <li>Otherwise fall back to the client default parser 
({@link RestClient#getMatchingParser(String)}).
+                *      <li>Otherwise fall back to the client's 
explicitly-configured default parser
+                *              ({@link RestClient#getMatchingParser(String)}); 
if none is configured this throws
+                *              <c>415 Unsupported Media Type</c> (there is no 
implicit JSON fallback).
                 * </ol>
                 *
                 * @param resp The response (source of the client + parsers).
                 * @param responseContentType The response {@code Content-Type} 
header value. May be <jk>null</jk>.
                 * @param acceptFallback The {@code accept} media type 
fallback. May be <jk>null</jk>/empty.
                 * @return The parser to use. Never <jk>null</jk>.
+                * @throws UnsupportedMediaType If no parser matches and no 
default parser is configured on the client.
                 */
                private static Parser selectParser(RestResponse resp, String 
responseContentType, String acceptFallback) {
                        var c = resp.getClient();
                        var p = c.getParserForMediaType(responseContentType);
-                       if (p != null)
-                               return p;
+                       if (p.isPresent())
+                               return p.get();
                        if (isNotEmpty(acceptFallback)) {
                                var ap = 
c.getParserForMediaType(acceptFallback);
-                               if (ap != null)
-                                       return ap;
+                               if (ap.isPresent())
+                                       return ap.get();
                        }
-                       return c.getMatchingParser(responseContentType);
+                       return 
c.getMatchingParser(responseContentType).orElseThrow(() -> new 
UnsupportedMediaType(
+                               "No parser matched the response Content-Type 
''{0}'' and no default parser is configured on the client.", 
responseContentType));
                }
 
                private static String combinePaths(String base, String method) {
diff --git 
a/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/ResponseBody_Cursor_Test.java
 
b/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/ResponseBody_Cursor_Test.java
index 45bc9a795d..d69c5fe0af 100644
--- 
a/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/ResponseBody_Cursor_Test.java
+++ 
b/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/ResponseBody_Cursor_Test.java
@@ -48,7 +48,9 @@ class ResponseBody_Cursor_Test {
                        .header("Content-Type", "application/json")
                        .body(new 
ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)))
                        .build();
-               return new RestResponse(tr, RestClient.create());
+               // Register a JSON parser so the negotiated-parser cursor paths 
resolve from the application/json Content-Type
+               // (there is no implicit JSON fallback as of 10.0.0).
+               return new RestResponse(tr, 
RestClient.builder().parser(JsonParser.DEFAULT).build());
        }
 
        // 
==========================================================================
diff --git 
a/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/RestClient_Negotiation_Test.java
 
b/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/RestClient_Negotiation_Test.java
index cef31d6095..ff6ea3a05d 100644
--- 
a/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/RestClient_Negotiation_Test.java
+++ 
b/juneau-rest/juneau-rest-client/src/test/java/org/apache/juneau/rest/client/RestClient_Negotiation_Test.java
@@ -24,15 +24,24 @@ import org.apache.juneau.marshall.parser.*;
 import org.apache.juneau.marshall.serializer.*;
 import org.junit.jupiter.api.*;
 
+/**
+ * Content-negotiation resolver contract for the next-gen {@link RestClient}.
+ *
+ * <p>
+ * As of 10.0.0 the implicit JSON fallback and the lone-registered-entry 
fallback are removed: a serializer/parser is
+ * resolved only by an exact media-type match or by an explicitly-configured 
{@code defaultSerializer(...)} /
+ * {@code defaultParser(...)}; otherwise the resolver is genuinely empty (and 
callers throw 415 / a client-side error).
+ */
 class RestClient_Negotiation_Test {
 
        @Test
-       void a01_unconfigured_defaultsToJson() throws Exception {
+       void a01_unconfigured_resolvesEmpty() throws Exception {
+               // No serializers/parsers and no explicit defaults — everything 
resolves empty (no implicit JSON).
                try (var c = RestClient.create()) {
-                       assertSame(JsonSerializer.DEFAULT, 
c.getDefaultSerializer());
-                       assertSame(JsonParser.DEFAULT, 
c.getMatchingParser(null));
-                       assertSame(JsonParser.DEFAULT, 
c.getMatchingParser("application/jsonl"));
-                       assertEquals("application/json", c.getDefaultAccept());
+                       assertTrue(c.getDefaultSerializer().isEmpty());
+                       assertTrue(c.getMatchingParser(null).isEmpty());
+                       
assertTrue(c.getMatchingParser("application/jsonl").isEmpty());
+                       assertNull(c.getDefaultAccept());
                }
        }
 
@@ -41,17 +50,18 @@ class RestClient_Negotiation_Test {
                var sset = SerializerSet.create().add(JsonSerializer.DEFAULT, 
JsonlSerializer.DEFAULT).build();
                var pset = ParserSet.create().add(JsonParser.DEFAULT, 
JsonlParser.DEFAULT).build();
                try (var c = 
RestClient.builder().serializers(sset).parsers(pset).build()) {
-                       assertInstanceOf(JsonlParser.class, 
c.getMatchingParser("application/jsonl"));
-                       assertInstanceOf(JsonParser.class, 
c.getMatchingParser("application/json"));
-                       assertSame(JsonSerializer.DEFAULT, 
c.getDefaultSerializer());
-                       assertInstanceOf(JsonParser.class, 
c.getMatchingParser("text/unknown"));
+                       assertInstanceOf(JsonlParser.class, 
c.getMatchingParser("application/jsonl").orElseThrow());
+                       assertInstanceOf(JsonParser.class, 
c.getMatchingParser("application/json").orElseThrow());
+                       // A registered set is NOT a default — no implicit 
first-entry/JSON fallback.
+                       assertTrue(c.getDefaultSerializer().isEmpty());
+                       
assertTrue(c.getMatchingParser("text/unknown").isEmpty());
                        
assertTrue(c.getDefaultAccept().contains("application/json"));
                        
assertTrue(c.getDefaultAccept().contains("application/jsonl"));
                }
        }
 
        @Test
-       void a03_explicitDefaultOverridesFirstInSet() throws Exception {
+       void a03_explicitDefaultIsUsedForUnmatched() throws Exception {
                var sset = SerializerSet.create().add(JsonSerializer.DEFAULT, 
JsonlSerializer.DEFAULT).build();
                var pset = ParserSet.create().add(JsonParser.DEFAULT, 
JsonlParser.DEFAULT).build();
                try (var c = RestClient.builder()
@@ -59,20 +69,23 @@ class RestClient_Negotiation_Test {
                                .defaultSerializer(JsonlSerializer.DEFAULT)
                                .defaultParser(JsonlParser.DEFAULT)
                                .build()) {
-                       assertSame(JsonlSerializer.DEFAULT, 
c.getDefaultSerializer());
-                       assertSame(JsonlParser.DEFAULT, 
c.getMatchingParser("text/unknown"));
-                       assertInstanceOf(JsonParser.class, 
c.getMatchingParser("application/json"));
+                       assertSame(JsonlSerializer.DEFAULT, 
c.getDefaultSerializer().orElseThrow());
+                       assertSame(JsonlParser.DEFAULT, 
c.getMatchingParser("text/unknown").orElseThrow());
+                       // Exact match still wins over the configured default.
+                       assertInstanceOf(JsonParser.class, 
c.getMatchingParser("application/json").orElseThrow());
                }
        }
 
        @Test
-       void a04_appendConvenience() throws Exception {
+       void a04_appendConvenience_isNotADefault() throws Exception {
+               // Appending serializers/parsers registers them in the set but 
does NOT make them the default.
                try (var c = RestClient.builder()
                                .serializer(JsonlSerializer.DEFAULT)
                                .parser(JsonlParser.DEFAULT)
                                .build()) {
-                       assertSame(JsonlSerializer.DEFAULT, 
c.getDefaultSerializer());
-                       assertInstanceOf(JsonlParser.class, 
c.getMatchingParser("application/jsonl"));
+                       assertTrue(c.getDefaultSerializer().isEmpty());
+                       assertInstanceOf(JsonlParser.class, 
c.getMatchingParser("application/jsonl").orElseThrow());
+                       
assertTrue(c.getMatchingParser("text/unknown").isEmpty());
                }
        }
 
@@ -82,8 +95,8 @@ class RestClient_Negotiation_Test {
                                .defaultSerializer(JsonlSerializer.DEFAULT)
                                .defaultParser(JsonlParser.DEFAULT)
                                .build()) {
-                       assertSame(JsonlSerializer.DEFAULT, 
c.getDefaultSerializer());
-                       assertSame(JsonlParser.DEFAULT, 
c.getMatchingParser("text/unknown"));
+                       assertSame(JsonlSerializer.DEFAULT, 
c.getDefaultSerializer().orElseThrow());
+                       assertSame(JsonlParser.DEFAULT, 
c.getMatchingParser("text/unknown").orElseThrow());
                }
        }
 
@@ -95,15 +108,6 @@ class RestClient_Negotiation_Test {
                }
        }
 
-       @Test
-       void a08_loneDefaultParserAdvertisesItsMediaTypes() throws Exception {
-               try (var c = 
RestClient.builder().defaultParser(JsonlParser.DEFAULT).build()) {
-                       var accept = c.getDefaultAccept();
-                       assertTrue(accept.startsWith("application/jsonl"), () 
-> "Default Accept was: " + accept);
-                       assertNotEquals("application/json", accept, () -> 
"Default Accept was: " + accept);
-               }
-       }
-
        @Test
        void a07_explicitSetWinsOverAppend() throws Exception {
                var sset = 
SerializerSet.create().add(JsonSerializer.DEFAULT).build();
@@ -113,10 +117,41 @@ class RestClient_Negotiation_Test {
                                .serializer(JsonlSerializer.DEFAULT)
                                .parser(JsonlParser.DEFAULT)
                                .build()) {
-                       assertSame(JsonSerializer.DEFAULT, 
c.getDefaultSerializer());
-                       assertInstanceOf(JsonParser.class, 
c.getMatchingParser("text/unknown"));
-                       assertInstanceOf(JsonParser.class, 
c.getMatchingParser("application/jsonl"));
+                       // The appended jsonl entries are ignored because an 
explicit set was supplied.
+                       assertInstanceOf(JsonParser.class, 
c.getMatchingParser("application/json").orElseThrow());
+                       
assertTrue(c.getMatchingParser("application/jsonl").isEmpty());
+                       
assertTrue(c.getMatchingParser("text/unknown").isEmpty());
+                       assertTrue(c.getDefaultSerializer().isEmpty());
                        assertFalse(c.getDefaultAccept().contains("jsonl"));
                }
        }
+
+       @Test
+       void a08_loneDefaultParserAdvertisesItsMediaTypes() throws Exception {
+               try (var c = 
RestClient.builder().defaultParser(JsonlParser.DEFAULT).build()) {
+                       var accept = c.getDefaultAccept();
+                       assertTrue(accept.startsWith("application/jsonl"), () 
-> "Default Accept was: " + accept);
+                       assertNotEquals("application/json", accept, () -> 
"Default Accept was: " + accept);
+               }
+       }
+
+       @Test
+       void a09_exactMatchWinsOverExplicitDefault() throws Exception {
+               var pset = ParserSet.create().add(JsonParser.DEFAULT, 
JsonlParser.DEFAULT).build();
+               try (var c = 
RestClient.builder().parsers(pset).defaultParser(JsonParser.DEFAULT).build()) {
+                       // Exact match on the registered set takes precedence 
over the explicit default.
+                       assertInstanceOf(JsonlParser.class, 
c.getMatchingParser("application/jsonl").orElseThrow());
+                       // Unmatched falls back to the explicit default.
+                       assertSame(JsonParser.DEFAULT, 
c.getMatchingParser("text/unknown").orElseThrow());
+               }
+       }
+
+       @Test
+       void a10_defaultSerializerOptInRestoresFallback() throws Exception {
+               try (var unconfigured = RestClient.create();
+                        var configured = 
RestClient.builder().defaultSerializer(JsonSerializer.DEFAULT).build()) {
+                       
assertTrue(unconfigured.getDefaultSerializer().isEmpty());
+                       assertSame(JsonSerializer.DEFAULT, 
configured.getDefaultSerializer().orElseThrow());
+               }
+       }
 }
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 6e2626a27f..c81477f149 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
@@ -25,6 +25,7 @@ import java.util.concurrent.*;
 
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.marshall.*;
+import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.rest.client.*;
 import org.apache.juneau.rest.client.RestRequest;
 import org.apache.juneau.rest.server.*;
@@ -271,6 +272,7 @@ public final class MockRestClient implements Closeable {
                                var ngClient = RestClient.builder()
                                        .transport(transport)
                                        .rootUrl("http://localhost"; + 
fullContextPath)
+                                       
.defaultSerializer(JsonSerializer.DEFAULT)
                                        .build();
 
                                return new MockRestClient(ngClient);
diff --git 
a/juneau-rest/juneau-rest-mock/src/test/java/org/apache/juneau/rest/mock/NextGenContentNegotiation_Test.java
 
b/juneau-rest/juneau-rest-mock/src/test/java/org/apache/juneau/rest/mock/NextGenContentNegotiation_Test.java
index dbba7c281a..883f3e2062 100644
--- 
a/juneau-rest/juneau-rest-mock/src/test/java/org/apache/juneau/rest/mock/NextGenContentNegotiation_Test.java
+++ 
b/juneau-rest/juneau-rest-mock/src/test/java/org/apache/juneau/rest/mock/NextGenContentNegotiation_Test.java
@@ -84,7 +84,8 @@ class NextGenContentNegotiation_Test {
        void a01_outboundContentTypeFromDefaultSerializer() throws Exception {
                var sset = 
SerializerSet.create().add(JsonlSerializer.DEFAULT).build();
                try (var mock = MockRestClient.create(JsonlServer.class)) {
-                       try (var nc = 
RestClient.builder().transport(mock.getClient().getTransport()).serializers(sset).build())
 {
+                       // A registered set is no longer an implicit default — 
designate the default serializer explicitly.
+                       try (var nc = 
RestClient.builder().transport(mock.getClient().getTransport()).serializers(sset).defaultSerializer(JsonlSerializer.DEFAULT).build())
 {
                                var rawBody = 
nc.post("/echoContentType").body(new Bean("a", 1)).run().body().asString();
                                var echoed = JsonParser.DEFAULT.parse(rawBody, 
String.class);
                                
assertTrue(echoed.startsWith("application/jsonl"), () -> "Echoed Content-Type 
was: " + echoed);
diff --git 
a/juneau-rest/juneau-rest-mock/src/test/java/org/apache/juneau/rest/mock/RemoteCursorBinding_NextGen_Test.java
 
b/juneau-rest/juneau-rest-mock/src/test/java/org/apache/juneau/rest/mock/RemoteCursorBinding_NextGen_Test.java
index f21aff0dcb..47ec943ae4 100644
--- 
a/juneau-rest/juneau-rest-mock/src/test/java/org/apache/juneau/rest/mock/RemoteCursorBinding_NextGen_Test.java
+++ 
b/juneau-rest/juneau-rest-mock/src/test/java/org/apache/juneau/rest/mock/RemoteCursorBinding_NextGen_Test.java
@@ -35,8 +35,8 @@ import org.junit.jupiter.api.*;
  * <p>
  * Verifies that {@code RecordReader} / {@code TokenReader} (and concrete 
subtypes like {@code JsonTokenReader}) work
  * as {@code @RemoteOp} return types, and that {@code RecordStreamBody} works 
as a {@code @Content} parameter and as a
- * directly-set request body.  The next-generation client performs no {@code 
Content-Type} negotiation, so cursors use
- * the default JSON marshaller.
+ * directly-set request body.  Each test builds a next-generation client 
configured with a JSON parser so the
+ * inbound cursor parser is negotiated from the response {@code Content-Type}.
  */
 class RemoteCursorBinding_NextGen_Test {
 
@@ -84,8 +84,9 @@ class RemoteCursorBinding_NextGen_Test {
                "resource" // Inner client returned by getClient() is owned by 
the MockRestClient, not closed separately.
        })
        void a01_recordReaderReturnType() throws Exception {
-               try (var client = MockRestClient.create(JsonServer.class)) {
-                       var api = 
client.getClient().remote(JsonClientApi.class);
+               try (var client = MockRestClient.create(JsonServer.class);
+                               var nc = 
RestClient.builder().transport(client.getClient().getTransport()).parser(JsonParser.DEFAULT).build())
 {
+                       var api = nc.remote(JsonClientApi.class);
                        try (RecordReader r = api.getBean()) {
                                var b = r.read(Bean.class);
                                assertEquals("alice", b.name);
@@ -99,8 +100,9 @@ class RemoteCursorBinding_NextGen_Test {
                "resource" // Inner client returned by getClient() is owned by 
the MockRestClient, not closed separately.
        })
        void a02_tokenReaderReturnType() throws Exception {
-               try (var client = MockRestClient.create(JsonServer.class)) {
-                       var api = 
client.getClient().remote(JsonClientApi.class);
+               try (var client = MockRestClient.create(JsonServer.class);
+                               var nc = 
RestClient.builder().transport(client.getClient().getTransport()).parser(JsonParser.DEFAULT).build())
 {
+                       var api = nc.remote(JsonClientApi.class);
                        try (TokenReader r = api.getBeanAsTokens()) {
                                var b = r.read(Bean.class);
                                assertEquals("alice", b.name);
@@ -114,8 +116,9 @@ class RemoteCursorBinding_NextGen_Test {
                "resource" // Inner client returned by getClient() is owned by 
the MockRestClient, not closed separately.
        })
        void a03_concreteCursorReturnType() throws Exception {
-               try (var client = MockRestClient.create(JsonServer.class)) {
-                       var api = 
client.getClient().remote(JsonClientApi.class);
+               try (var client = MockRestClient.create(JsonServer.class);
+                               var nc = 
RestClient.builder().transport(client.getClient().getTransport()).parser(JsonParser.DEFAULT).build())
 {
+                       var api = nc.remote(JsonClientApi.class);
                        try (JsonTokenReader r = api.getBeanAsJsonTokens()) {
                                var b = r.read(Bean.class);
                                assertEquals("alice", b.name);
@@ -132,8 +135,9 @@ class RemoteCursorBinding_NextGen_Test {
                "resource" // Fluent writer calls return the caller-owned 
writer for chaining; nothing new to close.
        })
        void b01_recordStreamBody_record() throws Exception {
-               try (var client = MockRestClient.create(JsonServer.class)) {
-                       var api = 
client.getClient().remote(JsonClientApi.class);
+               try (var client = MockRestClient.create(JsonServer.class);
+                               var nc = 
RestClient.builder().transport(client.getClient().getTransport()).parser(JsonParser.DEFAULT).build())
 {
+                       var api = nc.remote(JsonClientApi.class);
                        Bean got = api.echo(RecordStreamBody.records(w -> {
                                try {
                                        w.write(new Bean("dave", 99));
@@ -151,8 +155,9 @@ class RemoteCursorBinding_NextGen_Test {
                "resource" // Fluent writer calls return the caller-owned 
writer for chaining; nothing new to close.
        })
        void b02_recordStreamBody_token() throws Exception {
-               try (var client = MockRestClient.create(JsonServer.class)) {
-                       var api = 
client.getClient().remote(JsonClientApi.class);
+               try (var client = MockRestClient.create(JsonServer.class);
+                               var nc = 
RestClient.builder().transport(client.getClient().getTransport()).parser(JsonParser.DEFAULT).build())
 {
+                       var api = nc.remote(JsonClientApi.class);
                        Bean got = api.echo(RecordStreamBody.token(w -> {
                                try {
                                        w.startObject();
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
index eb66db0a2a..6269e26321 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/RestResponse.java
@@ -491,7 +491,7 @@ public class RestResponse extends 
HttpServletResponseWrapper {
                if (nn(serializer)) {
                        serializerMatch = opt(new 
SerializerMatch(getMediaType(), serializer));
                } else {
-                       serializerMatch = 
opt(opContext.getSerializers().getSerializerMatch(request.getHeaderParam("Accept").orElse("*/*")));
+                       serializerMatch = 
opContext.getSerializers().getSerializerMatch(request.getHeaderParam("Accept").orElse("*/*"));
                }
                return serializerMatch;
        }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/httppart/RequestContent.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/httppart/RequestContent.java
index 88cd167264..06540661b1 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/httppart/RequestContent.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/httppart/RequestContent.java
@@ -397,7 +397,7 @@ public class RequestContent {
                if (nn(mediaType) && nn(parser))
                        return opt(new ParserMatch(mediaType, parser));
                var mt = getMediaType();
-               return opt(mt).map(x -> parsers.getParserMatch(x));
+               return opt(mt).flatMap(parsers::getParserMatch);
        }
 
        /**
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
index 9f6b43e304..78e2142f31 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/swagger/BasicSwaggerProviderSession.java
@@ -733,7 +733,7 @@ public class BasicSwaggerProviderSession {
 
                for (var mt : mediaTypes) {
                        if (mt != MediaType.HTML) {
-                               var s2 = sm.getSerializers().getSerializer(mt);
+                               var s2 = 
sm.getSerializers().getSerializer(mt).orElse(null);
                                if (nn(s2)) {
                                        try {
                                                // @formatter:off
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/SerializedRequestAttrVar.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/SerializedRequestAttrVar.java
index 7ff76d1b8e..fb67b929ca 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/SerializedRequestAttrVar.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/vars/SerializedRequestAttrVar.java
@@ -69,7 +69,7 @@ public class SerializedRequestAttrVar extends StreamedVar {
                var s2 = splita(key);
                var req = 
session.getBean(RestRequest.class).orElseThrow(InternalServerError::new);
                var o = req.getAttribute(key).orElse(key);
-               Serializer s = 
req.getOpContext().getSerializers().getSerializer(s2[0]);
+               Serializer s = 
req.getOpContext().getSerializers().getSerializer(s2[0]).orElse(null);
                if (nn(s))
                        s.serialize(w, o);
        }
diff --git 
a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/server/vars/RestServerVars_Test.java
 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/server/vars/RestServerVars_Test.java
index a4646f43d4..7358faf609 100644
--- 
a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/server/vars/RestServerVars_Test.java
+++ 
b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/server/vars/RestServerVars_Test.java
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
 import java.io.*;
+import java.util.*;
 import org.junit.jupiter.api.*;
 
 import org.apache.juneau.*;
@@ -376,7 +377,7 @@ class RestServerVars_Test extends TestBase {
        @Test void 
e05_serializedRequestAttrVar_resolveTo_serializerNotFound_noOutput() throws 
Exception {
                var req = mock(RestRequest.class, RETURNS_DEEP_STUBS);
                
when(req.getAttribute(any())).thenReturn(mock(RequestAttribute.class));
-               
when(req.getOpContext().getSerializers().getSerializer(any(String.class))).thenReturn(null);
+               
when(req.getOpContext().getSerializers().getSerializer(any(String.class))).thenReturn(opte());
                var w = new StringWriter();
                new SerializedRequestAttrVar().resolveTo(sessionWith(req), w, 
"text/plain,myKey");
                assertEquals("", w.toString());
@@ -392,7 +393,7 @@ class RestServerVars_Test extends TestBase {
                var attr = mock(RequestAttribute.class);
                when(attr.orElse(any())).thenReturn(outputSb);
                when(req.getAttribute(any())).thenReturn(attr);
-               
when(req.getOpContext().getSerializers().getSerializer(any(String.class))).thenReturn(Json5Serializer.DEFAULT);
+               
when(req.getOpContext().getSerializers().getSerializer(any(String.class))).thenReturn(Optional.of(Json5Serializer.DEFAULT));
                new SerializedRequestAttrVar().resolveTo(sessionWith(req), new 
StringWriter(), "application/json,myKey");
                assertFalse(outputSb.toString().isEmpty());
        }


Reply via email to