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 dd84878120 TODO-278: CRTP-convert the SerializerWriter format-writer 
hierarchy (10.0.0 breaking change)
dd84878120 is described below

commit dd848781209600eb64bb339a5eddd7f4831b2fb5
Author: James Bognar <[email protected]>
AuthorDate: Tue Jul 21 16:26:11 2026 -0400

    TODO-278: CRTP-convert the SerializerWriter format-writer hierarchy (10.0.0 
breaking change)
    
    Full self-type conversion (family 4 of 4, previous family TODO-277 html5): 
SerializerWriter
    generified into an abstract self-typed root (SerializerWriter<SELF extends 
SerializerWriter<SELF>>
    extends java.io.Writer), with SELF-returning fluent methods including the 
java.io.Writer
    append(...) overloads (legal covariant override, confirmed by spike). ~330 
covariant
    return-narrowing overrides removed across the 13 format writers.
    
    Concrete-and-base split (Option A) for the three writers that are both 
directly instantiated
    and subclassed: JsonWriter/XmlWriter/HtmlWriter became abstract generic 
bases, each paired with
    a new concrete leaf (BasicJsonWriter/BasicXmlWriter/BasicHtmlWriter) for 
direct-new sites;
    JcsWriter/SimpleHtmlWriter keep only their real-logic overrides. No 
mutation funnel added (hot-path
    writer, plain CRTP only per Q2).
    
    Serializer output verified byte-identical. Impact is internal to 
juneau-marshall only.
    
    Co-authored-by: Cursor <[email protected]>
---
 .../org/apache/juneau/marshall/csv/CsvWriter.java  | 134 +-------
 .../apache/juneau/marshall/hjson/HjsonWriter.java  |  26 +-
 .../apache/juneau/marshall/hocon/HoconWriter.java  |  26 +-
 .../juneau/marshall/html/BasicHtmlDocTemplate.java |  24 +-
 .../juneau/marshall/html/BasicHtmlWriter.java      |  63 ++++
 .../juneau/marshall/html/HtmlDocTemplate.java      |   2 +-
 .../marshall/html/HtmlSerializerSession.java       |  20 +-
 .../apache/juneau/marshall/html/HtmlWriter.java    | 347 +--------------------
 .../juneau/marshall/html/SimpleHtmlWriter.java     | 346 +-------------------
 .../org/apache/juneau/marshall/ini/IniWriter.java  |  26 +-
 .../juneau/marshall/jcs/JcsSerializerSession.java  |   6 +-
 .../org/apache/juneau/marshall/jcs/JcsWriter.java  |   2 +-
 .../juneau/marshall/json/BasicJsonWriter.java      |  55 ++++
 .../marshall/json/JsonSerializerSession.java       |  16 +-
 .../apache/juneau/marshall/json/JsonWriter.java    | 161 +---------
 .../marshall/json5/Json5SerializerSession.java     |   4 +-
 .../marshall/json5l/Json5lSerializerSession.java   |   4 +-
 .../juneau/marshall/markdown/MarkdownWriter.java   |   2 +-
 .../juneau/marshall/prototext/PrototextWriter.java |  26 +-
 .../marshall/serializer/SerializerWriter.java      |  98 +++---
 .../marshall/soap/SoapXmlSerializerSession.java    |   2 +-
 .../apache/juneau/marshall/toml/TomlWriter.java    |  26 +-
 .../juneau/marshall/uon/UonSerializerSession.java  |  10 +-
 .../org/apache/juneau/marshall/uon/UonWriter.java  | 128 +-------
 .../urlencoding/UrlEncodingSerializerSession.java  |  10 +-
 .../apache/juneau/marshall/xml/BasicXmlWriter.java |  58 ++++
 .../juneau/marshall/xml/XmlSerializerSession.java  |  16 +-
 .../org/apache/juneau/marshall/xml/XmlWriter.java  | 245 ++++-----------
 .../marshall/yaml/YamlSerializerSession.java       |   6 +-
 .../apache/juneau/marshall/yaml/YamlWriter.java    | 138 +-------
 30 files changed, 382 insertions(+), 1645 deletions(-)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/csv/CsvWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/csv/CsvWriter.java
index 240628f92c..5aea817a88 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/csv/CsvWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/csv/CsvWriter.java
@@ -33,7 +33,7 @@ import org.apache.juneau.marshall.serializer.*;
        "resource",  // Writer resource managed by calling code
        "java:S3776" // Cognitive complexity acceptable for CSV cell 
quoting/escaping logic
 })
-public class CsvWriter extends SerializerWriter {
+public class CsvWriter extends SerializerWriter<CsvWriter> {
 
        /**
         * Constructor.
@@ -49,138 +49,6 @@ public class CsvWriter extends SerializerWriter {
                super(out, useWhitespace, maxIndent, trimStrings, quoteChar, 
uriResolver);
        }
 
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter append(char c) {
-               super.append(c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter append(char[] value) {
-               super.append(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter append(int indent, char c) {
-               super.append(indent, c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter append(int indent, String text) {
-               super.append(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter append(Object text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter append(String text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter appendIf(boolean b, char c) {
-               super.appendIf(b, c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter appendIf(boolean b, String text) {
-               super.appendIf(b, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter appendln(int indent, String text) {
-               super.appendln(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter appendln(String text) {
-               super.appendln(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter appendUri(Object value) {
-               super.appendUri(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter cr(int depth) {
-               super.cr(depth);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter cre(int depth) {
-               super.cre(depth);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter i(int indent) {
-               super.i(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter ie(int indent) {
-               super.ie(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter nl(int indent) {
-               super.nl(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter nlIf(boolean flag, int indent) {
-               super.nlIf(flag, indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter q() {
-               super.q();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter s() {
-               super.s();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter sIf(boolean flag) {
-               super.sIf(flag);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter w(char value) {
-               super.w(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public CsvWriter w(String value) {
-               super.w(value);
-               return this;
-       }
-
        /**
         * Writes an entry to the writer.
         *
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/hjson/HjsonWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/hjson/HjsonWriter.java
index 6e79de63ea..ddbe2844b7 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/hjson/HjsonWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/hjson/HjsonWriter.java
@@ -37,7 +37,7 @@ import org.apache.juneau.marshall.serializer.*;
        "java:S135",  // Multiple break/continue necessary for isNumber char 
validation loop
        "resource"    // Writer resource managed by calling code
 })
-public class HjsonWriter extends SerializerWriter {
+public class HjsonWriter extends SerializerWriter<HjsonWriter> {
 
        private static final AsciiSet QUOTE_KEY_CHARS = AsciiSet.of(" 
\t\n\r{},:[]\"'#/");
        private static final AsciiSet QUOTE_VALUE_CHARS = 
AsciiSet.of("\n\r{},:\"'#");
@@ -301,28 +301,4 @@ public class HjsonWriter extends SerializerWriter {
                w("'''");
                return this;
        }
-
-       // Override return types for chaining
-       @Override public HjsonWriter append(char c) { super.append(c); return 
this; }
-       @Override public HjsonWriter append(char[] value) { 
super.append(value); return this; }
-       @Override public HjsonWriter append(int indent, char c) { 
super.append(indent, c); return this; }
-       @Override public HjsonWriter append(int indent, String text) { 
super.append(indent, text); return this; }
-       @Override public HjsonWriter append(Object text) { super.append(text); 
return this; }
-       @Override public HjsonWriter append(String text) { super.append(text); 
return this; }
-       @Override public HjsonWriter appendIf(boolean b, char c) { 
super.appendIf(b, c); return this; }
-       @Override public HjsonWriter appendIf(boolean b, String text) { 
super.appendIf(b, text); return this; }
-       @Override public HjsonWriter appendln(int indent, String text) { 
super.appendln(indent, text); return this; }
-       @Override public HjsonWriter appendln(String text) { 
super.appendln(text); return this; }
-       @Override public HjsonWriter appendUri(Object value) { 
super.appendUri(value); return this; }
-       @Override public HjsonWriter cr(int depth) { super.cr(depth); return 
this; }
-       @Override public HjsonWriter cre(int depth) { super.cre(depth); return 
this; }
-       @Override public HjsonWriter i(int indent) { super.i(indent); return 
this; }
-       @Override public HjsonWriter ie(int indent) { super.ie(indent); return 
this; }
-       @Override public HjsonWriter nl(int indent) { super.nl(indent); return 
this; }
-       @Override public HjsonWriter nlIf(boolean flag, int indent) { 
super.nlIf(flag, indent); return this; }
-       @Override public HjsonWriter q() { super.q(); return this; }
-       @Override public HjsonWriter s() { super.s(); return this; }
-       @Override public HjsonWriter sIf(boolean flag) { super.sIf(flag); 
return this; }
-       @Override public HjsonWriter w(char value) { super.w(value); return 
this; }
-       @Override public HjsonWriter w(String value) { super.w(value); return 
this; }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/hocon/HoconWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/hocon/HoconWriter.java
index c952328631..f0b0acbd87 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/hocon/HoconWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/hocon/HoconWriter.java
@@ -39,7 +39,7 @@ import org.apache.juneau.marshall.serializer.*;
        "java:S135",  // Multiple break/continue necessary for isNumber char 
validation loop
        "resource"    // Writer resource managed by calling code
 })
-public class HoconWriter extends SerializerWriter {
+public class HoconWriter extends SerializerWriter<HoconWriter> {
 
        private static final AsciiSet QUOTE_KEY_CHARS = AsciiSet.of(" 
\t\n\r{},:[]=\"'#/");
        // HOCON parser-meaningful chars that must be quoted in a value to 
avoid mis-tokenization:
@@ -327,28 +327,4 @@ public class HoconWriter extends SerializerWriter {
                w("\"\"\"");
                return this;
        }
-
-       // Override return types for chaining
-       @Override public HoconWriter append(char c) { super.append(c); return 
this; }
-       @Override public HoconWriter append(char[] value) { 
super.append(value); return this; }
-       @Override public HoconWriter append(int indent, char c) { 
super.append(indent, c); return this; }
-       @Override public HoconWriter append(int indent, String text) { 
super.append(indent, text); return this; }
-       @Override public HoconWriter append(Object text) { super.append(text); 
return this; }
-       @Override public HoconWriter append(String text) { super.append(text); 
return this; }
-       @Override public HoconWriter appendIf(boolean b, char c) { 
super.appendIf(b, c); return this; }
-       @Override public HoconWriter appendIf(boolean b, String text) { 
super.appendIf(b, text); return this; }
-       @Override public HoconWriter appendln(int indent, String text) { 
super.appendln(indent, text); return this; }
-       @Override public HoconWriter appendln(String text) { 
super.appendln(text); return this; }
-       @Override public HoconWriter appendUri(Object value) { 
super.appendUri(value); return this; }
-       @Override public HoconWriter cr(int depth) { super.cr(depth); return 
this; }
-       @Override public HoconWriter cre(int depth) { super.cre(depth); return 
this; }
-       @Override public HoconWriter i(int indent) { super.i(indent); return 
this; }
-       @Override public HoconWriter ie(int indent) { super.ie(indent); return 
this; }
-       @Override public HoconWriter nl(int indent) { super.nl(indent); return 
this; }
-       @Override public HoconWriter nlIf(boolean flag, int indent) { 
super.nlIf(flag, indent); return this; }
-       @Override public HoconWriter q() { super.q(); return this; }
-       @Override public HoconWriter s() { super.s(); return this; }
-       @Override public HoconWriter sIf(boolean flag) { super.sIf(flag); 
return this; }
-       @Override public HoconWriter w(char value) { super.w(value); return 
this; }
-       @Override public HoconWriter w(String value) { super.w(value); return 
this; }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/BasicHtmlDocTemplate.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/BasicHtmlDocTemplate.java
index af9fe01370..b71f6f154b 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/BasicHtmlDocTemplate.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/BasicHtmlDocTemplate.java
@@ -63,7 +63,7 @@ public class BasicHtmlDocTemplate implements HtmlDocTemplate {
        private static final List<String> MARSHALL_URI_NAVLINK_SCHEMES = 
List.of("request", "servlet", "context");
 
        @Override /* Overridden from HtmlDocTemplate */
-       public void writeTo(HtmlDocSerializerSession session, HtmlWriter w, 
Object o) throws Exception {
+       public void writeTo(HtmlDocSerializerSession session, HtmlWriter<?> w, 
Object o) throws Exception {
                w.sTag("html").nl(0);
                w.sTag(1, "head").nl(1);
                head(session, w, o);
@@ -82,7 +82,7 @@ public class BasicHtmlDocTemplate implements HtmlDocTemplate {
         * @param o The object being serialized.
         * @throws Exception Any exception can be thrown.
         */
-       protected void article(HtmlDocSerializerSession session, HtmlWriter w, 
Object o) throws Exception {
+       protected void article(HtmlDocSerializerSession session, HtmlWriter<?> 
w, Object o) throws Exception {
                // To allow for page formatting using CSS, we encapsulate the 
data inside two div tags:
                // <div class='outerdata'><div class='data' 
id='data'>...</div></div>
                w.oTag(4, "div").attr("class", "outerdata").append('>').nl(4);
@@ -98,9 +98,9 @@ public class BasicHtmlDocTemplate implements HtmlDocTemplate {
                        session.indent = 6;
                        w.flush();
                        if (session.isResolveBodyVars()) {
-                               w = new HtmlWriter(w) {
+                               w = new BasicHtmlWriter(w) {
                                        @Override
-                                       public HtmlWriter text(Object value, 
boolean preserveWhitespace) {
+                                       public BasicHtmlWriter text(Object 
value, boolean preserveWhitespace) {
                                                return 
super.text(session.resolve(Shorts.s(value)), preserveWhitespace);
                                        }
                                };
@@ -120,7 +120,7 @@ public class BasicHtmlDocTemplate implements 
HtmlDocTemplate {
         * @param o The object being serialized.
         * @throws Exception Any exception can be thrown.
         */
-       protected void aside(HtmlDocSerializerSession session, HtmlWriter w, 
Object o) throws Exception {
+       protected void aside(HtmlDocSerializerSession session, HtmlWriter<?> w, 
Object o) throws Exception {
                var aside = session.getAside();
                for (var i = 0; i < aside.length; i++)
                        w.sIf(i > 0).appendln(4, session.resolve(aside[i]));
@@ -134,7 +134,7 @@ public class BasicHtmlDocTemplate implements 
HtmlDocTemplate {
         * @param o The object being serialized.
         * @throws Exception Any exception can be thrown.
         */
-       protected void body(HtmlDocSerializerSession session, HtmlWriter w, 
Object o) throws Exception {
+       protected void body(HtmlDocSerializerSession session, HtmlWriter<?> w, 
Object o) throws Exception {
 
                AsideFloat asideFloat = session.getAsideFloat();
                boolean hasAside = hasAside(session);
@@ -202,7 +202,7 @@ public class BasicHtmlDocTemplate implements 
HtmlDocTemplate {
         * @param o The object being serialized.
         * @throws Exception Any exception can be thrown.
         */
-       protected void footer(HtmlDocSerializerSession session, HtmlWriter w, 
Object o) throws Exception {
+       protected void footer(HtmlDocSerializerSession session, HtmlWriter<?> 
w, Object o) throws Exception {
                var footer = session.getFooter();
                for (var i = 0; i < footer.length; i++)
                        w.sIf(i > 0).appendln(3, session.resolve(footer[i]));
@@ -280,7 +280,7 @@ public class BasicHtmlDocTemplate implements 
HtmlDocTemplate {
         * @param o The object being serialized.
         * @throws Exception Any exception can be thrown.
         */
-       protected void head(HtmlDocSerializerSession session, HtmlWriter w, 
Object o) throws Exception {
+       protected void head(HtmlDocSerializerSession session, HtmlWriter<?> w, 
Object o) throws Exception {
 
                var head = session.getHead();
                for (var i = 0; i < head.length; i++)
@@ -313,7 +313,7 @@ public class BasicHtmlDocTemplate implements 
HtmlDocTemplate {
         * @param o The object being serialized.
         * @throws Exception Any exception can be thrown.
         */
-       protected void header(HtmlDocSerializerSession session, HtmlWriter w, 
Object o) throws Exception {
+       protected void header(HtmlDocSerializerSession session, HtmlWriter<?> 
w, Object o) throws Exception {
                // Write the title of the page.
                var header = session.getHeader();
                for (var i = 0; i < header.length; i++)
@@ -331,7 +331,7 @@ public class BasicHtmlDocTemplate implements 
HtmlDocTemplate {
        @SuppressWarnings({
                "java:S3776" // Cognitive complexity acceptable for navigation 
link rendering with conditional logic
        })
-       protected void nav(HtmlDocSerializerSession session, HtmlWriter w, 
Object o) throws Exception {
+       protected void nav(HtmlDocSerializerSession session, HtmlWriter<?> w, 
Object o) throws Exception {
                String[] links = session.getNavLinks();
                if (links.length > 0 && ! contains("NONE", links)) {
                        w.sTag(3, "ol").nl(3);
@@ -381,7 +381,7 @@ public class BasicHtmlDocTemplate implements 
HtmlDocTemplate {
         * @param o The object being serialized.
         * @throws Exception Any exception can be thrown.
         */
-       protected void script(HtmlDocSerializerSession session, HtmlWriter w, 
Object o) throws Exception {
+       protected void script(HtmlDocSerializerSession session, HtmlWriter<?> 
w, Object o) throws Exception {
                var addSpace = Flag.create();
                for (var s : session.getScript())
                        w.sIf(addSpace.getAndSet()).append(3, 
session.resolve(s)).append('\n'); // Must always append a newline even if 
whitespace disabled!
@@ -396,7 +396,7 @@ public class BasicHtmlDocTemplate implements 
HtmlDocTemplate {
         * @param o The object being serialized.
         * @throws Exception Any exception can be thrown.
         */
-       protected void style(HtmlDocSerializerSession session, HtmlWriter w, 
Object o) throws Exception {
+       protected void style(HtmlDocSerializerSession session, HtmlWriter<?> w, 
Object o) throws Exception {
                var addSpace = Flag.create();
                for (var s : session.getStylesheet())
                        w.sIf(addSpace.getAndSet()).append(3, "@import 
").q().append(session.resolveUri(session.resolve(s))).q().appendln(";");
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/BasicHtmlWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/BasicHtmlWriter.java
new file mode 100644
index 0000000000..fc56644b28
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/BasicHtmlWriter.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.marshall.html;
+
+import java.io.*;
+
+import org.apache.juneau.marshall.*;
+
+/**
+ * Concrete {@link HtmlWriter} leaf for standard HTML output.
+ *
+ * <p>
+ * This is the directly-instantiable form of {@link HtmlWriter}, which is 
itself an abstract CRTP self-type base so it
+ * can also be extended (e.g. by {@link SimpleHtmlWriter} or by the 
body-var-resolving anonymous writer in
+ * {@link BasicHtmlDocTemplate}).
+ *
+ * <h5 class='section'>Notes:</h5><ul>
+ *     <li class='note'>
+ *             This class is not intended for external use.
+ * </ul>
+ */
+@SuppressWarnings({
+       "resource" // Writer resource managed by calling code
+})
+public class BasicHtmlWriter extends HtmlWriter<BasicHtmlWriter> {
+
+       /**
+        * Copy constructor.
+        *
+        * @param w Writer being copied.
+        */
+       public BasicHtmlWriter(HtmlWriter<?> w) {
+               super(w);
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param out The writer being wrapped.
+        * @param useWhitespace If <jk>true</jk>, tabs will be used in output.
+        * @param maxIndent The maximum indentation level.
+        * @param trimStrings If <jk>true</jk>, strings should be trimmed 
before they're serialized.
+        * @param quoteChar The quote character to use (i.e. <js>'\''</js> or 
<js>'"'</js>)
+        * @param uriResolver The URI resolver for resolving URIs to absolute 
or root-relative form.
+        */
+       public BasicHtmlWriter(Writer out, boolean useWhitespace, int 
maxIndent, boolean trimStrings, char quoteChar, UriResolver uriResolver) {
+               super(out, useWhitespace, maxIndent, trimStrings, quoteChar, 
uriResolver);
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlDocTemplate.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlDocTemplate.java
index 8f515ff200..ecea302448 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlDocTemplate.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlDocTemplate.java
@@ -79,5 +79,5 @@ public interface HtmlDocTemplate {
         * @param o The object being serialized.
         * @throws Exception Any exception can be thrown.
         */
-       void writeTo(HtmlDocSerializerSession session, HtmlWriter w, Object o) 
throws Exception;
+       void writeTo(HtmlDocSerializerSession session, HtmlWriter<?> w, Object 
o) throws Exception;
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlSerializerSession.java
index 5ab5a56995..ef22a02c89 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlSerializerSession.java
@@ -330,7 +330,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
         * @return The same writer passed in.
         * @throws SerializeException If a problem occurred trying to send 
output to the writer.
         */
-       private XmlWriter doSerialize(Object o, XmlWriter w) throws 
SerializeException {
+       private XmlWriter<?> doSerialize(Object o, XmlWriter<?> w) throws 
SerializeException {
                writeAnything(w, o, getExpectedRootType(o), null, null, 
getInitialDepth() - 1, true, false);
                return w;
        }
@@ -443,7 +443,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
        @SuppressWarnings({
                "java:S3776" // Cognitive complexity acceptable for bean map 
serialization
        })
-       private void writeBeanMap(XmlWriter out, BeanMap<?> m, ClassMeta<?> 
eType, BeanPropertyMeta ppMeta) throws SerializeException {
+       private void writeBeanMap(XmlWriter<?> out, BeanMap<?> m, ClassMeta<?> 
eType, BeanPropertyMeta ppMeta) throws SerializeException {
 
                var mcm = (ClassMeta<?>) m.getBeanInfo();
                HtmlClassMeta cHtml = getHtmlClassMeta(mcm);
@@ -519,7 +519,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                "java:S3776", // Cognitive complexity acceptable for this 
specific logic
                "java:S6541", // Single-threaded session contexts do not 
require synchronization
        })
-       private void writeCollection(XmlWriter out, Object in, ClassMeta<?> 
sType, ClassMeta<?> eType, String name, BeanPropertyMeta ppMeta) throws 
SerializeException {
+       private void writeCollection(XmlWriter<?> out, Object in, ClassMeta<?> 
sType, ClassMeta<?> eType, String name, BeanPropertyMeta ppMeta) throws 
SerializeException {
 
                HtmlClassMeta cHtml = getHtmlClassMeta(sType);
                HtmlBeanPropertyMeta bpHtml = getHtmlBeanPropertyMeta(ppMeta);
@@ -677,7 +677,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
        }
 
        
-       private void writeMap(XmlWriter out, Map m, ClassMeta<?> sType, 
ClassMeta<?> eKeyType, ClassMeta<?> eValueType, String typeName, 
BeanPropertyMeta ppMeta) throws SerializeException {
+       private void writeMap(XmlWriter<?> out, Map m, ClassMeta<?> sType, 
ClassMeta<?> eKeyType, ClassMeta<?> eValueType, String typeName, 
BeanPropertyMeta ppMeta) throws SerializeException {
 
                var keyType = eKeyType == null ? string() : eKeyType;
                var valueType = eValueType == null ? object() : eValueType;
@@ -705,7 +705,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                out.ie(i).eTag(TAG_table).nl(i);
        }
 
-       private void writeMapEntry(XmlWriter out, Map.Entry e, ClassMeta<?> 
keyType, ClassMeta<?> valueType, int i, BeanPropertyMeta ppMeta) throws 
SerializeException {
+       private void writeMapEntry(XmlWriter<?> out, Map.Entry e, ClassMeta<?> 
keyType, ClassMeta<?> valueType, int i, BeanPropertyMeta ppMeta) throws 
SerializeException {
                Object key = generalize(e.getKey(), keyType);
                Object value = null;
                try {
@@ -773,11 +773,11 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
         *      <br>Must not be <jk>null</jk>.
         * @return The output target object wrapped in an {@link HtmlWriter}.
         */
-       protected final HtmlWriter getHtmlWriter(SerializerPipe out) {
+       protected final HtmlWriter<?> getHtmlWriter(SerializerPipe out) {
                Object output = out.getRawOutput();
-               if (output instanceof HtmlWriter output2)
+               if (output instanceof HtmlWriter<?> output2)
                        return output2;
-               var w = new HtmlWriter(out.getWriter(), isUseWhitespace(), 
getMaxIndent(), isTrimStrings(), getQuoteChar(), getUriResolver());
+               var w = new BasicHtmlWriter(out.getWriter(), isUseWhitespace(), 
getMaxIndent(), isTrimStrings(), getQuoteChar(), getUriResolver());
                out.setWriter(w);
                return w;
        }
@@ -853,7 +853,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                "java:S3776", // Cognitive complexity acceptable for this 
specific logic
                "java:S107", // Method has many parameters; acceptable for 
builder/configuration methods
        })
-       protected ContentResult writeAnything(XmlWriter out, Object o, 
ClassMeta<?> eType, String name, BeanPropertyMeta pMeta, int xIndent, boolean 
isRoot, boolean nlIfElement)
+       protected ContentResult writeAnything(XmlWriter<?> out, Object o, 
ClassMeta<?> eType, String name, BeanPropertyMeta pMeta, int xIndent, boolean 
isRoot, boolean nlIfElement)
                throws SerializeException {
 
                ClassMeta<?> aType = null;       // The actual type
@@ -1068,7 +1068,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
        }
 
        @Override /* Overridden from XmlSerializerSession */
-       protected ContentResult writeAnything(XmlWriter out, Object o, 
ClassMeta<?> eType, String keyName, String elementName, Namespace 
elementNamespace, boolean addNamespaceUris, XmlFormat format,
+       protected ContentResult writeAnything(XmlWriter<?> out, Object o, 
ClassMeta<?> eType, String keyName, String elementName, Namespace 
elementNamespace, boolean addNamespaceUris, XmlFormat format,
                boolean isMixed, boolean preserveWhitespace, BeanPropertyMeta 
pMeta) throws SerializeException {
 
                // If this is a bean, then we want to serialize it as HTML 
unless it's @Html(format=XML).
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlWriter.java
index 2ec414ad01..096bf65e3a 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/HtmlWriter.java
@@ -32,16 +32,17 @@ import org.apache.juneau.marshall.xml.*;
  * </ul>
  */
 @SuppressWarnings({
-       "resource" // Writer resource managed by calling code
+       "resource", // Writer resource managed by calling code
+       "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than 
a single-letter name.
 })
-public class HtmlWriter extends XmlWriter {
+public abstract class HtmlWriter<SELF extends HtmlWriter<SELF>> extends 
XmlWriter<SELF> {
 
        /**
         * Copy constructor.
         *
         * @param w Writer being copied.
         */
-       public HtmlWriter(HtmlWriter w) {
+       public HtmlWriter(HtmlWriter<?> w) {
                super(w);
        }
 
@@ -60,339 +61,33 @@ public class HtmlWriter extends XmlWriter {
        }
 
        @Override /* Overridden from SerializerWriter */
-       public HtmlWriter append(char c) {
-               super.append(c);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter append(char[] value) {
-               super.append(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public HtmlWriter append(int indent, char c) {
-               super.append(indent, c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public HtmlWriter append(int indent, String text) {
-               super.append(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public HtmlWriter append(Object text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public HtmlWriter append(String text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter appendIf(boolean flag, char value) {
-               super.appendIf(flag, value);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter appendIf(boolean flag, String value) {
-               super.appendIf(flag, value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public HtmlWriter appendln(int indent, String text) {
-               super.appendln(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public HtmlWriter appendln(String text) {
-               super.appendln(text);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter appendUri(Object value) {
-               super.appendUri(value);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter attr(String name, Object value) {
-               super.attr(name, value);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter attr(String name, Object value, boolean 
valNeedsEncoding) {
-               super.attr(null, name, value, valNeedsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter attr(String ns, String name, Object value) {
-               super.attr(ns, name, value);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter attr(String ns, String name, Object value, boolean 
valNeedsEncoding) {
-               super.attr(ns, name, value, valNeedsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter attrUri(String name, Object value) {
-               super.attrUri(name, value);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter ceTag() {
-               super.ceTag();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public HtmlWriter cr(int depth) {
+       public SELF cr(int depth) {
                if (depth > 0)
                        super.cr(depth);
-               return this;
+               return self();
        }
 
        @Override /* Overridden from SerializerWriter */
-       public HtmlWriter cre(int depth) {
+       public SELF cre(int depth) {
                if (depth > 0)
                        super.cre(depth);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter cTag() {
-               super.cTag();
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter eTag(int indent, String name) {
-               super.eTag(indent, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter eTag(int indent, String ns, String name) {
-               super.eTag(indent, ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter eTag(int indent, String ns, String name, boolean 
needsEncoding) {
-               super.eTag(indent, ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter eTag(String name) {
-               super.eTag(name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter eTag(String ns, String name) {
-               super.eTag(ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter eTag(String ns, String name, boolean needsEncoding) {
-               super.eTag(ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public HtmlWriter i(int indent) {
-               super.i(indent);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter ie(int indent) {
-               super.ie(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public HtmlWriter nl(int indent) {
-               super.nl(indent);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter nlIf(boolean flag, int indent) {
-               super.nlIf(flag, indent);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter oAttr(String ns, String name) {
-               super.oAttr(ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter oTag(int indent, String name) {
-               super.oTag(indent, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter oTag(int indent, String ns, String name) {
-               super.oTag(indent, ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter oTag(int indent, String ns, String name, boolean 
needsEncoding) {
-               super.oTag(indent, ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter oTag(String name) {
-               super.oTag(name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter oTag(String ns, String name) {
-               super.oTag(ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter oTag(String ns, String name, boolean needsEncoding) {
-               super.oTag(ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public HtmlWriter q() {
-               super.q();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public HtmlWriter s() {
-               super.s();
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter sIf(boolean flag) {
-               super.sIf(flag);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter sTag(int indent, String name) {
-               super.sTag(indent, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter sTag(int indent, String ns, String name) {
-               super.sTag(indent, ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter sTag(int indent, String ns, String name, boolean 
needsEncoding) {
-               super.sTag(indent, ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter sTag(String name) {
-               super.sTag(name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter sTag(String ns, String name) {
-               super.sTag(ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter sTag(String ns, String name, boolean needsEncoding) {
-               super.sTag(ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter tag(int indent, String name) {
-               super.tag(indent, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter tag(int indent, String ns, String name) {
-               super.tag(indent, ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter tag(int indent, String ns, String name, boolean 
needsEncoding) {
-               super.tag(indent, ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter tag(String name) {
-               super.tag(name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter tag(String ns, String name) {
-               super.tag(ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from XmlSerializerWriter */
-       public HtmlWriter tag(String ns, String name, boolean needsEncoding) {
-               super.tag(ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter text(Object value) {
-               super.text(value);
-               return this;
+               return self();
        }
 
        @Override /* Overridden from XmlSerializerWriter */
        @SuppressWarnings({
                "java:S3776" // Cognitive complexity acceptable for text 
serialization with whitespace handling
        })
-       public HtmlWriter text(Object o, boolean preserveWhitespace) {
+       public SELF text(Object o, boolean preserveWhitespace) {
 
                if (o == null) {
                        append("<null/>");
-                       return this;
+                       return self();
                }
                var s = o.toString();
                if (s.isEmpty()) {
                        append("<sp/>");
-                       return this;
+                       return self();
                }
 
                for (var i = 0; i < s.length(); i++) {
@@ -424,24 +119,6 @@ public class HtmlWriter extends XmlWriter {
                                w(test);
                }
 
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter textUri(Object value) {
-               super.textUri(value);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter w(char c) {
-               super.w(c);
-               return this;
-       }
-
-       @Override /* Overridden from XmlWriter */
-       public HtmlWriter w(String s) {
-               super.w(s);
-               return this;
+               return self();
        }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/SimpleHtmlWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/SimpleHtmlWriter.java
index 06affbf4e7..0cac8f222b 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/SimpleHtmlWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/html/SimpleHtmlWriter.java
@@ -35,7 +35,7 @@ import java.io.*;
 @SuppressWarnings({
        "resource" // Writer resource managed by calling code
 })
-public class SimpleHtmlWriter extends HtmlWriter {
+public class SimpleHtmlWriter extends HtmlWriter<SimpleHtmlWriter> {
 
        /**
         * Constructor.
@@ -44,350 +44,8 @@ public class SimpleHtmlWriter extends HtmlWriter {
                super(new StringWriter(), true, 100, false, '\'', null);
        }
 
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter append(char c) {
-               super.append(c);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter append(char[] value) {
-               super.append(value);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter append(int indent, char c) {
-               super.append(indent, c);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter append(int indent, String text) {
-               super.append(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter append(Object text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter append(String text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter appendIf(boolean flag, char value) {
-               super.appendIf(flag, value);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter appendIf(boolean flag, String value) {
-               super.appendIf(flag, value);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter appendln(int indent, String text) {
-               super.appendln(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter appendln(String text) {
-               super.appendln(text);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter appendUri(Object value) {
-               super.appendUri(value);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter attr(String name, Object value) {
-               super.attr(name, value);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter attr(String name, Object value, boolean 
valNeedsEncoding) {
-               super.attr(name, value, valNeedsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter attr(String ns, String name, Object value) {
-               super.attr(ns, name, value);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter attr(String ns, String name, Object value, 
boolean valNeedsEncoding) {
-               super.attr(ns, name, value, valNeedsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter attrUri(String name, Object value) {
-               super.attrUri(name, value);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter ceTag() {
-               super.ceTag();
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter cr(int depth) {
-               super.cr(depth);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter cre(int depth) {
-               super.cre(depth);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter cTag() {
-               super.cTag();
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter eTag(int indent, String name) {
-               super.eTag(indent, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter eTag(int indent, String ns, String name) {
-               super.eTag(indent, ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter eTag(int indent, String ns, String name, 
boolean needsEncoding) {
-               super.eTag(indent, ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter eTag(String name) {
-               super.eTag(name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter eTag(String ns, String name) {
-               super.eTag(ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter eTag(String ns, String name, boolean 
needsEncoding) {
-               super.eTag(ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter i(int indent) {
-               super.i(indent);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter ie(int indent) {
-               super.ie(indent);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter nl(int indent) {
-               super.nl(indent);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter nlIf(boolean flag, int indent) {
-               super.nlIf(flag, indent);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter oAttr(String ns, String name) {
-               super.oAttr(ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter oTag(int indent, String name) {
-               super.oTag(indent, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter oTag(int indent, String ns, String name) {
-               super.oTag(indent, ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter oTag(int indent, String ns, String name, 
boolean needsEncoding) {
-               super.oTag(indent, ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter oTag(String name) {
-               super.oTag(name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter oTag(String ns, String name) {
-               super.oTag(ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter oTag(String ns, String name, boolean 
needsEncoding) {
-               super.oTag(ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter q() {
-               super.q();
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter s() {
-               super.s();
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter sIf(boolean flag) {
-               super.sIf(flag);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter sTag(int indent, String name) {
-               super.sTag(indent, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter sTag(int indent, String ns, String name) {
-               super.sTag(indent, ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter sTag(int indent, String ns, String name, 
boolean needsEncoding) {
-               super.sTag(indent, ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter sTag(String name) {
-               super.sTag(name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter sTag(String ns, String name) {
-               super.sTag(ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter sTag(String ns, String name, boolean 
needsEncoding) {
-               super.sTag(ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter tag(int indent, String name) {
-               super.tag(indent, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter tag(int indent, String ns, String name) {
-               super.tag(indent, ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter tag(int indent, String ns, String name, boolean 
needsEncoding) {
-               super.tag(indent, ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter tag(String name) {
-               super.tag(name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter tag(String ns, String name) {
-               super.tag(ns, name);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter tag(String ns, String name, boolean 
needsEncoding) {
-               super.tag(ns, name, needsEncoding);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter text(Object value) {
-               super.text(value);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter text(Object o, boolean preserveWhitespace) {
-               super.text(o, preserveWhitespace);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter textUri(Object value) {
-               super.textUri(value);
-               return this;
-       }
-
        @Override /* Overridden from Object */
        public String toString() {
                return out.toString();
        }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter w(char c) {
-               super.w(c);
-               return this;
-       }
-
-       @Override /* Overridden from HtmlWriter */
-       public SimpleHtmlWriter w(String s) {
-               super.w(s);
-               return this;
-       }
-}
\ No newline at end of file
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/ini/IniWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/ini/IniWriter.java
index fbd446962f..0ab4947cc8 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/ini/IniWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/ini/IniWriter.java
@@ -41,7 +41,7 @@ import org.apache.juneau.marshall.serializer.*;
 @SuppressWarnings({
        "resource" // Writer resource managed by calling code
 })
-public class IniWriter extends SerializerWriter {
+public class IniWriter extends SerializerWriter<IniWriter> {
 
        /** Key-value separator character. */
        protected final char kvSeparator;
@@ -148,28 +148,4 @@ public class IniWriter extends SerializerWriter {
                w('\n');
                return this;
        }
-
-       // Override return types for chaining
-       @Override public IniWriter append(char c) { super.append(c); return 
this; }
-       @Override public IniWriter append(char[] value) { super.append(value); 
return this; }
-       @Override public IniWriter append(int indent, char c) { 
super.append(indent, c); return this; }
-       @Override public IniWriter append(int indent, String text) { 
super.append(indent, text); return this; }
-       @Override public IniWriter append(Object text) { super.append(text); 
return this; }
-       @Override public IniWriter append(String text) { super.append(text); 
return this; }
-       @Override public IniWriter appendIf(boolean b, char c) { 
super.appendIf(b, c); return this; }
-       @Override public IniWriter appendIf(boolean b, String text) { 
super.appendIf(b, text); return this; }
-       @Override public IniWriter appendln(int indent, String text) { 
super.appendln(indent, text); return this; }
-       @Override public IniWriter appendln(String text) { 
super.appendln(text); return this; }
-       @Override public IniWriter appendUri(Object value) { 
super.appendUri(value); return this; }
-       @Override public IniWriter cr(int depth) { super.cr(depth); return 
this; }
-       @Override public IniWriter cre(int depth) { super.cre(depth); return 
this; }
-       @Override public IniWriter i(int indent) { super.i(indent); return 
this; }
-       @Override public IniWriter ie(int indent) { super.ie(indent); return 
this; }
-       @Override public IniWriter nl(int indent) { super.nl(indent); return 
this; }
-       @Override public IniWriter nlIf(boolean flag, int indent) { 
super.nlIf(flag, indent); return this; }
-       @Override public IniWriter q() { super.q(); return this; }
-       @Override public IniWriter s() { super.s(); return this; }
-       @Override public IniWriter sIf(boolean flag) { super.sIf(flag); return 
this; }
-       @Override public IniWriter w(char value) { super.w(value); return this; 
}
-       @Override public IniWriter w(String value) { super.w(value); return 
this; }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jcs/JcsSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jcs/JcsSerializerSession.java
index 6d449e75ff..af80b1f94c 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jcs/JcsSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jcs/JcsSerializerSession.java
@@ -272,7 +272,7 @@ public class JcsSerializerSession extends 
JsonSerializerSession {
        public boolean isRecordStreaming() { return false; }
 
        @Override /* Overridden from JsonSerializerSession */
-       protected JsonWriter getJsonWriter(SerializerPipe out) {
+       protected JsonWriter<?> getJsonWriter(SerializerPipe out) {
                var output = out.getRawOutput();
                if (output instanceof JcsWriter w)
                        return w;
@@ -286,7 +286,7 @@ public class JcsSerializerSession extends 
JsonSerializerSession {
                "unchecked", // Type erasure requires unchecked casts in 
collection/map serialization
        })
        @Override /* Overridden from JsonSerializerSession - package 
visibility, override by making accessible */
-       protected SerializerWriter writeMap(JsonWriter out, Map m, ClassMeta<?> 
type) throws SerializeException {
+       protected SerializerWriter<?> writeMap(JsonWriter<?> out, Map m, 
ClassMeta<?> type) throws SerializeException {
                var keyType = type.getKeyType();
                var valueType = type.getValueType();
 
@@ -331,7 +331,7 @@ public class JcsSerializerSession extends 
JsonSerializerSession {
        }
 
        @Override /* Overridden from JsonSerializerSession */
-       protected SerializerWriter writeBeanMap(JsonWriter out, BeanMap<?> m, 
String typeName) throws SerializeException {
+       protected SerializerWriter<?> writeBeanMap(JsonWriter<?> out, 
BeanMap<?> m, String typeName) throws SerializeException {
                var entries = new ArrayList<BeanProp>();
                Predicate<Object> checkNull = x -> isKeepNullProperties() || 
nn(x);
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jcs/JcsWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jcs/JcsWriter.java
index 5d78c6dcf4..0bf16d6007 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jcs/JcsWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/jcs/JcsWriter.java
@@ -37,7 +37,7 @@ import org.apache.juneau.marshall.json.*;
 @SuppressWarnings({
        "resource" // Writer resource managed by calling code
 })
-public class JcsWriter extends JsonWriter {
+public class JcsWriter extends JsonWriter<JcsWriter> {
 
        /**
         * Constructor.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json/BasicJsonWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json/BasicJsonWriter.java
new file mode 100644
index 0000000000..f205306e28
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json/BasicJsonWriter.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.marshall.json;
+
+import java.io.*;
+
+import org.apache.juneau.marshall.*;
+
+/**
+ * Concrete {@link JsonWriter} leaf for standard JSON output.
+ *
+ * <p>
+ * This is the directly-instantiable form of {@link JsonWriter}, which is 
itself an abstract CRTP self-type base so it
+ * can also be extended (e.g. by {@link 
org.apache.juneau.marshall.jcs.JcsWriter}).
+ *
+ * <h5 class='section'>Notes:</h5><ul>
+ *     <li class='note'>
+ *             This class is not intended for external use.
+ * </ul>
+ */
+public final class BasicJsonWriter extends JsonWriter<BasicJsonWriter> {
+
+       /**
+        * Constructor.
+        *
+        * @param out The writer being wrapped.
+        * @param useWhitespace If <jk>true</jk>, tabs and spaces will be used 
in output.
+        * @param maxIndent The maximum indentation level.
+        * @param escapeSolidus If <jk>true</jk>, forward slashes should be 
escaped in the output.
+        * @param quoteChar The quote character to use (i.e. <js>'\''</js> or 
<js>'"'</js>)
+        * @param simpleAttrs If <jk>true</jk>, JSON attributes will only be 
quoted when necessary.
+        * @param trimStrings If <jk>true</jk>, strings will be trimmed before 
being serialized.
+        * @param uriResolver The URI resolver for resolving URIs to absolute 
or root-relative form.
+        */
+       @SuppressWarnings({
+               "java:S107" // Constructor requires 8 parameters for JSON 
writer configuration
+       })
+       public BasicJsonWriter(Writer out, boolean useWhitespace, int 
maxIndent, boolean escapeSolidus, char quoteChar, boolean simpleAttrs, boolean 
trimStrings, UriResolver uriResolver) {
+               super(out, useWhitespace, maxIndent, escapeSolidus, quoteChar, 
simpleAttrs, trimStrings, uriResolver);
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json/JsonSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json/JsonSerializerSession.java
index 950201db51..916ab67432 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json/JsonSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json/JsonSerializerSession.java
@@ -150,7 +150,7 @@ public class JsonSerializerSession extends 
WriterSerializerSession implements To
                escapeSolidus = builder.escapeSolidus;
        }
 
-       protected SerializerWriter writeBeanMap(JsonWriter out, BeanMap<?> m, 
String typeName) throws SerializeException {
+       protected SerializerWriter<?> writeBeanMap(JsonWriter<?> out, 
BeanMap<?> m, String typeName) throws SerializeException {
                int i = indent;
                out.w('{');
 
@@ -183,7 +183,7 @@ public class JsonSerializerSession extends 
WriterSerializerSession implements To
        }
 
        
-       private SerializerWriter writeCollection(JsonWriter out, Collection c, 
ClassMeta<?> type) throws SerializeException {
+       private SerializerWriter<?> writeCollection(JsonWriter<?> out, 
Collection c, ClassMeta<?> type) throws SerializeException {
 
                var elementType = type.getElementType();
 
@@ -199,7 +199,7 @@ public class JsonSerializerSession extends 
WriterSerializerSession implements To
                return out;
        }
 
-       private SerializerWriter writeStreamable(JsonWriter out, Object o, 
ClassMeta<?> sType, ClassMeta<?> type) throws SerializeException {
+       private SerializerWriter<?> writeStreamable(JsonWriter<?> out, Object 
o, ClassMeta<?> sType, ClassMeta<?> type) throws SerializeException {
                var elementType = type.getElementType();
 
                out.w('[');
@@ -215,7 +215,7 @@ public class JsonSerializerSession extends 
WriterSerializerSession implements To
        }
 
        
-       protected SerializerWriter writeMap(JsonWriter out, Map m, ClassMeta<?> 
type) throws SerializeException {
+       protected SerializerWriter<?> writeMap(JsonWriter<?> out, Map m, 
ClassMeta<?> type) throws SerializeException {
 
                var keyType = type.getKeyType();
                var valueType = type.getValueType();
@@ -259,11 +259,11 @@ public class JsonSerializerSession extends 
WriterSerializerSession implements To
         *      <br>Must not be <jk>null</jk>.
         * @return The output target object wrapped in an {@link JsonWriter}.
         */
-       protected JsonWriter getJsonWriter(SerializerPipe out) {
+       protected JsonWriter<?> getJsonWriter(SerializerPipe out) {
                var output = out.getRawOutput();
-               if (output instanceof JsonWriter output2)
+               if (output instanceof JsonWriter<?> output2)
                        return output2;
-               var w = new JsonWriter(out.getWriter(), isUseWhitespace(), 
getMaxIndent(), isEscapeSolidus(), getQuoteChar(), false, isTrimStrings(), 
getUriResolver());
+               var w = new BasicJsonWriter(out.getWriter(), isUseWhitespace(), 
getMaxIndent(), isEscapeSolidus(), getQuoteChar(), false, isTrimStrings(), 
getUriResolver());
                out.setWriter(w);
                return w;
        }
@@ -357,7 +357,7 @@ public class JsonSerializerSession extends 
WriterSerializerSession implements To
        @SuppressWarnings({
                "java:S3776", // Cognitive complexity acceptable for this 
specific logic
        })
-       protected JsonWriter writeAnything(JsonWriter out, Object o, 
ClassMeta<?> eType, String attrName, BeanPropertyMeta pMeta) throws 
SerializeException {
+       protected JsonWriter<?> writeAnything(JsonWriter<?> out, Object o, 
ClassMeta<?> eType, String attrName, BeanPropertyMeta pMeta) throws 
SerializeException {
 
                if (o == null) {
                        out.append("null");
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json/JsonWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json/JsonWriter.java
index 21e8f3fb3c..4e413ec076 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json/JsonWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json/JsonWriter.java
@@ -39,9 +39,10 @@ import org.apache.juneau.marshall.serializer.*;
  * </ul>
  */
 @SuppressWarnings({
-       "resource" // Writer resource managed by calling code
+       "resource", // Writer resource managed by calling code
+       "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than 
a single-letter name.
 })
-public class JsonWriter extends SerializerWriter {
+public abstract class JsonWriter<SELF extends JsonWriter<SELF>> extends 
SerializerWriter<SELF> {
 
        // Characters that trigger special handling of serializing attribute 
values.
        // @formatter:off
@@ -109,74 +110,8 @@ public class JsonWriter extends SerializerWriter {
        @SuppressWarnings({
                "java:S107" // Factory method mirrors the constructor; all 8 
parameters are distinct JSON writer configuration settings
        })
-       public static JsonWriter create(Writer out, boolean useWhitespace, int 
maxIndent, boolean escapeSolidus, char quoteChar, boolean simpleAttrs, boolean 
trimStrings, UriResolver uriResolver) {
-               return new JsonWriter(out, useWhitespace, maxIndent, 
escapeSolidus, quoteChar, simpleAttrs, trimStrings, uriResolver);
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter append(char c) {
-               super.append(c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter append(char[] value) {
-               super.append(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter append(int indent, char c) {
-               super.append(indent, c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter append(int indent, String text) {
-               super.append(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter append(Object text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter append(String text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter appendIf(boolean b, char c) {
-               super.appendIf(b, c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter appendIf(boolean b, String text) {
-               super.appendIf(b, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter appendln(int indent, String text) {
-               super.appendln(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter appendln(String text) {
-               super.appendln(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter appendUri(Object value) {
-               super.appendUri(value);
-               return this;
+       public static JsonWriter<?> create(Writer out, boolean useWhitespace, 
int maxIndent, boolean escapeSolidus, char quoteChar, boolean simpleAttrs, 
boolean trimStrings, UriResolver uriResolver) {
+               return new BasicJsonWriter(out, useWhitespace, maxIndent, 
escapeSolidus, quoteChar, simpleAttrs, trimStrings, uriResolver);
        }
 
        /**
@@ -189,7 +124,7 @@ public class JsonWriter extends SerializerWriter {
        @SuppressWarnings({
                "java:S3776" // Cognitive complexity acceptable for JSON 
attribute writing with encoding
        })
-       public JsonWriter attr(String s) {
+       public SELF attr(String s) {
 
                if (trimStrings)
                        s = trim(s);
@@ -227,55 +162,7 @@ public class JsonWriter extends SerializerWriter {
                else
                        w(s);
 
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter cr(int depth) {
-               super.cr(depth);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter cre(int depth) {
-               super.cre(depth);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter i(int indent) {
-               super.i(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter ie(int indent) {
-               super.ie(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter nl(int indent) {
-               super.nl(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter nlIf(boolean flag, int indent) {
-               super.nlIf(flag, indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter q() {
-               super.q();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter s() {
-               super.s();
-               return this;
+               return self();
        }
 
        /**
@@ -284,16 +171,10 @@ public class JsonWriter extends SerializerWriter {
         * @param indent The number of spaces to indent.
         * @return This object.
         */
-       public JsonWriter s(int indent) {
+       public SELF s(int indent) {
                if (indent <= maxIndent)
                        super.s();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter sIf(boolean flag) {
-               super.sIf(flag);
-               return this;
+               return self();
        }
 
        /**
@@ -302,10 +183,10 @@ public class JsonWriter extends SerializerWriter {
         * @param depth The current indentation depth.
         * @return This object.
         */
-       public JsonWriter smi(int depth) {
+       public SELF smi(int depth) {
                if (depth > maxIndent)
                        super.s();
-               return this;
+               return self();
        }
 
        /**
@@ -318,9 +199,9 @@ public class JsonWriter extends SerializerWriter {
        @SuppressWarnings({
                "java:S3776" // Cognitive complexity acceptable for JSON string 
value writing with escaping
        })
-       public JsonWriter stringValue(String s) {
+       public SELF stringValue(String s) {
                if (s == null)
-                       return this;
+                       return self();
                boolean doConvert = false;
                for (var i = 0; i < s.length() && ! doConvert; i++) {
                        var c = s.charAt(i);
@@ -355,7 +236,7 @@ public class JsonWriter extends SerializerWriter {
                        }
                }
                q();
-               return this;
+               return self();
        }
 
        /**
@@ -364,19 +245,7 @@ public class JsonWriter extends SerializerWriter {
         * @param uri The URI to append to the output.
         * @return This object.
         */
-       public SerializerWriter uriValue(Object uri) {
+       public SELF uriValue(Object uri) {
                return stringValue(uriResolver.resolve(uri));
        }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter w(char value) {
-               super.w(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public JsonWriter w(String value) {
-               super.w(value);
-               return this;
-       }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5SerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5SerializerSession.java
index c7c2427bf3..5df7b5d898 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5SerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5/Json5SerializerSession.java
@@ -86,9 +86,9 @@ public class Json5SerializerSession extends 
JsonSerializerSession {
        protected boolean isSimpleAttrs() { return true; }
 
        @Override
-       protected JsonWriter getJsonWriter(SerializerPipe out) {
+       protected JsonWriter<?> getJsonWriter(SerializerPipe out) {
                var output = out.getRawOutput();
-               if (output instanceof JsonWriter output2)
+               if (output instanceof JsonWriter<?> output2)
                        return output2;
                var w = JsonWriter.create(out.getWriter(), isUseWhitespace(), 
getMaxIndent(), isEscapeSolidus(), getQuoteChar(), true, isTrimStrings(), 
getUriResolver());
                out.setWriter(w);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializerSession.java
index b9dad10c32..925dbc9ce5 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializerSession.java
@@ -102,11 +102,11 @@ public class Json5lSerializerSession extends 
JsonlSerializerSession {
        }
 
        @Override /* Overridden from JsonSerializerSession */
-       protected JsonWriter getJsonWriter(SerializerPipe out) {
+       protected JsonWriter<?> getJsonWriter(SerializerPipe out) {
                if (! json5Sugar)
                        return super.getJsonWriter(out);
                var output = out.getRawOutput();
-               if (output instanceof JsonWriter output2)
+               if (output instanceof JsonWriter<?> output2)
                        return output2;
                var w = JsonWriter.create(out.getWriter(), isUseWhitespace(), 
getMaxIndent(), isEscapeSolidus(), SUGAR_QUOTE, true, isTrimStrings(), 
getUriResolver());
                out.setWriter(w);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/markdown/MarkdownWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/markdown/MarkdownWriter.java
index 5316a5b590..025e2c6ad6 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/markdown/MarkdownWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/markdown/MarkdownWriter.java
@@ -36,7 +36,7 @@ import org.apache.juneau.marshall.serializer.*;
        "java:S110", // Inheritance depth acceptable for serializer writer 
hierarchy
        "resource"   // Writer lifecycle managed by caller
 })
-public class MarkdownWriter extends SerializerWriter {
+public class MarkdownWriter extends SerializerWriter<MarkdownWriter> {
 
        /**
         * Constructor.
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/prototext/PrototextWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/prototext/PrototextWriter.java
index 7d720db347..099e5de523 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/prototext/PrototextWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/prototext/PrototextWriter.java
@@ -42,7 +42,7 @@ import org.apache.juneau.marshall.serializer.*;
        "java:S3776", // Cognitive complexity acceptable for protobuf 
formatting methods
        "resource" // Writer resource managed by calling code
 })
-public class PrototextWriter extends SerializerWriter {
+public class PrototextWriter extends SerializerWriter<PrototextWriter> {
 
        /**
         * Constructor.
@@ -322,28 +322,4 @@ public class PrototextWriter extends SerializerWriter {
                }
                return sb.toString();
        }
-
-       // Override return types for chaining
-       @Override public PrototextWriter append(char c) { super.append(c); 
return this; }
-       @Override public PrototextWriter append(char[] value) { 
super.append(value); return this; }
-       @Override public PrototextWriter append(int indent, char c) { 
super.append(indent, c); return this; }
-       @Override public PrototextWriter append(int indent, String text) { 
super.append(indent, text); return this; }
-       @Override public PrototextWriter append(Object text) { 
super.append(text); return this; }
-       @Override public PrototextWriter append(String text) { 
super.append(text); return this; }
-       @Override public PrototextWriter appendIf(boolean b, char c) { 
super.appendIf(b, c); return this; }
-       @Override public PrototextWriter appendIf(boolean b, String text) { 
super.appendIf(b, text); return this; }
-       @Override public PrototextWriter appendln(int indent, String text) { 
super.appendln(indent, text); return this; }
-       @Override public PrototextWriter appendln(String text) { 
super.appendln(text); return this; }
-       @Override public PrototextWriter appendUri(Object value) { 
super.appendUri(value); return this; }
-       @Override public PrototextWriter cr(int depth) { super.cr(depth); 
return this; }
-       @Override public PrototextWriter cre(int depth) { super.cre(depth); 
return this; }
-       @Override public PrototextWriter i(int indent) { super.i(indent); 
return this; }
-       @Override public PrototextWriter ie(int indent) { super.ie(indent); 
return this; }
-       @Override public PrototextWriter nl(int indent) { super.nl(indent); 
return this; }
-       @Override public PrototextWriter nlIf(boolean flag, int indent) { 
super.nlIf(flag, indent); return this; }
-       @Override public PrototextWriter q() { super.q(); return this; }
-       @Override public PrototextWriter s() { super.s(); return this; }
-       @Override public PrototextWriter sIf(boolean flag) { super.sIf(flag); 
return this; }
-       @Override public PrototextWriter w(char value) { super.w(value); return 
this; }
-       @Override public PrototextWriter w(String value) { super.w(value); 
return this; }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/SerializerWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/SerializerWriter.java
index 71fd870cc5..c4af485627 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/SerializerWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/serializer/SerializerWriter.java
@@ -41,9 +41,15 @@ import org.apache.juneau.marshall.*;
  * </ul>
  */
 @SuppressWarnings({
-       "resource" // SerializerWriter manages Closeable resources
+       "resource", // SerializerWriter manages Closeable resources
+       "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than 
a single-letter name.
 })
-public class SerializerWriter extends Writer {
+public abstract class SerializerWriter<SELF extends SerializerWriter<SELF>> 
extends Writer {
+
+       @SuppressWarnings("unchecked")
+       protected final SELF self() {
+               return (SELF) this;
+       }
 
        /** The underlying writer. */
        protected final Writer out;
@@ -69,7 +75,7 @@ public class SerializerWriter extends Writer {
         * @param w Writer being copied.
         *      <br>Must not be <jk>null</jk>.
         */
-       public SerializerWriter(SerializerWriter w) {
+       public SerializerWriter(SerializerWriter<?> w) {
                this.out = w.out;
                this.useWhitespace = w.useWhitespace;
                this.maxIndent = w.maxIndent;
@@ -99,13 +105,13 @@ public class SerializerWriter extends Writer {
        }
 
        @Override /* Overridden from Writer */
-       public SerializerWriter append(char c) {
+       public SELF append(char c) {
                try {
                        out.write(c);
                } catch (IOException e) {
                        throw new SerializeException(e);
                }
-               return this;
+               return self();
        }
 
        /**
@@ -115,10 +121,10 @@ public class SerializerWriter extends Writer {
         *      <br>Must not be <jk>null</jk>.
         * @return This object.
         */
-       public SerializerWriter append(char[] value) {
+       public SELF append(char[] value) {
                for (var c : value)
                        w(c);
-               return this;
+               return self();
        }
 
        /**
@@ -128,7 +134,7 @@ public class SerializerWriter extends Writer {
         * @param value The character to write.
         * @return This object.
         */
-       public SerializerWriter append(int indent, char value) {
+       public SELF append(int indent, char value) {
                return i(indent).w(value);
        }
 
@@ -139,7 +145,7 @@ public class SerializerWriter extends Writer {
         * @param value The text to write.
         * @return This object.
         */
-       public SerializerWriter append(int indent, String value) {
+       public SELF append(int indent, String value) {
                return append(indent, false, value);
        }
 
@@ -150,9 +156,9 @@ public class SerializerWriter extends Writer {
         *      <br>Must not be <jk>null</jk>.
         * @return This object.
         */
-       public SerializerWriter append(Object value) {
+       public SELF append(Object value) {
                w(value == null ? null : value.toString());
-               return this;
+               return self();
        }
 
        /**
@@ -162,10 +168,10 @@ public class SerializerWriter extends Writer {
         *      <br>Can be <jk>null</jk> (ignored).
         * @return This object.
         */
-       public SerializerWriter append(String value) {
+       public SELF append(String value) {
                if (nn(value))
                        w(value);
-               return this;
+               return self();
        }
 
        /**
@@ -175,10 +181,10 @@ public class SerializerWriter extends Writer {
         * @param value The text to write.
         * @return This object.
         */
-       public SerializerWriter appendIf(boolean flag, char value) {
+       public SELF appendIf(boolean flag, char value) {
                if (flag)
                        w(value);
-               return this;
+               return self();
        }
 
        /**
@@ -188,10 +194,10 @@ public class SerializerWriter extends Writer {
         * @param value The text to write.
         * @return This object.
         */
-       public SerializerWriter appendIf(boolean flag, String value) {
+       public SELF appendIf(boolean flag, String value) {
                if (flag)
                        w(value);
-               return this;
+               return self();
        }
 
        /**
@@ -202,7 +208,7 @@ public class SerializerWriter extends Writer {
         * @param value The text to write.
         * @return This object.
         */
-       public SerializerWriter appendln(int indent, String value) {
+       public SELF appendln(int indent, String value) {
                return append(indent, true, value);
        }
 
@@ -212,7 +218,7 @@ public class SerializerWriter extends Writer {
         * @param value The text to write.
         * @return This object.
         */
-       public SerializerWriter appendln(String value) {
+       public SELF appendln(String value) {
                return append(0, true, value);
        }
 
@@ -232,9 +238,9 @@ public class SerializerWriter extends Writer {
         *      <br>Can be <jk>null</jk>.
         * @return This object.
         */
-       public SerializerWriter appendUri(Object value) {
+       public SELF appendUri(Object value) {
                uriResolver.append(this, value);
-               return this;
+               return self();
        }
 
        @Override /* Overridden from Writer */
@@ -251,10 +257,10 @@ public class SerializerWriter extends Writer {
         * @param depth The indentation.
         * @return This object.
         */
-       public SerializerWriter cr(int depth) {
+       public SELF cr(int depth) {
                if (useWhitespace && depth <= maxIndent)
                        return nl(depth).i(depth);
-               return this;
+               return self();
        }
 
        /**
@@ -266,10 +272,10 @@ public class SerializerWriter extends Writer {
         * @param depth The indentation.
         * @return This object.
         */
-       public SerializerWriter cre(int depth) {
+       public SELF cre(int depth) {
                if (useWhitespace && depth <= maxIndent - 1)
                        return nl(depth).i(depth);
-               return this;
+               return self();
        }
 
        @Override /* Overridden from Writer */
@@ -283,11 +289,11 @@ public class SerializerWriter extends Writer {
         * @param indent The number of tabs to indent.
         * @return This object.
         */
-       public SerializerWriter i(int indent) {
+       public SELF i(int indent) {
                if (useWhitespace && indent <= maxIndent)
                        for (var i = 0; i < indent; i++)
                                w('\t');
-               return this;
+               return self();
        }
 
        /**
@@ -296,11 +302,11 @@ public class SerializerWriter extends Writer {
         * @param indent The number of tabs to indent.
         * @return This object.
         */
-       public SerializerWriter ie(int indent) {
+       public SELF ie(int indent) {
                if (useWhitespace && indent <= maxIndent - 1)
                        for (var i = 0; i < indent; i++)
                                w('\t');
-               return this;
+               return self();
        }
 
        /**
@@ -309,10 +315,10 @@ public class SerializerWriter extends Writer {
         * @param indent The current indentation level.
         * @return This object.
         */
-       public SerializerWriter nl(int indent) {
+       public SELF nl(int indent) {
                if (useWhitespace && indent <= maxIndent)
                        w('\n');
-               return this;
+               return self();
        }
 
        /**
@@ -322,10 +328,10 @@ public class SerializerWriter extends Writer {
         * @param indent The current indentation level.
         * @return This object.
         */
-       public SerializerWriter nlIf(boolean flag, int indent) {
+       public SELF nlIf(boolean flag, int indent) {
                if (flag && useWhitespace && indent <= maxIndent)
                        w('\n');
-               return this;
+               return self();
        }
 
        /**
@@ -333,9 +339,9 @@ public class SerializerWriter extends Writer {
         *
         * @return This object.
         */
-       public SerializerWriter q() {
+       public SELF q() {
                w(quoteChar);
-               return this;
+               return self();
        }
 
        /**
@@ -343,10 +349,10 @@ public class SerializerWriter extends Writer {
         *
         * @return This object.
         */
-       public SerializerWriter s() {
+       public SELF s() {
                if (useWhitespace)
                        w(' ');
-               return this;
+               return self();
        }
 
        /**
@@ -359,10 +365,10 @@ public class SerializerWriter extends Writer {
         * @param flag The boolean flag.
         * @return This object.
         */
-       public SerializerWriter sIf(boolean flag) {
+       public SELF sIf(boolean flag) {
                if (flag && ! useWhitespace)
                        w(' ');
-               return this;
+               return self();
        }
 
        /**
@@ -371,13 +377,13 @@ public class SerializerWriter extends Writer {
         * @param value The character to write.
         * @return This object.
         */
-       public SerializerWriter w(char value) {
+       public SELF w(char value) {
                try {
                        out.write(value);
                } catch (IOException e) {
                        throw new SerializeException(e);
                }
-               return this;
+               return self();
        }
 
        /**
@@ -387,13 +393,13 @@ public class SerializerWriter extends Writer {
         *      <br>Must not be <jk>null</jk>.
         * @return This object.
         */
-       public SerializerWriter w(String value) {
+       public SELF w(String value) {
                try {
                        out.write(value);
                } catch (IOException e) {
                        throw new SerializeException(e);
                }
-               return this;
+               return self();
        }
 
        @Override /* Overridden from Writer */
@@ -415,10 +421,10 @@ public class SerializerWriter extends Writer {
         * @throws IOException If a problem occurred trying to write to the 
writer.
         * @return This object.
         */
-       private SerializerWriter append(int indent, boolean newline, String 
value) {
+       private SELF append(int indent, boolean newline, String value) {
 
                if (value == null)
-                       return this;
+                       return self();
 
                // If text contains newlines, we break it up into lines and 
indent them separately.
                if (value.indexOf('\n') != -1 && useWhitespace && indent <= 
maxIndent) {
@@ -431,6 +437,6 @@ public class SerializerWriter extends Writer {
                if (newline)
                        nl(indent);
 
-               return this;
+               return self();
        }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/soap/SoapXmlSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/soap/SoapXmlSerializerSession.java
index a0d30a3beb..4bb2ed1922 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/soap/SoapXmlSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/soap/SoapXmlSerializerSession.java
@@ -106,7 +106,7 @@ public class SoapXmlSerializerSession extends 
XmlSerializerSession {
 
        @Override /* Overridden from SerializerSession */
        protected void doWrite(SerializerPipe out, Object o) throws 
IOException, SerializeException {
-               try (XmlWriter w = getXmlWriter(out)) {
+               try (XmlWriter<?> w = getXmlWriter(out)) {
                        // @formatter:off
                        w.append("<?xml")
                                .attr("version", "1.0")
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/toml/TomlWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/toml/TomlWriter.java
index 22287f649a..5e95d82760 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/toml/TomlWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/toml/TomlWriter.java
@@ -43,7 +43,7 @@ import org.apache.juneau.marshall.serializer.*;
 @SuppressWarnings({
        "resource" // Writer resource managed by calling code
 })
-public class TomlWriter extends SerializerWriter {
+public class TomlWriter extends SerializerWriter<TomlWriter> {
 
        /**
         * Constructor.
@@ -246,28 +246,4 @@ public class TomlWriter extends SerializerWriter {
                }
                return true;
        }
-
-       // Override return types for chaining
-       @Override public TomlWriter append(char c) { super.append(c); return 
this; }
-       @Override public TomlWriter append(char[] value) { super.append(value); 
return this; }
-       @Override public TomlWriter append(int indent, char c) { 
super.append(indent, c); return this; }
-       @Override public TomlWriter append(int indent, String text) { 
super.append(indent, text); return this; }
-       @Override public TomlWriter append(Object text) { super.append(text); 
return this; }
-       @Override public TomlWriter append(String text) { super.append(text); 
return this; }
-       @Override public TomlWriter appendIf(boolean b, char c) { 
super.appendIf(b, c); return this; }
-       @Override public TomlWriter appendIf(boolean b, String text) { 
super.appendIf(b, text); return this; }
-       @Override public TomlWriter appendln(int indent, String text) { 
super.appendln(indent, text); return this; }
-       @Override public TomlWriter appendln(String text) { 
super.appendln(text); return this; }
-       @Override public TomlWriter appendUri(Object value) { 
super.appendUri(value); return this; }
-       @Override public TomlWriter cr(int depth) { super.cr(depth); return 
this; }
-       @Override public TomlWriter cre(int depth) { super.cre(depth); return 
this; }
-       @Override public TomlWriter i(int indent) { super.i(indent); return 
this; }
-       @Override public TomlWriter ie(int indent) { super.ie(indent); return 
this; }
-       @Override public TomlWriter nl(int indent) { super.nl(indent); return 
this; }
-       @Override public TomlWriter nlIf(boolean flag, int indent) { 
super.nlIf(flag, indent); return this; }
-       @Override public TomlWriter q() { super.q(); return this; }
-       @Override public TomlWriter s() { super.s(); return this; }
-       @Override public TomlWriter sIf(boolean flag) { super.sIf(flag); return 
this; }
-       @Override public TomlWriter w(char value) { super.w(value); return 
this; }
-       @Override public TomlWriter w(String value) { super.w(value); return 
this; }
 }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/uon/UonSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/uon/UonSerializerSession.java
index 500234ea5e..488774720a 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/uon/UonSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/uon/UonSerializerSession.java
@@ -206,7 +206,7 @@ public class UonSerializerSession extends 
WriterSerializerSession implements Htt
                return new UonWriter(this, out, isUseWhitespace(), 
getMaxIndent(), isEncoding(), isTrimStrings(), plainTextParams, getQuoteChar(), 
getUriResolver());
        }
 
-       private SerializerWriter writeBeanMap(UonWriter out, BeanMap<?> m, 
String typeName) throws SerializeException {
+       private SerializerWriter<?> writeBeanMap(UonWriter out, BeanMap<?> m, 
String typeName) throws SerializeException {
 
                if (! plainTextParams)
                        out.append('(');
@@ -248,7 +248,7 @@ public class UonSerializerSession extends 
WriterSerializerSession implements Htt
                "rawtypes", // Raw types necessary for generic collection/map 
serialization
                "unchecked", // Type erasure requires unchecked casts in 
collection/map serialization
        })
-       private SerializerWriter writeCollection(UonWriter out, Collection c, 
ClassMeta<?> type) throws SerializeException {
+       private SerializerWriter<?> writeCollection(UonWriter out, Collection 
c, ClassMeta<?> type) throws SerializeException {
 
                var elementType = type.getElementType();
 
@@ -269,7 +269,7 @@ public class UonSerializerSession extends 
WriterSerializerSession implements Htt
                return out;
        }
 
-       private SerializerWriter writeStreamable(UonWriter out, Object o, 
ClassMeta<?> sType, ClassMeta<?> type) throws SerializeException {
+       private SerializerWriter<?> writeStreamable(UonWriter out, Object o, 
ClassMeta<?> sType, ClassMeta<?> type) throws SerializeException {
 
                var elementType = type.getElementType();
 
@@ -294,7 +294,7 @@ public class UonSerializerSession extends 
WriterSerializerSession implements Htt
                "rawtypes", // Raw types necessary for generic collection/map 
serialization
                "unchecked", // Type erasure requires unchecked casts in 
collection/map serialization
        })
-       private SerializerWriter writeMap(UonWriter out, Map m, ClassMeta<?> 
type) throws SerializeException {
+       private SerializerWriter<?> writeMap(UonWriter out, Map m, ClassMeta<?> 
type) throws SerializeException {
 
                var keyType = type.getKeyType();
                var valueType = type.getValueType();
@@ -385,7 +385,7 @@ public class UonSerializerSession extends 
WriterSerializerSession implements Htt
                "rawtypes", // Raw types necessary for generic type handling
                "java:S3776", // Cognitive complexity acceptable for this 
specific logic
        })
-       protected SerializerWriter writeAnything(UonWriter out, Object o, 
ClassMeta<?> eType, String attrName, BeanPropertyMeta pMeta) throws 
SerializeException {
+       protected SerializerWriter<?> writeAnything(UonWriter out, Object o, 
ClassMeta<?> eType, String attrName, BeanPropertyMeta pMeta) throws 
SerializeException {
 
                if (o == null) {
                        out.appendObject(null, false);
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/uon/UonWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/uon/UonWriter.java
index 57a64d1b25..8abd9fac74 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/uon/UonWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/uon/UonWriter.java
@@ -41,7 +41,7 @@ import org.apache.juneau.marshall.serializer.*;
        "resource", // Resource management handled externally
        "java:S4144", // Identical methods intentional for different test 
scenarios
 })
-public class UonWriter extends SerializerWriter {
+public class UonWriter extends SerializerWriter<UonWriter> {
 
        // Characters that do not need to be URL-encoded in strings.
        private static final AsciiSet unencodedChars = 
AsciiSet.create().ranges("a-z", "A-Z", 
"0-9").chars(";/?:@-_.!*'$(),~=").build();
@@ -87,66 +87,6 @@ public class UonWriter extends SerializerWriter {
                this.quoteChar2 = quoteChar;
        }
 
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter append(char c) {
-               super.append(c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter append(char[] value) {
-               super.append(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter append(int indent, char c) {
-               super.append(indent, c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter append(int indent, String text) {
-               super.append(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter append(Object text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter append(String text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter appendIf(boolean b, char c) {
-               super.appendIf(b, c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter appendIf(boolean b, String text) {
-               super.appendIf(b, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter appendln(int indent, String text) {
-               super.appendln(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter appendln(String text) {
-               super.appendln(text);
-               return this;
-       }
-
        /**
         * Serializes the specified simple object as a UON string value.
         *
@@ -224,72 +164,6 @@ public class UonWriter extends SerializerWriter {
                return appendObject(uriResolver.resolve(uri), false);
        }
 
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter cr(int depth) {
-               super.cr(depth);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter cre(int depth) {
-               super.cre(depth);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter i(int indent) {
-               super.i(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter ie(int indent) {
-               super.ie(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter nl(int indent) {
-               super.nl(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter nlIf(boolean flag, int indent) {
-               super.nlIf(flag, indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter q() {
-               super.q();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter s() {
-               super.s();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter sIf(boolean flag) {
-               super.sIf(flag);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter w(char value) {
-               super.w(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public UonWriter w(String value) {
-               super.w(value);
-               return this;
-       }
-
        /**
         * Prints out a two-byte %xx sequence for the given byte value.
         */
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/urlencoding/UrlEncodingSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/urlencoding/UrlEncodingSerializerSession.java
index 15bd440b23..4df6ca0732 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/urlencoding/UrlEncodingSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/urlencoding/UrlEncodingSerializerSession.java
@@ -155,7 +155,7 @@ public class UrlEncodingSerializerSession extends 
UonSerializerSession {
        @SuppressWarnings({
                "java:S3776" // Cognitive complexity acceptable for URL 
encoding serialization routing
        })
-       private SerializerWriter writeAnything(UonWriter out, Object o) throws 
IOException, SerializeException {
+       private SerializerWriter<?> writeAnything(UonWriter out, Object o) 
throws IOException, SerializeException {
 
                ClassMeta<?> aType = null;                      // The actual 
type
                ClassMeta<?> sType = null;                      // The 
serialized type
@@ -215,7 +215,7 @@ public class UrlEncodingSerializerSession extends 
UonSerializerSession {
                "java:S3776", // Cognitive complexity acceptable for 
expanded-params bean map serialization
                "java:S2177", // Intentional: UrlEncodingSerializerSession 
provides its own private writeBeanMap() with expanded-params logic
        })
-       private SerializerWriter writeBeanMap(UonWriter out, BeanMap<?> m, 
String typeName) throws SerializeException {
+       private SerializerWriter<?> writeBeanMap(UonWriter out, BeanMap<?> m, 
String typeName) throws SerializeException {
                var addAmp = Flag.create();
 
                if (nn(typeName)) {
@@ -279,7 +279,7 @@ public class UrlEncodingSerializerSession extends 
UonSerializerSession {
                return out;
        }
 
-       private SerializerWriter writeCollectionMap(UonWriter out, Map<?,?> m, 
ClassMeta<?> type) throws SerializeException {
+       private SerializerWriter<?> writeCollectionMap(UonWriter out, Map<?,?> 
m, ClassMeta<?> type) throws SerializeException {
 
                var valueType = type.getValueType();
 
@@ -294,7 +294,7 @@ public class UrlEncodingSerializerSession extends 
UonSerializerSession {
                return out;
        }
 
-       private SerializerWriter writeStreamableAsCollectionMap(UonWriter out, 
Object o, ClassMeta<?> sType) throws SerializeException {
+       private SerializerWriter<?> writeStreamableAsCollectionMap(UonWriter 
out, Object o, ClassMeta<?> sType) throws SerializeException {
                var addAmp = Flag.create();
                var i = IntegerHolder.create();
                forEachStreamableEntry(o, sType, v -> {
@@ -308,7 +308,7 @@ public class UrlEncodingSerializerSession extends 
UonSerializerSession {
        @SuppressWarnings({
                "java:S2177" // Intentional: UrlEncodingSerializerSession 
provides its own private writeMap() with expanded-params logic
        })
-       private SerializerWriter writeMap(UonWriter out, Map m, ClassMeta<?> 
type) throws SerializeException {
+       private SerializerWriter<?> writeMap(UonWriter out, Map m, ClassMeta<?> 
type) throws SerializeException {
 
                var keyType = type.getKeyType();
                var valueType = type.getValueType();
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/xml/BasicXmlWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/xml/BasicXmlWriter.java
new file mode 100644
index 0000000000..ed6644f66f
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/xml/BasicXmlWriter.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.marshall.xml;
+
+import java.io.*;
+
+import org.apache.juneau.marshall.*;
+
+/**
+ * Concrete {@link XmlWriter} leaf for standard XML output.
+ *
+ * <p>
+ * This is the directly-instantiable form of {@link XmlWriter}, which is 
itself an abstract CRTP self-type base so it
+ * can also be extended (e.g. by {@link 
org.apache.juneau.marshall.html.HtmlWriter}).
+ *
+ * <h5 class='section'>Notes:</h5><ul>
+ *     <li class='note'>
+ *             This class is not intended for external use.
+ * </ul>
+ */
+@SuppressWarnings({
+       "resource" // Writer resource managed by calling code
+})
+public final class BasicXmlWriter extends XmlWriter<BasicXmlWriter> {
+
+       /**
+        * Constructor.
+        *
+        * @param out The wrapped writer.
+        * @param useWhitespace If <jk>true</jk> XML elements will be indented.
+        * @param maxIndent The maximum indentation level.
+        * @param trimStrings If <jk>true</jk>, strings should be trimmed 
before they're serialized.
+        * @param quoteChar The quote character to use for attributes.  Should 
be <js>'\''</js> or <js>'"'</js>.
+        * @param uriResolver The URI resolver for resolving URIs to absolute 
or root-relative form.
+        * @param enableNs Flag to indicate if XML namespaces are enabled.
+        * @param defaultNamespace The default namespace if XML namespaces are 
enabled.
+        */
+       @SuppressWarnings({
+               "java:S107" // Constructor requires 8 parameters for XML writer 
configuration
+       })
+       public BasicXmlWriter(Writer out, boolean useWhitespace, int maxIndent, 
boolean trimStrings, char quoteChar, UriResolver uriResolver, boolean enableNs, 
Namespace defaultNamespace) {
+               super(out, useWhitespace, maxIndent, trimStrings, quoteChar, 
uriResolver, enableNs, defaultNamespace);
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/xml/XmlSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/xml/XmlSerializerSession.java
index 939029c494..5d1524ab79 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/xml/XmlSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/xml/XmlSerializerSession.java
@@ -311,11 +311,11 @@ public class XmlSerializerSession extends 
WriterSerializerSession implements Rec
         *      <br>Must not be <jk>null</jk>.
         * @return The output target object wrapped in an {@link XmlWriter}.
         */
-       public final XmlWriter getXmlWriter(SerializerPipe out) {
+       public final XmlWriter<?> getXmlWriter(SerializerPipe out) {
                var output = out.getRawOutput();
-               if (output instanceof XmlWriter output2)
+               if (output instanceof XmlWriter<?> output2)
                        return output2;
-               var w = new XmlWriter(out.getWriter(), isUseWhitespace(), 
getMaxIndent(), isTrimStrings(), getQuoteChar(), getUriResolver(), 
isEnableNamespaces(), defaultNamespace);
+               var w = new BasicXmlWriter(out.getWriter(), isUseWhitespace(), 
getMaxIndent(), isTrimStrings(), getQuoteChar(), getUriResolver(), 
isEnableNamespaces(), defaultNamespace);
                out.setWriter(w);
                return w;
        }
@@ -392,7 +392,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession implements Rec
                "java:S3776", // Cognitive complexity acceptable for this 
specific logic
                "java:S6541", // Single-threaded session contexts do not 
require synchronization
        })
-       private ContentResult writeBeanMap(XmlWriter out, BeanMap<?> m, 
Namespace elementNs, boolean isCollapsed, boolean isMixedOrText) throws 
SerializeException {
+       private ContentResult writeBeanMap(XmlWriter<?> out, BeanMap<?> m, 
Namespace elementNs, boolean isCollapsed, boolean isMixedOrText) throws 
SerializeException {
                boolean hasChildren = false;
                var bm = m.getMeta();
 
@@ -536,7 +536,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession implements Rec
                return isMixedOrText ? CR_MIXED : CR_ELEMENTS;
        }
 
-       private XmlWriter writeCollection(XmlWriter out, Object in, 
ClassMeta<?> sType, ClassMeta<?> eType, BeanPropertyMeta ppMeta, boolean 
isMixed) throws SerializeException {
+       private XmlWriter<?> writeCollection(XmlWriter<?> out, Object in, 
ClassMeta<?> sType, ClassMeta<?> eType, BeanPropertyMeta ppMeta, boolean 
isMixed) throws SerializeException {
 
                var eeType = eType.getElementType();
 
@@ -571,7 +571,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession implements Rec
                return out;
        }
 
-       private XmlWriter writeStreamable(XmlWriter out, Object in, 
ClassMeta<?> sType, ClassMeta<?> eType, BeanPropertyMeta ppMeta, boolean 
isMixed) throws SerializeException {
+       private XmlWriter<?> writeStreamable(XmlWriter<?> out, Object in, 
ClassMeta<?> sType, ClassMeta<?> eType, BeanPropertyMeta ppMeta, boolean 
isMixed) throws SerializeException {
 
                var eeType = eType.getElementType();
 
@@ -600,7 +600,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession implements Rec
                return out;
        }
 
-       private ContentResult writeMap(XmlWriter out, Map m, ClassMeta<?> 
sType, ClassMeta<?> eKeyType, ClassMeta<?> eValueType, boolean isMixed) throws 
SerializeException {
+       private ContentResult writeMap(XmlWriter<?> out, Map m, ClassMeta<?> 
sType, ClassMeta<?> eKeyType, ClassMeta<?> eValueType, boolean isMixed) throws 
SerializeException {
 
                var keyType = eKeyType == null ? sType.getKeyType() : eKeyType;
                var valueType = eValueType == null ? sType.getValueType() : 
eValueType;
@@ -843,7 +843,7 @@ public class XmlSerializerSession extends 
WriterSerializerSession implements Rec
                "java:S6541", // Single-threaded session contexts do not 
require synchronization
                "java:S107", // Method has many parameters; acceptable for 
builder/configuration methods
        })
-       protected ContentResult writeAnything(XmlWriter out, Object o, 
ClassMeta<?> eType, String keyName, String elementName, Namespace 
elementNamespace, boolean addNamespaceUris, XmlFormat format,
+       protected ContentResult writeAnything(XmlWriter<?> out, Object o, 
ClassMeta<?> eType, String keyName, String elementName, Namespace 
elementNamespace, boolean addNamespaceUris, XmlFormat format,
                boolean isMixedOrText, boolean preserveWhitespace, 
BeanPropertyMeta pMeta) throws SerializeException {
 
                JsonType type = null;              // The type string (e.g. 
<type> or <x x='type'>
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/xml/XmlWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/xml/XmlWriter.java
index 78a7e74e81..d5224d0ae6 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/xml/XmlWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/xml/XmlWriter.java
@@ -38,9 +38,10 @@ import org.apache.juneau.marshall.serializer.*;
  * </ul>
  */
 @SuppressWarnings({
-       "resource" // Writer resource managed by calling code
+       "resource", // Writer resource managed by calling code
+       "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than 
a single-letter name.
 })
-public class XmlWriter extends SerializerWriter {
+public abstract class XmlWriter<SELF extends XmlWriter<SELF>> extends 
SerializerWriter<SELF> {
 
        private String defaultNsPrefix;
        private boolean enableNs;
@@ -71,82 +72,12 @@ public class XmlWriter extends SerializerWriter {
         *
         * @param w Writer being copied.
         */
-       public XmlWriter(XmlWriter w) {
+       public XmlWriter(XmlWriter<?> w) {
                super(w);
                this.enableNs = w.enableNs;
                this.defaultNsPrefix = w.defaultNsPrefix;
        }
 
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter append(char c) {
-               try {
-                       out.write(c);
-               } catch (IOException e) {
-                       throw new SerializeException(e);
-               }
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter append(char[] value) {
-               super.append(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter append(int indent, char c) {
-               super.append(indent, c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter append(int indent, String text) {
-               super.append(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter append(Object text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter append(String text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter appendIf(boolean flag, char value) {
-               super.appendIf(flag, value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter appendIf(boolean flag, String value) {
-               super.appendIf(flag, value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter appendln(int indent, String text) {
-               super.appendln(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter appendln(String text) {
-               super.appendln(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter appendUri(Object value) {
-               super.appendUri(value);
-               return this;
-       }
-
        /**
         * Same as {@link #attr(String, String, Object)}, except pass in a 
{@link Namespace} object for the namespace.
         *
@@ -155,7 +86,7 @@ public class XmlWriter extends SerializerWriter {
         * @param value The attribute value.
         * @return This object.
         */
-       public XmlWriter attr(Namespace ns, String name, Object value) {
+       public SELF attr(Namespace ns, String name, Object value) {
                return oAttr(ns == null ? null : ns.name, 
name).q().attrValue(value, false).q();
        }
 
@@ -166,7 +97,7 @@ public class XmlWriter extends SerializerWriter {
         * @param value The attribute value.
         * @return This object.
         */
-       public XmlWriter attr(String name, Object value) {
+       public SELF attr(String name, Object value) {
                return attr((String)null, name, value);
        }
 
@@ -178,7 +109,7 @@ public class XmlWriter extends SerializerWriter {
         * @param valNeedsEncoding If <jk>true</jk>, attribute name will be 
encoded.
         * @return This object.
         */
-       public XmlWriter attr(String name, Object value, boolean 
valNeedsEncoding) {
+       public SELF attr(String name, Object value, boolean valNeedsEncoding) {
                return attr(null, name, value, valNeedsEncoding);
        }
 
@@ -190,7 +121,7 @@ public class XmlWriter extends SerializerWriter {
         * @param value The attribute value.
         * @return This object.
         */
-       public XmlWriter attr(String ns, String name, Object value) {
+       public SELF attr(String ns, String name, Object value) {
                return oAttr(ns, name).q().attrValue(value, false).q();
        }
 
@@ -203,7 +134,7 @@ public class XmlWriter extends SerializerWriter {
         * @param valNeedsEncoding If <jk>true</jk>, attribute name will be 
encoded.
         * @return This object.
         */
-       public XmlWriter attr(String ns, String name, Object value, boolean 
valNeedsEncoding) {
+       public SELF attr(String ns, String name, Object value, boolean 
valNeedsEncoding) {
                return oAttr(ns, name).q().attrValue(value, 
valNeedsEncoding).q();
        }
 
@@ -215,7 +146,7 @@ public class XmlWriter extends SerializerWriter {
         * @param value The attribute value, convertible to a URI via 
<c>toString()</c>
         * @return This object.
         */
-       public XmlWriter attrUri(Namespace ns, String name, Object value) {
+       public SELF attrUri(Namespace ns, String name, Object value) {
                return attr(ns, name, uriResolver.resolve(value));
        }
 
@@ -226,7 +157,7 @@ public class XmlWriter extends SerializerWriter {
         * @param value The attribute value.  Can be any object whose 
<c>toString()</c> method returns a URI.
         * @return This object.
         */
-       public XmlWriter attrUri(String name, Object value) {
+       public SELF attrUri(String name, Object value) {
                return attrUri((String)null, name, value);
        }
 
@@ -238,7 +169,7 @@ public class XmlWriter extends SerializerWriter {
         * @param value The attribute value, convertible to a URI via 
<c>toString()</c>
         * @return This object.
         */
-       public XmlWriter attrUri(String ns, String name, Object value) {
+       public SELF attrUri(String ns, String name, Object value) {
                return attr(ns, name, uriResolver.resolve(value), true);
        }
 
@@ -250,21 +181,9 @@ public class XmlWriter extends SerializerWriter {
         *
         * @return This object.
         */
-       public XmlWriter ceTag() {
+       public SELF ceTag() {
                w('/').w('>');
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter cr(int depth) {
-               super.cr(depth);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter cre(int depth) {
-               super.cre(depth);
-               return this;
+               return self();
        }
 
        /**
@@ -275,9 +194,9 @@ public class XmlWriter extends SerializerWriter {
         *
         * @return This object.
         */
-       public XmlWriter cTag() {
+       public SELF cTag() {
                w('>');
-               return this;
+               return self();
        }
 
        /**
@@ -287,7 +206,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter eTag(int indent, String name) {
+       public SELF eTag(int indent, String name) {
                return i(indent).eTag(name);
        }
 
@@ -299,7 +218,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter eTag(int indent, String ns, String name) {
+       public SELF eTag(int indent, String ns, String name) {
                return i(indent).eTag(ns, name, false);
        }
 
@@ -312,7 +231,7 @@ public class XmlWriter extends SerializerWriter {
         * @param needsEncoding If <jk>true</jk>, element name will be encoded.
         * @return This object.
         */
-       public XmlWriter eTag(int indent, String ns, String name, boolean 
needsEncoding) {
+       public SELF eTag(int indent, String ns, String name, boolean 
needsEncoding) {
                return i(indent).eTag(ns, name, needsEncoding);
        }
 
@@ -322,7 +241,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter eTag(String name) {
+       public SELF eTag(String name) {
                return eTag(null, name);
        }
 
@@ -333,7 +252,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter eTag(String ns, String name) {
+       public SELF eTag(String ns, String name) {
                return eTag(ns, name, false);
        }
 
@@ -345,7 +264,7 @@ public class XmlWriter extends SerializerWriter {
         * @param needsEncoding If <jk>true</jk>, element name will be encoded.
         * @return This object.
         */
-       public XmlWriter eTag(String ns, String name, boolean needsEncoding) {
+       public SELF eTag(String ns, String name, boolean needsEncoding) {
                w('<').w('/');
                if (enableNs && nn(ns) && ! (ns.isEmpty() || 
ns.equals(defaultNsPrefix)))
                        w(ns).w(':');
@@ -354,31 +273,7 @@ public class XmlWriter extends SerializerWriter {
                else
                        append(name);
                w('>');
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter i(int indent) {
-               super.i(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter ie(int indent) {
-               super.ie(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter nl(int indent) {
-               super.nl(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter nlIf(boolean flag, int indent) {
-               super.nlIf(flag, indent);
-               return this;
+               return self();
        }
 
        /**
@@ -388,7 +283,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The attribute name.
         * @return This object.
         */
-       public XmlWriter oAttr(Namespace ns, String name) {
+       public SELF oAttr(Namespace ns, String name) {
                return oAttr(ns == null ? null : ns.name, name);
        }
 
@@ -399,12 +294,12 @@ public class XmlWriter extends SerializerWriter {
         * @param name The attribute name.
         * @return This object.
         */
-       public XmlWriter oAttr(String ns, String name) {
+       public SELF oAttr(String ns, String name) {
                w(' ');
                if (enableNs && nn(ns) && ! (ns.isEmpty() || 
ns.equals(defaultNsPrefix)))
                        w(ns).w(':');
                w(name).w('=');
-               return this;
+               return self();
        }
 
        /**
@@ -414,7 +309,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter oTag(int indent, String name) {
+       public SELF oTag(int indent, String name) {
                return i(indent).oTag(null, name, false);
        }
 
@@ -426,7 +321,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter oTag(int indent, String ns, String name) {
+       public SELF oTag(int indent, String ns, String name) {
                return i(indent).oTag(ns, name, false);
        }
 
@@ -439,7 +334,7 @@ public class XmlWriter extends SerializerWriter {
         * @param needsEncoding If <jk>true</jk>, element name will be encoded.
         * @return This object.
         */
-       public XmlWriter oTag(int indent, String ns, String name, boolean 
needsEncoding) {
+       public SELF oTag(int indent, String ns, String name, boolean 
needsEncoding) {
                return i(indent).oTag(ns, name, needsEncoding);
        }
 
@@ -449,7 +344,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter oTag(String name) {
+       public SELF oTag(String name) {
                return oTag(null, name, false);
        }
 
@@ -460,7 +355,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter oTag(String ns, String name) {
+       public SELF oTag(String ns, String name) {
                return oTag(ns, name, false);
        }
 
@@ -472,7 +367,7 @@ public class XmlWriter extends SerializerWriter {
         * @param needsEncoding If <jk>true</jk>, element name will be encoded.
         * @return This object.
         */
-       public XmlWriter oTag(String ns, String name, boolean needsEncoding) {
+       public SELF oTag(String ns, String name, boolean needsEncoding) {
                w('<');
                if (enableNs && nn(ns) && ! (ns.isEmpty() || 
ns.equals(defaultNsPrefix)))
                        w(ns).w(':');
@@ -480,25 +375,7 @@ public class XmlWriter extends SerializerWriter {
                        XmlUtils.encodeElementName(out, name);
                else
                        append(name);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter q() {
-               super.q();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter s() {
-               super.s();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter sIf(boolean flag) {
-               super.sIf(flag);
-               return this;
+               return self();
        }
 
        /**
@@ -508,7 +385,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter sTag(int indent, String name) {
+       public SELF sTag(int indent, String name) {
                return i(indent).sTag(null, name, false);
        }
 
@@ -520,7 +397,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter sTag(int indent, String ns, String name) {
+       public SELF sTag(int indent, String ns, String name) {
                return i(indent).sTag(ns, name, false);
        }
 
@@ -533,7 +410,7 @@ public class XmlWriter extends SerializerWriter {
         * @param needsEncoding If <jk>true</jk>, element name will be encoded.
         * @return This object.
         */
-       public XmlWriter sTag(int indent, String ns, String name, boolean 
needsEncoding) {
+       public SELF sTag(int indent, String ns, String name, boolean 
needsEncoding) {
                return i(indent).sTag(ns, name, needsEncoding);
        }
 
@@ -543,7 +420,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter sTag(String name) {
+       public SELF sTag(String name) {
                return sTag(null, name);
        }
 
@@ -554,7 +431,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter sTag(String ns, String name) {
+       public SELF sTag(String ns, String name) {
                return sTag(ns, name, false);
        }
 
@@ -566,9 +443,9 @@ public class XmlWriter extends SerializerWriter {
         * @param needsEncoding If <jk>true</jk>, element name will be encoded.
         * @return This object.
         */
-       public XmlWriter sTag(String ns, String name, boolean needsEncoding) {
+       public SELF sTag(String ns, String name, boolean needsEncoding) {
                oTag(ns, name, needsEncoding).w('>');
-               return this;
+               return self();
        }
 
        /**
@@ -578,7 +455,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter tag(int indent, String name) {
+       public SELF tag(int indent, String name) {
                return i(indent).tag(name);
        }
 
@@ -590,7 +467,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter tag(int indent, String ns, String name) {
+       public SELF tag(int indent, String ns, String name) {
                return i(indent).tag(ns, name);
        }
 
@@ -603,7 +480,7 @@ public class XmlWriter extends SerializerWriter {
         * @param needsEncoding If <jk>true</jk>, element name will be encoded.
         * @return This object.
         */
-       public XmlWriter tag(int indent, String ns, String name, boolean 
needsEncoding) {
+       public SELF tag(int indent, String ns, String name, boolean 
needsEncoding) {
                return i(indent).tag(ns, name, needsEncoding);
        }
 
@@ -613,7 +490,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter tag(String name) {
+       public SELF tag(String name) {
                return tag(null, name, false);
        }
 
@@ -624,7 +501,7 @@ public class XmlWriter extends SerializerWriter {
         * @param name The element name.
         * @return This object.
         */
-       public XmlWriter tag(String ns, String name) {
+       public SELF tag(String ns, String name) {
                return tag(ns, name, false);
        }
 
@@ -636,7 +513,7 @@ public class XmlWriter extends SerializerWriter {
         * @param needsEncoding If <jk>true</jk>, element name will be encoded.
         * @return This object.
         */
-       public XmlWriter tag(String ns, String name, boolean needsEncoding) {
+       public SELF tag(String ns, String name, boolean needsEncoding) {
                w('<');
                if (enableNs && nn(ns) && ! (ns.isEmpty() || 
ns.equals(defaultNsPrefix)))
                        w(ns).w(':');
@@ -645,7 +522,7 @@ public class XmlWriter extends SerializerWriter {
                else
                        w(name);
                w('/').w('>');
-               return this;
+               return self();
        }
 
        /**
@@ -654,9 +531,9 @@ public class XmlWriter extends SerializerWriter {
         * @param value The object being serialized.
         * @return This object.
         */
-       public XmlWriter text(Object value) {
+       public SELF text(Object value) {
                text(value, false);
-               return this;
+               return self();
        }
 
        /**
@@ -667,9 +544,9 @@ public class XmlWriter extends SerializerWriter {
         *      If <jk>true</jk>, then we're serializing {@link 
XmlFormat#MIXED_PWS} or {@link XmlFormat#TEXT_PWS} content.
         * @return This object.
         */
-       public XmlWriter text(Object value, boolean preserveWhitespace) {
+       public SELF text(Object value, boolean preserveWhitespace) {
                XmlUtils.encodeText(this, value, trimStrings, 
preserveWhitespace);
-               return this;
+               return self();
        }
 
        /**
@@ -678,9 +555,9 @@ public class XmlWriter extends SerializerWriter {
         * @param value The object being serialized.
         * @return This object.
         */
-       public XmlWriter textUri(Object value) {
+       public SELF textUri(Object value) {
                text(uriResolver.resolve(value), false);
-               return this;
+               return self();
        }
 
        @Override /* Overridden from Object */
@@ -688,25 +565,13 @@ public class XmlWriter extends SerializerWriter {
                return out.toString();
        }
 
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter w(char c) {
-               super.w(c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public XmlWriter w(String s) {
-               super.w(s);
-               return this;
-       }
-
-       private XmlWriter attrValue(Object value, boolean needsEncoding) {
+       protected SELF attrValue(Object value, boolean needsEncoding) {
                if (needsEncoding)
                        XmlUtils.encodeAttrValue(out, value, this.trimStrings);
                else if (value instanceof URI || value instanceof URL)
                        append(uriResolver.resolve(value));
                else
                        append(value);
-               return this;
+               return self();
        }
 }
\ No newline at end of file
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/yaml/YamlSerializerSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/yaml/YamlSerializerSession.java
index 3be7963c3f..58bfc9213f 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/yaml/YamlSerializerSession.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/yaml/YamlSerializerSession.java
@@ -261,7 +261,7 @@ public class YamlSerializerSession extends 
WriterSerializerSession implements Re
                return false;
        }
 
-       private SerializerWriter writeBeanMap(YamlWriter out, BeanMap<?> m, 
String typeName) throws SerializeException {
+       private SerializerWriter<?> writeBeanMap(YamlWriter out, BeanMap<?> m, 
String typeName) throws SerializeException {
                int i = indent - 1;
 
                var addAtt = Flag.create();
@@ -308,7 +308,7 @@ public class YamlSerializerSession extends 
WriterSerializerSession implements Re
                "rawtypes", // Raw types necessary for generic collection/map 
serialization
                "unchecked", // Type erasure requires unchecked casts in 
collection/map serialization
        })
-       private SerializerWriter writeMap(YamlWriter out, Map m, ClassMeta<?> 
type) throws SerializeException {
+       private SerializerWriter<?> writeMap(YamlWriter out, Map m, 
ClassMeta<?> type) throws SerializeException {
                int i = indent - 1;
 
                var keyType = type.getKeyType();
@@ -347,7 +347,7 @@ public class YamlSerializerSession extends 
WriterSerializerSession implements Re
                "rawtypes", // Raw types necessary for generic collection/map 
serialization
                "unchecked", // Type erasure requires unchecked casts in 
collection/map serialization
        })
-       private SerializerWriter writeCollection(YamlWriter out, Collection c, 
ClassMeta<?> type, boolean asField) throws SerializeException {
+       private SerializerWriter<?> writeCollection(YamlWriter out, Collection 
c, ClassMeta<?> type, boolean asField) throws SerializeException {
                int i = indent - 1;
 
                if (c.isEmpty()) {
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/yaml/YamlWriter.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/yaml/YamlWriter.java
index bde34b499e..b677be3c36 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/yaml/YamlWriter.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/yaml/YamlWriter.java
@@ -40,7 +40,7 @@ import org.apache.juneau.marshall.serializer.*;
 @SuppressWarnings({
        "resource" // Writer resource managed by calling code
 })
-public class YamlWriter extends SerializerWriter {
+public class YamlWriter extends SerializerWriter<YamlWriter> {
 
        private static final AsciiSet specialChars = 
AsciiSet.of(":#{}[],&*!|>'\"%@`");
 
@@ -277,140 +277,4 @@ public class YamlWriter extends SerializerWriter {
                }
                w('"');
        }
-
-       
//-----------------------------------------------------------------------------------------------------------------
-       // Overridden methods - return type covariance for chaining
-       
//-----------------------------------------------------------------------------------------------------------------
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter append(char c) {
-               super.append(c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter append(char[] value) {
-               super.append(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter append(int indent, char c) {
-               super.append(indent, c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter append(int indent, String text) {
-               super.append(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter append(Object text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter append(String text) {
-               super.append(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter appendIf(boolean b, char c) {
-               super.appendIf(b, c);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter appendIf(boolean b, String text) {
-               super.appendIf(b, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter appendln(int indent, String text) {
-               super.appendln(indent, text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter appendln(String text) {
-               super.appendln(text);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter appendUri(Object value) {
-               super.appendUri(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter cr(int depth) {
-               super.cr(depth);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter cre(int depth) {
-               super.cre(depth);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter i(int indent) {
-               super.i(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter ie(int indent) {
-               super.ie(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter nl(int indent) {
-               super.nl(indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter nlIf(boolean flag, int indent) {
-               super.nlIf(flag, indent);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter q() {
-               super.q();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter s() {
-               super.s();
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter sIf(boolean flag) {
-               super.sIf(flag);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter w(char value) {
-               super.w(value);
-               return this;
-       }
-
-       @Override /* Overridden from SerializerWriter */
-       public YamlWriter w(String value) {
-               super.w(value);
-               return this;
-       }
 }

Reply via email to