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 309531fa84 feat(marshall): JSON5L (JSON5 + JSONL) line-delimited format
309531fa84 is described below

commit 309531fa8470e0d3a2370c1cfbc387e90c3806e6
Author: James Bognar <[email protected]>
AuthorDate: Wed Jun 17 08:52:31 2026 -0400

    feat(marshall): JSON5L (JSON5 + JSONL) line-delimited format
---
 .../juneau/marshall/json5l/Json5lConfig.java       |  47 +++++
 .../marshall/json5l/Json5lConfigAnnotation.java    |  76 +++++++
 .../juneau/marshall/json5l/Json5lParser.java       | 154 ++++++++++++++
 .../marshall/json5l/Json5lParserSession.java       | 197 ++++++++++++++++++
 .../juneau/marshall/json5l/Json5lSerializer.java   | 200 ++++++++++++++++++
 .../marshall/json5l/Json5lSerializerSession.java   | 139 +++++++++++++
 .../juneau/marshall/json5l/Json5lTokenReader.java  |  78 +++++++
 .../juneau/marshall/json5l/package-info.java       |  45 ++++
 .../apache/juneau/marshall/marshaller/Json5l.java  | 229 +++++++++++++++++++++
 .../org/apache/juneau/ComboRoundTripTest_Base.java |  28 +++
 .../org/apache/juneau/ComboRoundTrip_Tester.java   |   4 +
 .../marshall/json5l/Json5lCoverage_Test.java       | 199 ++++++++++++++++++
 .../marshall/json5l/Json5lMediaType_Test.java      |  73 +++++++
 .../juneau/marshall/json5l/Json5lParser_Test.java  | 155 ++++++++++++++
 .../marshall/json5l/Json5lRoundTrip_Test.java      |  78 +++++++
 .../marshall/json5l/Json5lTokenStream_Test.java    | 136 ++++++++++++
 .../apache/juneau/marshall/json5l/Json5l_Test.java | 182 ++++++++++++++++
 .../org/apache/juneau/marshaller/Json5l_Test.java  |  99 +++++++++
 .../org/apache/juneau/ComboRoundTripTest_Base.java |  28 +++
 .../org/apache/juneau/ComboRoundTrip_Tester.java   |   4 +
 .../juneau/rest/client/classic/RestClient.java     |   3 +
 .../org/apache/juneau/http/header/ContentType.java |   2 +
 .../juneau/http/header/ContentType_Test.java       |   1 +
 .../server/reactive/ReactiveResponseProcessor.java |   4 +-
 .../rest/server/config/BasicUniversalConfig.java   |   5 +
 25 files changed, 2164 insertions(+), 2 deletions(-)

diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lConfig.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lConfig.java
new file mode 100644
index 0000000000..d3efc265e4
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lConfig.java
@@ -0,0 +1,47 @@
+/*
+ * 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.json5l;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+import org.apache.juneau.marshall.*;
+
+/**
+ * Annotation for specifying config properties for REST classes and methods 
using JSON5L.
+ *
+ * <p>
+ * Used primarily for specifying bean configuration properties on REST classes 
and methods.
+ */
+@Target({ TYPE, METHOD })
+@Retention(RUNTIME)
+@Inherited
+@ContextApply({ Json5lConfigAnnotation.SerializerApply.class, 
Json5lConfigAnnotation.ParserApply.class })
+public @interface Json5lConfig {
+
+       /**
+        * Optional rank for this config.
+        *
+        * <p>
+        * Can be used to override default ordering and application of config 
annotations.
+        *
+        * @return The annotation value.
+        */
+       int rank() default 0;
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lConfigAnnotation.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lConfigAnnotation.java
new file mode 100644
index 0000000000..d9623304ff
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lConfigAnnotation.java
@@ -0,0 +1,76 @@
+/*
+ * 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.json5l;
+
+import org.apache.juneau.commons.reflect.*;
+import org.apache.juneau.commons.svl.*;
+import org.apache.juneau.marshall.*;
+import org.apache.juneau.marshall.json.*;
+
+/**
+ * Utility classes and methods for the {@link Json5lConfig @Json5lConfig} 
annotation.
+ */
+public class Json5lConfigAnnotation {
+
+       private Json5lConfigAnnotation() {}
+
+       /**
+        * Applies {@link Json5lConfig} annotations to a {@link 
org.apache.juneau.marshall.json.JsonParser.Builder}.
+        */
+       @SuppressWarnings({
+               "rawtypes" // Raw types required for reflective annotation 
application.
+       })
+       public static class ParserApply extends AnnotationApplier<Json5lConfig, 
JsonParser.Builder> {
+
+               /**
+                * Constructor.
+                *
+                * @param vr The resolver for resolving values in annotations.
+                */
+               public ParserApply(VarResolverSession vr) {
+                       super(Json5lConfig.class, JsonParser.Builder.class, vr);
+               }
+
+               @Override
+               public void apply(AnnotationInfo<Json5lConfig> ai, 
JsonParser.Builder b) {
+                       // No-op: JSON5L reuses JsonParser.Builder; no 
format-specific settings initially
+               }
+       }
+
+       /**
+        * Applies {@link Json5lConfig} annotations to a {@link 
org.apache.juneau.marshall.json.JsonSerializer.Builder}.
+        */
+       @SuppressWarnings({
+               "rawtypes" // Raw types required for reflective annotation 
application.
+       })
+       public static class SerializerApply extends 
AnnotationApplier<Json5lConfig, JsonSerializer.Builder> {
+
+               /**
+                * Constructor.
+                *
+                * @param vr The resolver for resolving values in annotations.
+                */
+               public SerializerApply(VarResolverSession vr) {
+                       super(Json5lConfig.class, JsonSerializer.Builder.class, 
vr);
+               }
+
+               @Override
+               public void apply(AnnotationInfo<Json5lConfig> ai, 
JsonSerializer.Builder b) {
+                       // No-op: JSON5L reuses JsonSerializer.Builder; no 
format-specific settings initially
+               }
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lParser.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lParser.java
new file mode 100644
index 0000000000..51b6c0c620
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lParser.java
@@ -0,0 +1,154 @@
+/*
+ * 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.json5l;
+
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
+
+import org.apache.juneau.commons.collections.*;
+import org.apache.juneau.marshall.json5.*;
+
+/**
+ * Parses JSON5L (JSON5 Lines) input into POJO models.
+ *
+ * <h5 class='topic'>Media types</h5>
+ * <p>
+ * Handles <c>Content-Type</c> types:  <bc>application/json5l, text/json5l</bc>
+ *
+ * <h5 class='topic'>Description</h5>
+ * <p>
+ * JSON5L combines the relaxed JSON5 dialect with JSONL's newline-delimited 
framing.  Each non-empty,
+ * non-comment line is parsed as a complete JSON5 value (single-quoted 
strings, unquoted field names,
+ * trailing commas, comments, relaxed numbers).  Because JSON5 is a strict 
superset of JSON, this
+ * parser also reads plain JSONL input unchanged.
+ *
+ * <p>
+ * Comment handling follows JSON5 line-by-line: a line that is blank or 
contains only a comment is
+ * skipped (like a blank line); inline comments within a record's JSON are 
tolerated.  A
+ * <c>/* &#42;/</c> block comment must open and close on the same physical 
line — block comments
+ * cannot span record boundaries.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ *     <jc>// Parse JSON5L into a list of POJOs</jc>
+ *     List&lt;MyBean&gt; <jv>list</jv> = 
Json5lParser.<jsf>DEFAULT</jsf>.parse(<jv>json5lInput</jv>, 
List.<jk>class</jk>, MyBean.<jk>class</jk>);
+ * </p>
+ *
+ * <h5 class='figure'>Example input:</h5>
+ * <p class='bjson'>
+ * // header comment line
+ * {name:'Alice',age:30}
+ * {name:'Bob',age:25}  // trailing comment
+ * </p>
+ *
+ * <h5 class='section'>Notes:</h5><ul>
+ *     <li class='note'>This class is thread safe and reusable.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ *     <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/Json5lBasics";>JSON5L Basics</a>
+ * </ul>
+ */
+@SuppressWarnings({
+       "java:S110", // Inheritance depth acceptable for Json5lParser hierarchy
+       "java:S115"  // Constants use naming conventions that embed type info 
or config keys
+})
+public class Json5lParser extends Json5Parser {
+
+       private static final String ARG_copyFrom = "copyFrom";
+
+       /**
+        * Builder class.
+        */
+       public static class Builder extends Json5Parser.Builder {
+
+               private static final Cache<HashKey,Json5lParser> CACHE = 
Cache.of(HashKey.class, Json5lParser.class).build();
+
+               /**
+                * Constructor, default settings.
+                */
+               protected Builder() {
+                       
consumes("application/json5l,text/json5l,application/jsonl,application/x-ndjson,text/jsonl")
+                               .type(Json5lParser.class);
+               }
+
+               /**
+                * Copy constructor.
+                *
+                * @param copyFrom The builder to copy from.
+                *      <br>Cannot be <jk>null</jk>.
+                */
+               protected Builder(Builder copyFrom) {
+                       super(assertArgNotNull(ARG_copyFrom, copyFrom));
+               }
+
+               /**
+                * Copy constructor.
+                *
+                * @param copyFrom The bean to copy from.
+                *      <br>Cannot be <jk>null</jk>.
+                */
+               protected Builder(Json5lParser copyFrom) {
+                       super(assertArgNotNull(ARG_copyFrom, copyFrom));
+               }
+
+               @Override /* Overridden from Context.Builder<?> */
+               public Json5lParser build() {
+                       return cache(CACHE).build(Json5lParser.class);
+               }
+
+               @Override /* Overridden from Context.Builder<?> */
+               public Builder copy() {
+                       return new Builder(this);
+               }
+       }
+
+       /** Default parser, Accept=application/json5l. */
+       public static final Json5lParser DEFAULT = new Json5lParser(create());
+
+       /**
+        * Creates a new builder for this object.
+        *
+        * @return A new builder.
+        */
+       public static Builder create() {
+               return new Builder();
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param builder The builder for this object.
+        */
+       public Json5lParser(Builder builder) {
+               super(builder);
+       }
+
+       @Override /* Overridden from Context */
+       public Builder copy() {
+               return new Builder(this);
+       }
+
+       @Override /* Overridden from Context */
+       public Json5lParserSession.Builder createSession() {
+               return Json5lParserSession.create(this);
+       }
+
+       @Override /* Overridden from Context */
+       public Json5lParserSession getSession() {
+               return createSession().build();
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lParserSession.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lParserSession.java
new file mode 100644
index 0000000000..eb26c47228
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lParserSession.java
@@ -0,0 +1,197 @@
+/*
+ * 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.json5l;
+
+import java.io.*;
+
+import org.apache.juneau.commons.reflect.*;
+import org.apache.juneau.marshall.*;
+import org.apache.juneau.marshall.json.*;
+import org.apache.juneau.marshall.json5.*;
+import org.apache.juneau.marshall.parser.*;
+import org.apache.juneau.marshall.stream.*;
+
+/**
+ * Session object that lives for the duration of a single use of {@link 
Json5lParser}.
+ *
+ * <p>
+ * Extends {@link Json5ParserSession} to inherit the full JSON5 dialect 
(single-quoted strings,
+ * bare/unquoted field names, trailing commas, missing values, comments, 
relaxed numbers) and
+ * re-adds JSONL's newline-delimited framing: input is read one line at a 
time, blank and
+ * comment-only lines are skipped, and each remaining line is parsed as one 
JSON5 value.
+ *
+ * <h5 class='section'>Notes:</h5><ul>
+ *     <li class='warn'>This class is not thread safe and is typically 
discarded after one use.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ *     <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/Json5lBasics";>JSON5L Basics</a>
+ * </ul>
+ */
+@SuppressWarnings({
+       "unchecked", // Type erasure: elementType is ClassMeta<?>; 
toArray/convertToType return Object
+       "java:S110", // Inheritance depth acceptable
+       "resource"   // Closeable resources are owned by the caller's parser 
session; Eclipse JDT @Owning warning is by design.
+})
+public class Json5lParserSession extends Json5ParserSession {
+
+       /**
+        * Builder class.
+        */
+       @SuppressWarnings({
+               "java:S110" // Inheritance depth is intentional across parser 
session builders
+       })
+       public static class Builder extends Json5ParserSession.Builder {
+
+               /**
+                * Constructor
+                *
+                * @param ctx The context creating this session.
+                */
+               protected Builder(Json5lParser ctx) {
+                       super(ctx);
+               }
+
+               @Override
+               public Json5lParserSession build() {
+                       return new Json5lParserSession(this);
+               }
+       }
+
+       /**
+        * Creates a new builder for this object.
+        *
+        * @param ctx The context creating this session.
+        * @return A new builder.
+        */
+       public static Builder create(Json5lParser ctx) {
+               return new Builder(ctx);
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param builder The builder for this object.
+        */
+       protected Json5lParserSession(Builder builder) {
+               super(builder);
+       }
+
+       /**
+        * Opens a low-level pull-parser cursor over a JSON5L document, bound 
to this live session.
+        *
+        * <p>
+        * Each top-level JSON5 value (one per line) is emitted as a flat 
sequence at depth 0; there is
+        * no virtual root container.  Same honored/ignored builder properties 
as
+        * {@link Json5ParserSession#parseTokens(Object)}.
+        *
+        * @param input The input.  Accepts {@link Reader}, {@link 
CharSequence}, {@link InputStream},
+        *      <code><jk>byte</jk>[]</code>, or {@link File}.
+        * @return A new {@link Json5lTokenReader}.
+        * @throws IOException If a problem occurred opening the underlying 
input.
+        */
+       @SuppressWarnings({
+               "java:S2095" // ParserPipe lifecycle is transferred to the 
returned Json5lTokenReader, which closes it via its own close(); the caller 
owns the cursor via try-with-resources.
+       })
+       @Override /* Json5ParserSession */
+       public TokenReader parseTokens(Object input) throws IOException {
+               var pipe = new ParserPipe(
+                       input,
+                       isDebug(),
+                       false /* strict */,
+                       isAutoCloseStreams(),
+                       isUnbuffered(),
+                       getStreamCharset(),
+                       getFileCharset());
+               return new Json5lTokenReader(pipe, new 
JsonTokenReader.Settings(isTrimStrings()), this);
+       }
+
+       /**
+        * Streaming array-element {@link RecordReader} for JSON5L.
+        *
+        * <p>
+        * Aliases the JSON5L line record stream: the {@link Json5lTokenReader} 
returned by
+        * {@link #parseTokens(Object)} is itself a {@link RecordReader} that 
yields one record per
+        * top-level line at depth 0.  Memory is O(1) in the number of lines.
+        *
+        * @param input The input.
+        * @return A new line-delimited {@link RecordReader}.
+        * @throws IOException If a problem occurred opening the underlying 
input.
+        */
+       @Override /* ArrayRecordReadable */
+       public RecordReader parseArrayRecords(Object input) throws IOException {
+               return parseTokens(input);
+       }
+
+       @Override /* Overridden from JsonParserSession */
+       protected <T> T doParse(ParserPipe pipe, ClassMeta<T> type) throws 
IOException, ParseException, ExecutableException {
+               try (var r = pipe.getParserReader()) {
+                       if (r == null)
+                               return null;
+
+                       if (type.isCollectionOrArray()) {
+                               var elementType = type.getElementType();
+                               var results = newGenericList();
+                               var br = new BufferedReader(r);
+                               String line;
+                               while ((line = br.readLine()) != null) {
+                                       var trimmed = line.trim();
+                                       if (isParseable(trimmed)) {
+                                               try (var linePipe = 
createPipe(trimmed)) {
+                                                       var item = 
super.doParse(linePipe, elementType);
+                                                       results.add(item);
+                                               }
+                                       }
+                               }
+                               return type.isArray() ? (T) toArray(type, 
results) : (T) convertToType(results, type);
+                       }
+
+                       // Single object: parse just the first non-empty, 
non-comment line
+                       var br = new BufferedReader(r);
+                       String line;
+                       while ((line = br.readLine()) != null) {
+                               var trimmed = line.trim();
+                               if (isParseable(trimmed)) {
+                                       try (var linePipe = 
createPipe(trimmed)) {
+                                               return super.doParse(linePipe, 
type);
+                                       }
+                               }
+                       }
+                       return null;
+               }
+       }
+
+       /**
+        * Returns <jk>true</jk> if the trimmed line contains a JSON5 value to 
parse — i.e. it is neither
+        * blank nor a comment-only line (a line consisting solely of a 
<c>//</c> line comment or a
+        * single-line <c>/* &#42;/</c> block comment).
+        *
+        * @param trimmed The whitespace-trimmed line.
+        * @return <jk>true</jk> if the line should be parsed as a JSON5 value.
+        */
+       private static boolean isParseable(String trimmed) {
+               if (trimmed.isEmpty())
+                       return false;
+               if (trimmed.startsWith("//"))
+                       return false;
+               // A line that is exactly a single block comment (possibly with 
surrounding whitespace,
+               // already trimmed) is comment-only.  Anything after the 
closing */ makes it parseable.
+               if (trimmed.startsWith("/*") && trimmed.endsWith("*/") && 
trimmed.indexOf("*/") == trimmed.length() - 2)
+                       return false;
+               return true;
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializer.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializer.java
new file mode 100644
index 0000000000..17bc08015f
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializer.java
@@ -0,0 +1,200 @@
+/*
+ * 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.json5l;
+
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
+
+import org.apache.juneau.commons.collections.*;
+import org.apache.juneau.marshall.json.*;
+import org.apache.juneau.marshall.jsonl.*;
+
+/**
+ * Serializes POJO models to JSON5L (JSON5 Lines).
+ *
+ * <h5 class='topic'>Media types</h5>
+ * <p>
+ * Handles <c>Accept</c> types:  <bc>application/json5l, text/json5l</bc>
+ * <p>
+ * Produces <c>Content-Type</c> types:  <bc>application/json5l</bc>
+ *
+ * <h5 class='topic'>Description</h5>
+ * <p>
+ * JSON5L combines the relaxed JSON5 dialect with JSONL's newline-delimited 
framing: each top-level
+ * value is written as a compact document on its own line, exactly as {@link 
JsonlSerializer} does.
+ * <p>
+ * By <b>default</b> the per-line output is strict RFC-8259 JSON 
(byte-identical to
+ * {@link JsonlSerializer}), keeping output maximally portable.  The {@link 
Builder#json5Sugar()
+ * json5Sugar()} opt-in switches the per-line output to JSON5 sugar 
(single-quoted strings, unquoted
+ * field names where safe).
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ *     <jc>// Strict-per-line output (default)</jc>
+ *     String <jv>json5l</jv> = 
Json5lSerializer.<jsf>DEFAULT</jsf>.serialize(<jv>myList</jv>);
+ *
+ *     <jc>// JSON5-sugar-per-line output</jc>
+ *     Json5lSerializer <jv>serializer</jv> = 
Json5lSerializer.<jsm>create</jsm>().json5Sugar().build();
+ *     String <jv>json5l</jv> = <jv>serializer</jv>.serialize(<jv>myList</jv>);
+ * </p>
+ *
+ * <h5 class='figure'>Example output, sugar off (List of beans):</h5>
+ * <p class='bjson'>
+ * {"name":"Alice","age":30}
+ * {"name":"Bob","age":25}
+ * </p>
+ *
+ * <h5 class='figure'>Example output, sugar on:</h5>
+ * <p class='bjson'>
+ * {name:'Alice',age:30}
+ * {name:'Bob',age:25}
+ * </p>
+ *
+ * <h5 class='section'>Notes:</h5><ul>
+ *     <li class='note'>This class is thread safe and reusable.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ *     <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/Json5lBasics";>JSON5L Basics</a>
+ * </ul>
+ */
+@SuppressWarnings({
+       "java:S110" // Inheritance depth acceptable
+})
+public class Json5lSerializer extends JsonlSerializer {
+
+       private static final String ARG_copyFrom = "copyFrom";
+
+       /**
+        * Builder class.
+        */
+       public static class Builder extends JsonSerializer.Builder<Builder> {
+
+               private static final Cache<HashKey,Json5lSerializer> CACHE = 
Cache.of(HashKey.class, Json5lSerializer.class).build();
+
+               boolean json5Sugar;
+
+               /**
+                * Constructor, default settings.
+                */
+               protected Builder() {
+                       produces("application/json5l")
+                               
.accept("application/json5l,text/json5l,application/jsonl;q=0.9,application/x-ndjson;q=0.9,text/jsonl;q=0.9")
+                               .type(Json5lSerializer.class)
+                               .useWhitespace(false);
+               }
+
+               /**
+                * Copy constructor.
+                *
+                * @param copyFrom The builder to copy from.
+                *      <br>Cannot be <jk>null</jk>.
+                */
+               protected Builder(Builder copyFrom) {
+                       super(assertArgNotNull(ARG_copyFrom, copyFrom));
+                       json5Sugar = copyFrom.json5Sugar;
+               }
+
+               /**
+                * Copy constructor.
+                *
+                * @param copyFrom The bean to copy from.
+                *      <br>Cannot be <jk>null</jk>.
+                */
+               protected Builder(Json5lSerializer copyFrom) {
+                       super(assertArgNotNull(ARG_copyFrom, copyFrom));
+                       json5Sugar = copyFrom.json5Sugar;
+               }
+
+               /**
+                * Emit JSON5 sugar (single-quoted strings, unquoted field 
names where safe) on each line
+                * instead of strict RFC-8259 JSON.
+                *
+                * <p>
+                * Off by default, in which case the per-line output is 
byte-identical to
+                * {@link JsonlSerializer}.
+                *
+                * @return This object.
+                */
+               public Builder json5Sugar() {
+                       json5Sugar = true;
+                       return this;
+               }
+
+               @Override /* Overridden from Context.Builder<?> */
+               public HashKey hashKey() {
+                       return HashKey.of(super.hashKey(), json5Sugar);
+               }
+
+               @Override /* Overridden from Context.Builder<?> */
+               public Json5lSerializer build() {
+                       return cache(CACHE).build(Json5lSerializer.class);
+               }
+
+               @Override /* Overridden from Context.Builder<?> */
+               public Builder copy() {
+                       return new Builder(this);
+               }
+       }
+
+       /** Default serializer, strict-JSON-per-line. */
+       public static final Json5lSerializer DEFAULT = new 
Json5lSerializer(create());
+
+       final boolean json5Sugar;
+
+       /**
+        * Creates a new builder for this object.
+        *
+        * @return A new builder.
+        */
+       public static Builder create() {
+               return new Builder();
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param builder The builder for this object.
+        */
+       public Json5lSerializer(Builder builder) {
+               super(builder.useWhitespace(false));
+               json5Sugar = builder.json5Sugar;
+       }
+
+       /**
+        * Returns <jk>true</jk> if this serializer emits JSON5 sugar on each 
line.
+        *
+        * @return <jk>true</jk> if JSON5 sugar is enabled.
+        */
+       public boolean isJson5Sugar() {
+               return json5Sugar;
+       }
+
+       @Override /* Overridden from Context */
+       public Builder copy() {
+               return new Builder(this);
+       }
+
+       @Override /* Overridden from Context */
+       public Json5lSerializerSession.Builder createSession() {
+               return Json5lSerializerSession.create(this);
+       }
+
+       @Override /* Overridden from Context */
+       public Json5lSerializerSession getSession() {
+               return createSession().build();
+       }
+}
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
new file mode 100644
index 0000000000..91fc7ee791
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lSerializerSession.java
@@ -0,0 +1,139 @@
+/*
+ * 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.json5l;
+
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
+
+import java.io.*;
+
+import org.apache.juneau.marshall.json.*;
+import org.apache.juneau.marshall.jsonl.*;
+import org.apache.juneau.marshall.serializer.*;
+import org.apache.juneau.marshall.stream.*;
+
+/**
+ * Session object that lives for the duration of a single use of {@link 
Json5lSerializer}.
+ *
+ * <p>
+ * Extends {@link JsonlSerializerSession} to inherit JSONL's line-delimited 
framing.  When the
+ * owning {@link Json5lSerializer} has {@link 
Json5lSerializer.Builder#json5Sugar() json5Sugar}
+ * enabled, the per-line output switches to JSON5 sugar (single-quoted 
strings, unquoted field names
+ * where safe) on both the databind and token-streaming paths; otherwise the 
inherited strict
+ * RFC-8259 output is used unchanged.
+ *
+ * <h5 class='section'>Notes:</h5><ul>
+ *     <li class='warn'>This class is not thread safe and is typically 
discarded after one use.
+ * </ul>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ *     <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/Json5lBasics";>JSON5L Basics</a>
+ * </ul>
+ */
+@SuppressWarnings({
+       "resource", // Resource management handled externally
+       "java:S110", // Inheritance depth acceptable
+       "java:S115" // Constants use UPPER_snakeCase convention
+})
+public class Json5lSerializerSession extends JsonlSerializerSession {
+
+       private static final String ARG_ctx = "ctx";
+
+       private static final char SUGAR_QUOTE = '\'';
+
+       /**
+        * Builder class.
+        */
+       public static class Builder extends JsonlSerializerSession.Builder {
+
+               final Json5lSerializer ctx;
+
+               /**
+                * Constructor
+                *
+                * @param ctx The context creating this session.
+                *      <br>Cannot be <jk>null</jk>.
+                */
+               protected Builder(Json5lSerializer ctx) {
+                       super(assertArgNotNull(ARG_ctx, ctx));
+                       this.ctx = ctx;
+               }
+
+               @Override
+               public Json5lSerializerSession build() {
+                       return new Json5lSerializerSession(this);
+               }
+       }
+
+       /**
+        * Creates a new builder for this object.
+        *
+        * @param ctx The context creating this session.
+        *      <br>Cannot be <jk>null</jk>.
+        * @return A new builder.
+        */
+       public static Builder create(Json5lSerializer ctx) {
+               return new Builder(assertArgNotNull(ARG_ctx, ctx));
+       }
+
+       private final boolean json5Sugar;
+
+       /**
+        * Constructor.
+        *
+        * @param builder The builder for this object.
+        */
+       protected Json5lSerializerSession(Builder builder) {
+               super(builder);
+               json5Sugar = builder.ctx.isJson5Sugar();
+       }
+
+       @Override /* Overridden from JsonSerializerSession */
+       protected JsonWriter getJsonWriter(SerializerPipe out) {
+               if (! json5Sugar)
+                       return super.getJsonWriter(out);
+               var output = out.getRawOutput();
+               if (output instanceof JsonWriter output2)
+                       return output2;
+               var w = JsonWriter.create(out.getWriter(), isUseWhitespace(), 
getMaxIndent(), isEscapeSolidus(), SUGAR_QUOTE, true, isTrimStrings(), 
getUriResolver());
+               out.setWriter(w);
+               return w;
+       }
+
+       @Override /* Overridden from JsonlSerializerSession */
+       public TokenWriter serializeTokens(Object output) throws IOException {
+               if (! json5Sugar)
+                       return super.serializeTokens(output);
+               var walk = new PojoWalker.Options(
+                       isKeepNullProperties(),
+                       isTrimEmptyMaps(),
+                       isTrimEmptyCollections(),
+                       isSortMaps(),
+                       isSortCollections(),
+                       isTrimStrings(),
+                       getMarshallingContext());
+               var settings = new JsonTokenWriter.Settings(
+                       false /* useWhitespace — JSON5L is one-record-per-line, 
no pretty-print */,
+                       getMaxIndent(),
+                       SUGAR_QUOTE,
+                       isEscapeSolidus(),
+                       isTrimStrings(),
+                       true /* simpleAttrs — JSON5 sugar: unquoted field names 
where safe */,
+                       walk,
+                       false /* disableObject */);
+               return JsonlTokenWriter.forOutput(output, settings);
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lTokenReader.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lTokenReader.java
new file mode 100644
index 0000000000..768402ae45
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/Json5lTokenReader.java
@@ -0,0 +1,78 @@
+/*
+ * 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.json5l;
+
+import java.io.*;
+
+import org.apache.juneau.marshall.json5.*;
+import org.apache.juneau.marshall.parser.*;
+import org.apache.juneau.marshall.stream.*;
+
+/**
+ * Reference implementation of the public {@link TokenReader} surface for the 
JSON5L format.
+ *
+ * <p>
+ * Subclasses {@link Json5TokenReader} to inherit the JSON5 dialect 
relaxations (single-quoted and
+ * bare-identifier strings/field names, trailing commas, missing values, 
comments) and adds JSONL's
+ * flat top-level sequencing: each line's value is emitted in turn at {@link 
#getDepth() depth} 0
+ * and {@link #next()} returns {@link TokenType#END_OF_STREAM} only when the 
input is exhausted.
+ *
+ * <h5 class='section'>Notes:</h5><ul>
+ *     <li class='warn'>This class is not thread safe.
+ *     <li>The cursor is a true O(1)-memory streaming cursor ({@link 
#isStreaming()} == <jk>true</jk>).
+ * </ul>
+ */
+@SuppressWarnings({
+       "resource" // The cursor's underlying ParserPipe is owned by the caller 
via try-with-resources on the cursor itself; Eclipse JDT flags the inner pipe 
as unclosed but that's by design.
+})
+public class Json5lTokenReader extends Json5TokenReader {
+
+       /**
+        * Constructor with default settings.
+        *
+        * @param pipe The parser input pipe to read from.  Must not be 
<jk>null</jk>.
+        * @throws IOException If a problem occurred opening the underlying 
reader.
+        */
+       public Json5lTokenReader(ParserPipe pipe) throws IOException {
+               super(pipe);
+       }
+
+       /**
+        * Constructor used by {@link Json5lParserSession#parseTokens(Object)} 
to plumb the calling
+        * session through so that {@link #read(Class)} can delegate to the 
JSON5L databind path.
+        *
+        * @param pipe The parser input pipe.  Must not be <jk>null</jk>.
+        * @param settings The cursor-level settings.  Must not be 
<jk>null</jk>.
+        * @param session The {@link Json5lParserSession} for {@link 
#read(Class)} delegation, or
+        *      <jk>null</jk> to disable {@code read}.
+        * @throws IOException If a problem occurred opening the underlying 
reader.
+        */
+       public Json5lTokenReader(ParserPipe pipe, Settings settings, 
Json5lParserSession session) throws IOException {
+               super(pipe, settings, session);
+       }
+
+       @Override /* JsonTokenReader */
+       protected void afterValue() {
+               // At depth 0, a top-level value just completed and we want the 
next next() / read() to
+               // consume the next line's value (instead of transitioning to 
S05_end as plain JSON would).
+               // Inside a container the parent's logic is still correct.
+               if (depth == 0)
+                       state = S00_expectValue;
+               else
+                       super.afterValue();
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/package-info.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/package-info.java
new file mode 100644
index 0000000000..00eb936686
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/json5l/package-info.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+/**
+ * JSON5L (JSON5 Lines) marshalling support.
+ *
+ * <p>
+ * JSON5L combines the relaxed JSON5 dialect (comments, unquoted/single-quoted 
keys, trailing
+ * commas, relaxed numbers) with JSONL's newline-delimited framing (one 
document per line).
+ *
+ * <p>
+ * This package provides {@link 
org.apache.juneau.marshall.json5l.Json5lSerializer} and
+ * {@link org.apache.juneau.marshall.json5l.Json5lParser}.  The serializer 
emits strict
+ * RFC-8259 JSON per line by default (byte-identical to {@link 
org.apache.juneau.marshall.jsonl.JsonlSerializer}),
+ * with an opt-in for JSON5 sugar.  The parser accepts the full JSON5 dialect 
per line — and,
+ * because JSON5 is a strict superset of JSON, plain JSONL input as well.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bjava'>
+ *     <jc>// Serialize a list of beans to JSON5L</jc>
+ *     String <jv>json5l</jv> = Json5l.<jsm>of</jsm>(<jv>myList</jv>);
+ *
+ *     <jc>// Parse JSON5L back to a list</jc>
+ *     List&lt;MyBean&gt; <jv>list</jv> = 
Json5l.<jsm>to</jsm>(<jv>json5l</jv>, List.<jk>class</jk>, 
MyBean.<jk>class</jk>);
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ *     <li class='link'><a class="doclink" href="https://jsonlines.org/";>JSON 
Lines Specification</a>
+ *     <li class='link'><a class="doclink" href="https://json5.org/";>JSON5 
Specification</a>
+ * </ul>
+ */
+package org.apache.juneau.marshall.json5l;
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/marshaller/Json5l.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/marshaller/Json5l.java
new file mode 100644
index 0000000000..f0a278cf5a
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/marshall/marshaller/Json5l.java
@@ -0,0 +1,229 @@
+/*
+ * 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.marshaller;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.nio.charset.*;
+
+import org.apache.juneau.marshall.*;
+import org.apache.juneau.marshall.json5l.*;
+import org.apache.juneau.marshall.parser.*;
+import org.apache.juneau.marshall.serializer.*;
+
+/**
+ * Pairs {@link Json5lSerializer} and {@link Json5lParser} into a single class 
with convenience
+ * read/write methods.
+ *
+ * <h5 class='figure'>Examples:</h5>
+ * <p class='bjava'>
+ *     <jc>// Serialize to JSON5L using instance</jc>
+ *     Json5l <jv>json5l</jv> = <jk>new</jk> Json5l();
+ *     String <jv>out</jv> = <jv>json5l</jv>.write(<jv>myList</jv>);
+ *     List&lt;MyBean&gt; <jv>in</jv> = <jv>json5l</jv>.read(<jv>out</jv>, 
List.<jk>class</jk>, MyBean.<jk>class</jk>);
+ *
+ *     <jc>// Serialize to JSON5L using DEFAULT instance</jc>
+ *     String <jv>out</jv> = Json5l.<jsm>of</jsm>(<jv>myList</jv>);
+ *     List&lt;MyBean&gt; <jv>in</jv> = Json5l.<jsm>to</jsm>(<jv>out</jv>, 
List.<jk>class</jk>, MyBean.<jk>class</jk>);
+ * </p>
+ *
+ * <h5 class='figure'>Example output (List of beans, strict-per-line 
default):</h5>
+ * <p class='bjson'>
+ * {"name":"Alice","age":30}
+ * {"name":"Bob","age":25}
+ * </p>
+ *
+ * <h5 class='section'>See Also:</h5><ul>
+ *     <li class='link'><a class="doclink" 
href="https://juneau.apache.org/docs/topics/Marshallers";>Marshallers</a>
+ * </ul>
+ */
+public class Json5l extends CharMarshaller {
+
+       /**
+        * Default reusable instance.
+        */
+       public static final Json5l DEFAULT = new Json5l();
+
+       /**
+        * Serializes a Java object to a JSON5L string.
+        *
+        * <p>
+        * A shortcut for calling 
<c><jsf>DEFAULT</jsf>.write(<jv>object</jv>)</c>.
+        *
+        * @param object The object to serialize.
+        * @return The serialized object.
+        * @throws SerializeException If a problem occurred trying to convert 
the output.
+        */
+       public static String of(Object object) throws SerializeException {
+               return DEFAULT.write(object);
+       }
+
+       /**
+        * Serializes a Java object to a JSON5L output.
+        *
+        * <p>
+        * A shortcut for calling <c><jsf>DEFAULT</jsf>.write(<jv>object</jv>, 
<jv>output</jv>)</c>.
+        *
+        * @param object The object to serialize.
+        * @param output
+        *      The output object.
+        *      <br>Can be any of the following types:
+        *      <ul>
+        *              <li>{@link Writer}
+        *              <li>{@link OutputStream} - Output will be written as 
UTF-8 encoded stream.
+        *              <li>{@link File} - Output will be written as 
system-default encoded stream.
+        *              <li>{@link StringBuilder} - Output will be written to 
the specified string builder.
+        *      </ul>
+        * @return The output object.
+        * @throws SerializeException If a problem occurred trying to convert 
the output.
+        * @throws IOException Thrown by underlying stream.
+        */
+       public static Object of(Object object, Object output) throws 
SerializeException, IOException {
+               DEFAULT.write(object, output);
+               return output;
+       }
+
+       /**
+        * Parses a JSON5L input object to the specified Java type.
+        *
+        * <p>
+        * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, 
<jv>type</jv>)</c>.
+        *
+        * @param <T> The class type of the object being created.
+        * @param input
+        *      The input.
+        *      <br>Can be any of the following types:
+        *      <ul>
+        *              <li><jk>null</jk>
+        *              <li>{@link Reader}
+        *              <li>{@link CharSequence}
+        *              <li>{@link InputStream} containing UTF-8 encoded text 
(or charset defined by
+        *                      {@link 
org.apache.juneau.marshall.parser.ReaderParser.Builder#streamCharset(Charset)} 
property value).
+        *              <li><code><jk>byte</jk>[]</code> containing UTF-8 
encoded text (or charset defined by
+        *                      {@link 
org.apache.juneau.marshall.parser.ReaderParser.Builder#streamCharset(Charset)} 
property value).
+        *              <li>{@link File} containing system encoded text (or 
charset defined by
+        *                      {@link 
org.apache.juneau.marshall.parser.ReaderParser.Builder#fileCharset(Charset)} 
property value).
+        *      </ul>
+        * @param type The object type to create.
+        * @return The parsed object.
+        * @throws ParseException Malformed input encountered.
+        * @throws IOException Thrown by underlying stream.
+        */
+       public static <T> T to(Object input, Class<T> type) throws 
ParseException, IOException {
+               return DEFAULT.read(input, type);
+       }
+
+       /**
+        * Parses a JSON5L input object to the specified Java type.
+        *
+        * <p>
+        * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, 
<jv>type</jv>, <jv>args</jv>)</c>.
+        *
+        * @param <T> The class type of the object to create.
+        * @param input
+        *      The input.
+        *      <br>Can be any of the following types:
+        *      <ul>
+        *              <li><jk>null</jk>
+        *              <li>{@link Reader}
+        *              <li>{@link CharSequence}
+        *              <li>{@link InputStream} containing UTF-8 encoded text 
(or charset defined by
+        *                      {@link 
org.apache.juneau.marshall.parser.ReaderParser.Builder#streamCharset(java.nio.charset.Charset)}
 property value).
+        *              <li><code><jk>byte</jk>[]</code> containing UTF-8 
encoded text (or charset defined by
+        *                      {@link 
org.apache.juneau.marshall.parser.ReaderParser.Builder#streamCharset(java.nio.charset.Charset)}
 property value).
+        *              <li>{@link File} containing system encoded text (or 
charset defined by
+        *                      {@link 
org.apache.juneau.marshall.parser.ReaderParser.Builder#fileCharset(java.nio.charset.Charset)}
 property value).
+        *      </ul>
+        * @param type
+        *      The object type to create.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        * @param args
+        *      The type arguments of the class if it's a collection or map.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        *      <br>Ignored if the main type is not a map or collection.
+        * @return The parsed object.
+        * @throws ParseException Malformed input encountered.
+        * @throws IOException Thrown by underlying stream.
+        * @see MarshallingSession#getClassMeta(Type,Type...) for argument 
syntax for maps and collections.
+        */
+       public static <T> T to(Object input, Type type, Type...args) throws 
ParseException, IOException {
+               return DEFAULT.read(input, type, args);
+       }
+
+       /**
+        * Parses a JSON5L input string to the specified type.
+        *
+        * <p>
+        * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, 
<jv>type</jv>)</c>.
+        *
+        * @param <T> The class type of the object being created.
+        * @param input The input.
+        * @param type The object type to create.
+        * @return The parsed object.
+        * @throws ParseException Malformed input encountered.
+        */
+       public static <T> T to(String input, Class<T> type) throws 
ParseException {
+               return DEFAULT.read(input, type);
+       }
+
+       /**
+        * Parses a JSON5L input string to the specified Java type.
+        *
+        * <p>
+        * A shortcut for calling <c><jsf>DEFAULT</jsf>.read(<jv>input</jv>, 
<jv>type</jv>, <jv>args</jv>)</c>.
+        *
+        * @param <T> The class type of the object to create.
+        * @param input The input.
+        * @param type
+        *      The object type to create.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        * @param args
+        *      The type arguments of the class if it's a collection or map.
+        *      <br>Can be any of the following: {@link ClassMeta}, {@link 
Class}, {@link ParameterizedType}, {@link GenericArrayType}
+        *      <br>Ignored if the main type is not a map or collection.
+        * @return The parsed object.
+        * @throws ParseException Malformed input encountered.
+        * @see MarshallingSession#getClassMeta(Type,Type...) for argument 
syntax for maps and collections.
+        */
+       public static <T> T to(String input, Type type, Type...args) throws 
ParseException {
+               return DEFAULT.read(input, type, args);
+       }
+
+       /**
+        * Constructor.
+        *
+        * <p>
+        * Uses {@link Json5lSerializer#DEFAULT} and {@link 
Json5lParser#DEFAULT}.
+        */
+       public Json5l() {
+               this(Json5lSerializer.DEFAULT, Json5lParser.DEFAULT);
+       }
+
+       /**
+        * Constructor.
+        *
+        * @param s
+        *      The serializer to use for serializing output.
+        *      <br>Must not be <jk>null</jk>.
+        * @param p
+        *      The parser to use for parsing input.
+        *      <br>Must not be <jk>null</jk>.
+        */
+       public Json5l(Json5lSerializer s, Json5lParser p) {
+               super(s, p);
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/ComboRoundTripTest_Base.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/ComboRoundTripTest_Base.java
index 4956344d33..ae837b791a 100644
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/ComboRoundTripTest_Base.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/ComboRoundTripTest_Base.java
@@ -194,6 +194,34 @@ public abstract class ComboRoundTripTest_Base extends 
TestBase {
                t.testParseVerify("jsonl");
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // JSON5L
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @ParameterizedTest
+       @MethodSource("testers")
+       public void a46_serializeJson5l(ComboRoundTrip_Tester<?> t) throws 
Exception {
+               t.testSerialize("json5l");
+       }
+
+       @ParameterizedTest
+       @MethodSource("testers")
+       public void a47_parseJson5l(ComboRoundTrip_Tester<?> t) throws 
Exception {
+               t.testParse("json5l");
+       }
+
+       @ParameterizedTest
+       @MethodSource("testers")
+       public void a48_parseJson5lJsonEquivalency(ComboRoundTrip_Tester<?> t) 
throws Exception {
+               t.testParseJsonEquivalency("json5l");
+       }
+
+       @ParameterizedTest
+       @MethodSource("testers")
+       public void a49_verifyJson5l(ComboRoundTrip_Tester<?> t) throws 
Exception {
+               t.testParseVerify("json5l");
+       }
+
        
//-----------------------------------------------------------------------------------------------------------------
        // XML
        
//-----------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java
index 25055235cf..2424219306 100644
--- 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java
@@ -32,6 +32,7 @@ import org.apache.juneau.marshall.html.*;
 import org.apache.juneau.marshall.ini.*;
 import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.marshall.json5.*;
+import org.apache.juneau.marshall.json5l.*;
 import org.apache.juneau.marshall.jsonl.*;
 import org.apache.juneau.marshall.markdown.*;
 import org.apache.juneau.marshall.msgpack.*;
@@ -113,6 +114,7 @@ public class ComboRoundTrip_Tester<T> {
                public Builder<T> json5T(String value) { expected.put("json5T", 
value); return this; }
                public Builder<T> json5R(String value) { expected.put("json5R", 
value); return this; }
                public Builder<T> jsonl(String value) { expected.put("jsonl", 
value); return this; }
+               public Builder<T> json5l(String value) { expected.put("json5l", 
value); return this; }
                public Builder<T> xml(String value) { expected.put("xml", 
value); return this; }
                public Builder<T> xmlT(String value) { expected.put("xmlT", 
value); return this; }
                public Builder<T> xmlR(String value) { expected.put("xmlR", 
value); return this; }
@@ -171,6 +173,7 @@ public class ComboRoundTrip_Tester<T> {
                serializers.put("json5T", create(b, 
Json5Serializer.create().typePropertyName("t").addBeanTypes().addRootType()));
                serializers.put("json5R", create(b, 
Json5Serializer.DEFAULT_READABLE.copy().addBeanTypes().addRootType()));
                serializers.put("jsonl", create(b, 
JsonlSerializer.create().keepNullProperties().addBeanTypes().addRootType()));
+               serializers.put("json5l", create(b, 
Json5lSerializer.create().keepNullProperties().addBeanTypes().addRootType()));
                serializers.put("xml", create(b, 
XmlSerializer.DEFAULT_SQ.copy().addBeanTypes().addRootType()));
                serializers.put("xmlT", create(b, 
XmlSerializer.create().sq().typePropertyName("t").addBeanTypes().addRootType()));
                serializers.put("xmlR", create(b, 
XmlSerializer.DEFAULT_SQ_READABLE.copy().addBeanTypes().addRootType()));
@@ -201,6 +204,7 @@ public class ComboRoundTrip_Tester<T> {
                parsers.put("json5T", create(b, 
Json5Parser.create().typePropertyName("t")));
                parsers.put("json5R", create(b, Json5Parser.DEFAULT.copy()));
                parsers.put("jsonl", create(b, JsonlParser.create()));
+               parsers.put("json5l", create(b, Json5lParser.create()));
                parsers.put("xml", create(b, XmlParser.DEFAULT.copy()));
                parsers.put("xmlT", create(b, 
XmlParser.create().typePropertyName("t")));
                parsers.put("xmlR", create(b, XmlParser.DEFAULT.copy()));
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lCoverage_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lCoverage_Test.java
new file mode 100644
index 0000000000..555056ed48
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lCoverage_Test.java
@@ -0,0 +1,199 @@
+/*
+ * 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.json5l;
+
+import static org.apache.juneau.junit.bct.BctAssertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.commons.bean.*;
+import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.marshall.parser.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Supplemental coverage tests for the {@code json5l} package — exercises edge 
branches not covered
+ * by the primary behavioral tests.
+ */
+@SuppressWarnings({
+       "unchecked", // Parser returns Object; casts in tests
+       "resource"   // Token readers/pipes are short-lived test fixtures.
+})
+class Json5lCoverage_Test extends TestBase {
+
+       @BeanType(properties = "name,age")
+       public static class Person {
+               public String name;
+               public int age;
+
+               public Person() {}
+               public Person(String name, int age) {
+                       this.name = name;
+                       this.age = age;
+               }
+       }
+
+       // 
=================================================================================
+       // A. Parser session — doParse array/null branches
+       // 
=================================================================================
+
+       @Test
+       void a01_parseToArrayType() throws Exception {
+               var in = "{name:'Alice',age:30}\n{name:'Bob',age:25}";
+               var arr = Json5lParser.DEFAULT.parse(in, Person[].class);
+               assertBean(arr, "0{name,age},1{name,age}", 
"{Alice,30},{Bob,25}");
+       }
+
+       @Test
+       void a02_singleTargetNoParseableLineReturnsNull() throws Exception {
+               // Only comment/blank lines → the single-value loop falls 
through to return null.
+               var p = Json5lParser.DEFAULT.parse("// just a comment\n\n", 
Person.class);
+               assertNull(p);
+       }
+
+       @Test
+       void a03_nullInputReturnsNull() throws Exception {
+               assertNull(Json5lParser.DEFAULT.parse((String) null, 
Person.class));
+       }
+
+       // 
=================================================================================
+       // B. isParseable — block-comment branch coverage
+       // 
=================================================================================
+
+       @Test
+       void b01_blockCommentWithTrailingContentIsParseable() throws Exception {
+               // `/* ... */ {a:1}` — closing */ is NOT at end of line, so the 
line IS parsed.
+               var in = "/* lead */ {name:'Alice',age:30}";
+               var list = (List<Person>) Json5lParser.DEFAULT.parse(in, 
List.class, Person.class);
+               assertBean(list, "0{name,age}", "{Alice,30}");
+       }
+
+       @Test
+       void b02_lineNotStartingWithCommentIsParseable() throws Exception {
+               var in = "{name:'Alice',age:30}";
+               var list = (List<Person>) Json5lParser.DEFAULT.parse(in, 
List.class, Person.class);
+               assertBean(list, "0{name}", "{Alice}");
+       }
+
+       // 
=================================================================================
+       // C. Token reader — single-arg constructor + read() delegation disabled
+       // 
=================================================================================
+
+       @Test
+       void c01_singleArgConstructor() throws Exception {
+               try (var pipe = new ParserPipe("{a:1}\n");
+                               var r = new Json5lTokenReader(pipe)) {
+                       assertNotNull(r.next());
+               }
+       }
+
+       // 
=================================================================================
+       // D. Serializer / builder — copy() paths
+       // 
=================================================================================
+
+       @Test
+       void d01_serializerBuilderCopy() {
+               var b = Json5lSerializer.create().json5Sugar();
+               var copy = b.copy();
+               assertTrue(copy.build().isJson5Sugar());
+       }
+
+       @Test
+       void d02_serializerContextCopy() {
+               var s = Json5lSerializer.create().json5Sugar().build();
+               assertTrue(s.copy().build().isJson5Sugar());
+       }
+
+       @Test
+       void d03_parserBuilderCopy() throws Exception {
+               var p = Json5lParser.create().copy().build();
+               var person = p.parse("{name:'Alice',age:30}", Person.class);
+               assertBean(person, "name,age", "Alice,30");
+       }
+
+       @Test
+       void d04_parserContextCopy() throws Exception {
+               var p = Json5lParser.DEFAULT.copy().build();
+               var person = p.parse("{name:'Bob',age:25}", Person.class);
+               assertBean(person, "name,age", "Bob,25");
+       }
+
+       // 
=================================================================================
+       // E. Serializer session — getJsonWriter when output is already a 
JsonWriter (sugar on)
+       // 
=================================================================================
+
+       @Test
+       void e01_sugarSerializeViaStringRoundTrip() throws Exception {
+               // Drives the getJsonWriter() sugar branch through 
serializeToString (which wraps a fresh writer).
+               var s = Json5lSerializer.create().json5Sugar().build();
+               var out = s.serialize(JsonMap.of("name", "Alice"));
+               assertEquals("{name:'Alice'}", out.trim());
+       }
+
+       @Test
+       void e02_strictSerializeViaStringUsesDoubleQuotes() throws Exception {
+               // Drives the getJsonWriter() strict (non-sugar) branch.
+               var out = Json5lSerializer.DEFAULT.serialize(JsonMap.of("name", 
"Alice"));
+               assertEquals("{\"name\":\"Alice\"}", out.trim());
+       }
+
+       // 
=================================================================================
+       // F. @Json5lConfig annotation appliers (no-op, but must be exercised)
+       // 
=================================================================================
+
+       @Json5lConfig(rank = 1)
+       public static class F_Configured {}
+
+       @Test
+       void f01_serializerApply() {
+               var s = 
Json5lSerializer.create().applyAnnotations(F_Configured.class).build();
+               assertNotNull(s);
+       }
+
+       @Test
+       void f02_parserApply() {
+               var p = 
Json5lParser.create().applyAnnotations(F_Configured.class).build();
+               assertNotNull(p);
+       }
+
+       // 
=================================================================================
+       // G. isParseable — remaining branch coverage
+       // 
=================================================================================
+
+       @Test
+       void g01_blankLinesOnlyBetweenRecords() throws Exception {
+               // Blank line in the array loop hits the isEmpty()==true branch.
+               var list = (List<Person>) 
Json5lParser.DEFAULT.parse("{name:'A'}\n   \n{name:'B'}", List.class, 
Person.class);
+               assertBean(list, "0{name},1{name}", "{A},{B}");
+       }
+
+       @Test
+       void g02_lineCommentInArrayLoopSkipped() throws Exception {
+               // `//`-prefixed line in the array loop hits the 
startsWith("//")==true branch.
+               var list = (List<Person>) 
Json5lParser.DEFAULT.parse("{name:'A'}\n// skip\n{name:'B'}", List.class, 
Person.class);
+               assertBean(list, "0{name},1{name}", "{A},{B}");
+       }
+
+       @Test
+       void g03_blockCommentWithTextBeforeCloseIsParseable() throws Exception {
+               // `/* */ x` style: indexOf("*/") != end so the line is parsed 
(the &&-chain's last branch).
+               var list = (List<Person>) Json5lParser.DEFAULT.parse("/* a 
*/{name:'A'}", List.class, Person.class);
+               assertBean(list, "0{name}", "{A}");
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lMediaType_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lMediaType_Test.java
new file mode 100644
index 0000000000..3cc8a680b5
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lMediaType_Test.java
@@ -0,0 +1,73 @@
+/*
+ * 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.json5l;
+
+import static org.apache.juneau.commons.utils.CollectionUtils.*;
+import static org.apache.juneau.junit.bct.BctAssertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Tests for JSON5L media type configuration.
+ */
+@SuppressWarnings({
+       "unchecked" // Parser returns Object; cast to List<JsonMap> in tests
+})
+class Json5lMediaType_Test {
+
+       @Test
+       void a01_producesCorrectMediaType() {
+               var ct = Json5lSerializer.DEFAULT.getResponseContentType();
+               assertEquals("application", ct.getType());
+               assertEquals("json5l", ct.getSubType());
+       }
+
+       @Test
+       void a02_acceptsAllMediaTypes() {
+               var types = new ArrayList<String>();
+               Json5lSerializer.DEFAULT.forEachAcceptMediaType(mt -> 
types.add(mt.getType() + "/" + mt.getSubType()));
+               assertTrue(types.stream().anyMatch(t -> t.contains("json5l")), 
"Expected application/json5l: " + types);
+               assertTrue(types.stream().anyMatch(t -> 
"text/json5l".equals(t)), "Expected text/json5l: " + types);
+               // Cross-acceptance of the JSONL family (reduced q-value).
+               assertTrue(types.stream().anyMatch(t -> t.contains("jsonl")), 
"Expected jsonl cross-accept: " + types);
+               assertTrue(types.stream().anyMatch(t -> t.contains("ndjson")), 
"Expected ndjson cross-accept: " + types);
+       }
+
+       @Test
+       void a03_consumesAllMediaTypes() {
+               var types = Json5lParser.DEFAULT.getMediaTypes().stream()
+                       .map(mt -> mt.getType() + "/" + mt.getSubType())
+                       .toList();
+               assertTrue(types.stream().anyMatch(t -> t.contains("json5l")), 
"Expected json5l types: " + types);
+               assertTrue(types.stream().anyMatch(t -> t.contains("jsonl")), 
"Expected jsonl cross-accept: " + types);
+               assertTrue(types.stream().anyMatch(t -> t.contains("ndjson")), 
"Expected ndjson cross-accept: " + types);
+       }
+
+       @Test
+       void a04_contentNegotiation() throws Exception {
+               var a = list(JsonMap.of("k", "v"));
+               var json5l = Json5l.of(a);
+               assertNotNull(json5l);
+               var b = (List<JsonMap>) Json5l.to(json5l, List.class, 
JsonMap.class);
+               assertBean(b, "0{k}", "{v}");
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lParser_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lParser_Test.java
new file mode 100644
index 0000000000..8f5e62d783
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lParser_Test.java
@@ -0,0 +1,155 @@
+/*
+ * 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.json5l;
+
+import static org.apache.juneau.commons.utils.CollectionUtils.*;
+import static org.apache.juneau.junit.bct.BctAssertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.commons.bean.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Tests for {@link Json5lParser}.
+ */
+@SuppressWarnings({
+       "unchecked" // Parser returns Object; cast to 
List<Person>/List<JsonMap>/List<String> in tests
+})
+class Json5lParser_Test extends TestBase {
+
+       @BeanType(properties = "name,age")
+       public static class Person {
+               public String name;
+               public int age;
+
+               public Person() {}
+               public Person(String name, int age) {
+                       this.name = name;
+                       this.age = age;
+               }
+       }
+
+       // 
=================================================================================
+       // A. Strict JSONL input (JSON5 is a superset of JSON)
+       // 
=================================================================================
+
+       @Test
+       void a01_parseStrictJsonlToListOfBeans() throws Exception {
+               var in = 
"{\"name\":\"Alice\",\"age\":30}\n{\"name\":\"Bob\",\"age\":25}";
+               var list = (List<Person>) Json5l.to(in, List.class, 
Person.class);
+               assertBean(list, "0{name,age},1{name,age}", 
"{Alice,30},{Bob,25}");
+       }
+
+       @Test
+       void a02_parseStrictSingleLine() throws Exception {
+               var p = Json5l.to("{\"name\":\"Alice\",\"age\":30}", 
Person.class);
+               assertBean(p, "name,age", "Alice,30");
+       }
+
+       // 
=================================================================================
+       // B. JSON5 dialect, one document per line
+       // 
=================================================================================
+
+       @Test
+       void b01_unquotedKeysAndSingleQuotes() throws Exception {
+               var in = "{name:'Alice',age:30}\n{name:'Bob',age:25}";
+               var list = (List<Person>) Json5l.to(in, List.class, 
Person.class);
+               assertBean(list, "0{name,age},1{name,age}", 
"{Alice,30},{Bob,25}");
+       }
+
+       @Test
+       void b02_trailingCommas() throws Exception {
+               var in = "{name:'Alice',age:30,}\n{name:'Bob',age:25,}";
+               var list = (List<Person>) Json5l.to(in, List.class, 
Person.class);
+               assertBean(list, "0{name,age},1{name,age}", 
"{Alice,30},{Bob,25}");
+       }
+
+       @Test
+       void b03_mixedStrictAndSugarLines() throws Exception {
+               var in = "{name:'Alice',age:30}\n{\"name\":\"Bob\",\"age\":25}";
+               var list = (List<Person>) Json5l.to(in, List.class, 
Person.class);
+               assertBean(list, "0{name,age},1{name,age}", 
"{Alice,30},{Bob,25}");
+       }
+
+       // 
=================================================================================
+       // C. Comment handling
+       // 
=================================================================================
+
+       @Test
+       void c01_commentOnlyLineSkipped() throws Exception {
+               var in = "// header 
comment\n{name:'Alice',age:30}\n{name:'Bob',age:25}";
+               var list = (List<Person>) Json5l.to(in, List.class, 
Person.class);
+               assertBean(list, "0{name},1{name}", "{Alice},{Bob}");
+       }
+
+       @Test
+       void c02_blockCommentOnlyLineSkipped() throws Exception {
+               var in = "/* block comment */\n{name:'Alice',age:30}";
+               var list = (List<Person>) Json5l.to(in, List.class, 
Person.class);
+               assertBean(list, "0{name}", "{Alice}");
+       }
+
+       @Test
+       void c03_inlineTrailingLineComment() throws Exception {
+               var in = "{name:'Alice',age:30} // 
trailing\n{name:'Bob',age:25}";
+               var list = (List<Person>) Json5l.to(in, List.class, 
Person.class);
+               assertBean(list, "0{name},1{name}", "{Alice},{Bob}");
+       }
+
+       @Test
+       void c04_blankAndCommentLinesInterspersed() throws Exception {
+               var in = "\n// one\n{name:'Alice'}\n\n/* two 
*/\n{name:'Bob'}\n";
+               var list = (List<Person>) Json5l.to(in, List.class, 
Person.class);
+               assertBean(list, "0{name},1{name}", "{Alice},{Bob}");
+       }
+
+       @Test
+       void c05_commentOnlySingleObjectTarget() throws Exception {
+               var in = "// nothing but a comment 
first\n{name:'Alice',age:30}";
+               var p = Json5l.to(in, Person.class);
+               assertBean(p, "name,age", "Alice,30");
+       }
+
+       // 
=================================================================================
+       // D. Empty / blank inputs
+       // 
=================================================================================
+
+       @Test
+       void d01_parseEmptyInput() throws Exception {
+               var list = (List<?>) Json5l.to("", List.class, Person.class);
+               assertNotNull(list);
+               assertTrue(list.isEmpty());
+       }
+
+       @Test
+       void d02_parseCommentOnlyInputToList() throws Exception {
+               var list = (List<?>) Json5l.to("// only a comment\n\n", 
List.class, Person.class);
+               assertNotNull(list);
+               assertTrue(list.isEmpty());
+       }
+
+       @Test
+       void d03_parseToListOfStrings() throws Exception {
+               var in = "'foo'\n'bar'\n'baz'";
+               var list = (List<String>) Json5l.to(in, List.class, 
String.class);
+               assertEquals(list("foo", "bar", "baz"), list);
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lRoundTrip_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lRoundTrip_Test.java
new file mode 100644
index 0000000000..586eec3c60
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lRoundTrip_Test.java
@@ -0,0 +1,78 @@
+/*
+ * 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.json5l;
+
+import static org.apache.juneau.commons.utils.CollectionUtils.*;
+import static org.apache.juneau.junit.bct.BctAssertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.commons.bean.*;
+import org.apache.juneau.marshall.collections.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Round-trip tests for {@link Json5lSerializer} / {@link Json5lParser}.
+ */
+@SuppressWarnings({
+       "unchecked" // Parser returns Object; cast to 
List<Person>/List<JsonMap> in tests
+})
+class Json5lRoundTrip_Test extends TestBase {
+
+       @BeanType(properties = "name,age")
+       public static class Person {
+               public String name;
+               public int age;
+
+               public Person() {}
+               public Person(String name, int age) {
+                       this.name = name;
+                       this.age = age;
+               }
+       }
+
+       @Test
+       void a01_strictRoundTrip() throws Exception {
+               var in = list(new Person("Alice", 30), new Person("Bob", 25));
+               var out = Json5lSerializer.DEFAULT.serialize(in);
+               var back = (List<Person>) Json5lParser.DEFAULT.parse(out, 
List.class, Person.class);
+               assertBean(back, "0{name,age},1{name,age}", 
"{Alice,30},{Bob,25}");
+       }
+
+       @Test
+       void a02_sugarRoundTrip() throws Exception {
+               var s = Json5lSerializer.create().json5Sugar().build();
+               var in = list(new Person("Alice", 30), new Person("Bob", 25));
+               var out = s.serialize(in);
+               // Sugar output is single-quoted / unquoted-key; the parser 
reads it back fine.
+               assertTrue(out.contains("name:'Alice'"));
+               var back = (List<Person>) Json5lParser.DEFAULT.parse(out, 
List.class, Person.class);
+               assertBean(back, "0{name,age},1{name,age}", 
"{Alice,30},{Bob,25}");
+       }
+
+       @Test
+       void a03_mapRoundTrip() throws Exception {
+               var in = list(JsonMap.of("x", 1), JsonMap.of("y", 2));
+               var out = Json5lSerializer.DEFAULT.serialize(in);
+               var back = (List<JsonMap>) Json5lParser.DEFAULT.parse(out, 
List.class, JsonMap.class);
+               assertEquals(2, back.size());
+               assertEquals(1, back.get(0).getInt("x"));
+               assertEquals(2, back.get(1).getInt("y"));
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lTokenStream_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lTokenStream_Test.java
new file mode 100644
index 0000000000..db46bfc3d7
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5lTokenStream_Test.java
@@ -0,0 +1,136 @@
+/*
+ * 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.json5l;
+
+import static org.apache.juneau.junit.bct.BctAssertions.*;
+import static org.apache.juneau.marshall.stream.TokenStreamAssertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.marshall.stream.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Unit tests for the public JSON5L token-streaming surface.
+ */
+@SuppressWarnings({
+       "resource" // Token readers are closed via try-with-resources; JDT's 
flow analysis over chained factory calls yields false-positive leak reports.
+})
+class Json5lTokenStream_Test extends TestBase {
+
+       // 
=================================================================================
+       // A. Reader — JSON5L flat-sequence semantics with JSON5 dialect
+       // 
=================================================================================
+
+       @Nested class A_reader extends TestBase {
+
+               @Test void a01_singleLine() throws Exception {
+                       try (var r = 
Json5lParser.DEFAULT.parseTokens("{a:1}\n")) {
+                               assertSequence(r,
+                                       TokenType.START_OBJECT,
+                                       TokenType.FIELD_NAME,
+                                       TokenType.VALUE_NUMBER,
+                                       TokenType.END_OBJECT,
+                                       TokenType.END_OF_STREAM);
+                       }
+               }
+
+               @Test void a02_multipleLinesFlat() throws Exception {
+                       try (var r = 
Json5lParser.DEFAULT.parseTokens("{a:1}\n{b:2}\n")) {
+                               assertSequence(r,
+                                       TokenType.START_OBJECT, 
TokenType.FIELD_NAME, TokenType.VALUE_NUMBER, TokenType.END_OBJECT,
+                                       TokenType.START_OBJECT, 
TokenType.FIELD_NAME, TokenType.VALUE_NUMBER, TokenType.END_OBJECT,
+                                       TokenType.END_OF_STREAM);
+                       }
+               }
+
+               @Test void a03_capability() throws Exception {
+                       assertInstanceOf(TokenReadable.class, 
Json5lParser.DEFAULT);
+                       try (var r = Json5lParser.DEFAULT.parseTokens("1\n")) {
+                               assertReaderStreaming(r);
+                       }
+               }
+       }
+
+       // 
=================================================================================
+       // B. Writer — strict default and json5Sugar output, newline per 
top-level value
+       // 
=================================================================================
+
+       @Nested class B_writer extends TestBase {
+
+               @Test void b01_strictDefault() throws Exception {
+                       var sb = new StringBuilder();
+                       try (var w = 
Json5lSerializer.DEFAULT.serializeTokens(sb)) {
+                               w.startObject(); w.fieldName("a"); w.number(1); 
w.endObject();
+                               w.startObject(); w.fieldName("b"); w.number(2); 
w.endObject();
+                       }
+                       assertEquals("{\"a\":1}\n{\"b\":2}\n", sb.toString());
+               }
+
+               @Test void b02_sugarUnquotedKeysSingleQuotes() throws Exception 
{
+                       var s = Json5lSerializer.create().json5Sugar().build();
+                       var sb = new StringBuilder();
+                       try (var w = s.serializeTokens(sb)) {
+                               w.startObject(); w.fieldName("a"); 
w.string("x"); w.endObject();
+                               w.startObject(); w.fieldName("b"); 
w.string("y"); w.endObject();
+                       }
+                       assertEquals("{a:'x'}\n{b:'y'}\n", sb.toString());
+               }
+
+               @Test void b03_capability() throws Exception {
+                       assertInstanceOf(TokenWritable.class, 
Json5lSerializer.DEFAULT);
+                       var sb = new StringBuilder();
+                       try (var w = 
Json5lSerializer.DEFAULT.serializeTokens(sb)) {
+                               assertWriterStreaming(w);
+                       }
+               }
+       }
+
+       // 
=================================================================================
+       // C. Array-record stream — JSON5L aliases its line record stream (no 
surrounding [...])
+       // 
=================================================================================
+
+       @Nested class C_arrayRecords extends TestBase {
+
+               @Test void c01_capability() {
+                       assertInstanceOf(ArrayRecordReadable.class, 
Json5lParser.DEFAULT);
+                       assertInstanceOf(ArrayRecordWritable.class, 
Json5lSerializer.DEFAULT);
+                       assertTrue(((ArrayRecordReadable) 
Json5lParser.DEFAULT).isArrayRecordStreaming());
+                       assertTrue(((ArrayRecordWritable) 
Json5lSerializer.DEFAULT).isArrayRecordStreaming());
+               }
+
+               @Test void c02_roundTrip() throws Exception {
+                       var sb = new StringBuilder();
+                       try (RecordWriter w = 
Json5lSerializer.DEFAULT.serializeArrayRecords(sb)) {
+                               assertTrue(w.isStreaming());
+                               w.write(java.util.Map.of("x", 1));
+                               w.write(java.util.Map.of("x", 2));
+                       }
+                       assertString("{\"x\":1}\n{\"x\":2}\n", sb);
+
+                       var records = new java.util.ArrayList<java.util.Map<?, 
?>>();
+                       try (RecordReader r = 
Json5lParser.DEFAULT.parseArrayRecords(sb.toString())) {
+                               assertTrue(r.isStreaming());
+                               while (r.canRead())
+                                       
records.add(r.read(java.util.Map.class));
+                       }
+                       assertEquals(2, records.size());
+                       assertBean(records.get(0), "x", "1");
+                       assertBean(records.get(1), "x", "2");
+               }
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5l_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5l_Test.java
new file mode 100644
index 0000000000..188a390c84
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshall/json5l/Json5l_Test.java
@@ -0,0 +1,182 @@
+/*
+ * 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.json5l;
+
+import static org.apache.juneau.commons.utils.CollectionUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.commons.bean.*;
+import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Tests for {@link Json5lSerializer}.
+ */
+class Json5l_Test extends TestBase {
+
+       @BeanType(properties = "name,age")
+       public static class Person {
+               public String name;
+               public int age;
+
+               public Person() {}
+               public Person(String name, int age) {
+                       this.name = name;
+                       this.age = age;
+               }
+       }
+
+       // 
=================================================================================
+       // A. Serialization — strict-per-line default (mirrors JSONL semantics)
+       // 
=================================================================================
+
+       @Test
+       void a01_serializeCollectionOfBeans() throws Exception {
+               var list = list(
+                       new Person("Alice", 30),
+                       new Person("Bob", 25),
+                       new Person("Carol", 35)
+               );
+               var json5l = Json5l.of(list);
+               var lines = json5l.split("\n");
+               assertEquals(3, lines.length);
+               assertTrue(lines[0].contains("\"name\":\"Alice\""));
+               assertTrue(lines[0].contains("\"age\":30"));
+               assertTrue(lines[1].contains("\"name\":\"Bob\""));
+               assertTrue(lines[2].contains("\"name\":\"Carol\""));
+               assertFalse(json5l.contains("["));
+               assertFalse(json5l.contains("]"));
+       }
+
+       @Test
+       void a02_serializeArray() throws Exception {
+               var arr = new Person[]{new Person("Alice", 30), new 
Person("Bob", 25)};
+               var json5l = Json5l.of(arr);
+               var lines = json5l.split("\n");
+               assertEquals(2, lines.length);
+               assertTrue(lines[0].contains("\"Alice\""));
+               assertTrue(lines[1].contains("\"Bob\""));
+       }
+
+       @Test
+       void a03_serializeSingleBean() throws Exception {
+               var p = new Person("Alice", 30);
+               var json5l = Json5l.of(p);
+               assertEquals(1, json5l.split("\n").length);
+               assertTrue(json5l.contains("\"name\":\"Alice\""));
+               assertTrue(json5l.contains("\"age\":30"));
+       }
+
+       @Test
+       void a04_serializeCollectionOfStrings() throws Exception {
+               var list = list("foo", "bar", "baz");
+               var json5l = Json5l.of(list);
+               var lines = json5l.split("\n");
+               assertEquals(3, lines.length);
+               assertTrue(lines[0].contains("\"foo\""));
+               assertTrue(lines[1].contains("\"bar\""));
+               assertTrue(lines[2].contains("\"baz\""));
+       }
+
+       @Test
+       void a05_serializeEmptyCollection() throws Exception {
+               var json5l = Json5l.of(list());
+               assertEquals("", json5l.trim());
+       }
+
+       @Test
+       void a06_serializeNullValues() throws Exception {
+               var list = list("a", null, "c");
+               var json5l = Json5l.of(list);
+               var lines = json5l.split("\n");
+               assertEquals(3, lines.length);
+               assertEquals("null", lines[1]);
+       }
+
+       @Test
+       void a07_serializeNestedObjects() throws Exception {
+               var outer = JsonMap.of("name", "Alice", "inner", 
JsonMap.of("x", 1, "y", 2));
+               var json5l = Json5l.of(outer);
+               assertTrue(json5l.contains("\"inner\":{\"x\":1,\"y\":2}"));
+       }
+
+       // 
=================================================================================
+       // B. Default output is byte-identical to JSONL (sugar off)
+       // 
=================================================================================
+
+       @Test
+       void b01_defaultIsByteIdenticalToJsonl() throws Exception {
+               var list = list(
+                       new Person("Alice", 30),
+                       new Person("Bob", 25)
+               );
+               assertEquals(Jsonl.of(list), Json5l.of(list));
+       }
+
+       @Test
+       void b02_defaultProducesStrictDoubleQuotes() throws Exception {
+               var json5l = Json5l.of(JsonMap.of("name", "Alice"));
+               assertTrue(json5l.contains("\"name\":\"Alice\""));
+               assertFalse(json5l.contains("'"));
+       }
+
+       // 
=================================================================================
+       // C. json5Sugar opt-in
+       // 
=================================================================================
+
+       @Test
+       void c01_sugarUsesSingleQuotesAndUnquotedKeys() throws Exception {
+               var s = Json5lSerializer.create().json5Sugar().build();
+               var out = s.serialize(JsonMap.of("name", "Alice", "age", 30));
+               assertTrue(out.contains("name:'Alice'"), "Expected unquoted key 
+ single quotes: " + out);
+               assertTrue(out.contains("age:30"), "Expected unquoted key: " + 
out);
+               assertFalse(out.contains("\""), "Expected no double quotes: " + 
out);
+       }
+
+       @Test
+       void c02_sugarStillOneLinePerRecord() throws Exception {
+               var s = Json5lSerializer.create().json5Sugar().build();
+               var out = s.serialize(list(new Person("Alice", 30), new 
Person("Bob", 25)));
+               var lines = out.split("\n");
+               assertEquals(2, lines.length);
+               assertTrue(lines[0].contains("name:'Alice'"));
+               assertTrue(lines[1].contains("name:'Bob'"));
+       }
+
+       @Test
+       void c03_sugarFlagIsReflectedOnContext() {
+               assertFalse(Json5lSerializer.DEFAULT.isJson5Sugar());
+               
assertTrue(Json5lSerializer.create().json5Sugar().build().isJson5Sugar());
+       }
+
+       @Test
+       void c04_sugarAndStrictAreDistinctCachedInstances() {
+               var strict = Json5lSerializer.create().build();
+               var sugar = Json5lSerializer.create().json5Sugar().build();
+               assertNotSame(strict, sugar);
+               assertFalse(strict.isJson5Sugar());
+               assertTrue(sugar.isJson5Sugar());
+       }
+
+       @Test
+       void c05_copyPreservesSugar() {
+               var sugar = Json5lSerializer.create().json5Sugar().build();
+               assertTrue(sugar.copy().build().isJson5Sugar());
+       }
+}
diff --git 
a/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshaller/Json5l_Test.java
 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshaller/Json5l_Test.java
new file mode 100644
index 0000000000..7f70c677ad
--- /dev/null
+++ 
b/juneau-core/juneau-marshall/src/test/java/org/apache/juneau/marshaller/Json5l_Test.java
@@ -0,0 +1,99 @@
+/*
+ * 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.marshaller;
+
+import static org.apache.juneau.commons.utils.CollectionUtils.*;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.junit.jupiter.api.*;
+
+@SuppressWarnings({
+       "unchecked", // Parser returns Object; cast to Map/List<JsonMap> in 
tests
+       "resource"   // Stream/reader instances are intentional short-lived 
test fixtures; auto-close not required for these assertions.
+})
+class Json5l_Test extends TestBase {
+
+       @Test void a01_of() throws Exception {
+               var a = "foo";
+               var b = JsonMap.of("foo", "bar");
+               var c = list(JsonMap.of("a", 1), JsonMap.of("b", 2));
+
+               assertEquals("\"foo\"", Json5l.of(a).trim());
+               assertEquals("\"foo\"", Json5l.of(a, 
stringWriter()).toString().trim());
+               assertTrue(Json5l.of(b).contains("\"foo\":\"bar\""));
+               var sw = new StringWriter();
+               Json5l.of(b, sw);
+               assertTrue(sw.toString().contains("\"foo\":\"bar\""));
+               var json5l = Json5l.of(c);
+               assertTrue(json5l.contains("\"a\":1"));
+               assertTrue(json5l.contains("\"b\":2"));
+               assertEquals(2, json5l.split("\n").length);
+       }
+
+       @Test void a02_to() throws Exception {
+               var a = "'foo'";
+               var b = "{foo:'bar'}";
+               var c = "{a:1}\n{b:2}";
+
+               assertEquals("foo", Json5l.to(a, String.class));
+               assertEquals("foo", Json5l.to(stringReader(a), String.class));
+               var m = (Map<String,String>) Json5l.to(b, Map.class, 
String.class, String.class);
+               assertEquals("bar", m.get("foo"));
+               m = (Map<String,String>) Json5l.to(stringReader(b), Map.class, 
String.class, String.class);
+               assertEquals("bar", m.get("foo"));
+               var list = (List<JsonMap>) Json5l.to(c, List.class, 
JsonMap.class);
+               assertEquals(2, list.size());
+               assertEquals(1, list.get(0).getInt("a"));
+               assertEquals(2, list.get(1).getInt("b"));
+       }
+
+       @Test void a03_roundTrip() throws Exception {
+               var a = list(JsonMap.of("x", 1), JsonMap.of("y", 2));
+               var json5l = Json5l.of(a);
+               var b = (List<JsonMap>) Json5l.to(json5l, List.class, 
JsonMap.class);
+               assertEquals(2, b.size());
+               assertEquals(1, b.get(0).getInt("x"));
+               assertEquals(2, b.get(1).getInt("y"));
+       }
+
+       @Test void a04_defaultInstance() throws Exception {
+               var a = list(JsonMap.of("k", "v"));
+               var json5l = Json5l.DEFAULT.write(a);
+               assertTrue(json5l.contains("\"k\":\"v\""));
+               var b = (List<JsonMap>) Json5l.DEFAULT.read(json5l, List.class, 
JsonMap.class);
+               assertEquals(1, b.size());
+               assertEquals("v", b.get(0).getString("k"));
+       }
+
+       
//-----------------------------------------------------------------------------------------------------------------
+       // Helper methods
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       private static Writer stringWriter() {
+               return new StringWriter();
+       }
+
+       private static Reader stringReader(String s) {
+               return new StringReader(s);
+       }
+}
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/ComboRoundTripTest_Base.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/ComboRoundTripTest_Base.java
index 4956344d33..ae837b791a 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/ComboRoundTripTest_Base.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/ComboRoundTripTest_Base.java
@@ -194,6 +194,34 @@ public abstract class ComboRoundTripTest_Base extends 
TestBase {
                t.testParseVerify("jsonl");
        }
 
+       
//-----------------------------------------------------------------------------------------------------------------
+       // JSON5L
+       
//-----------------------------------------------------------------------------------------------------------------
+
+       @ParameterizedTest
+       @MethodSource("testers")
+       public void a46_serializeJson5l(ComboRoundTrip_Tester<?> t) throws 
Exception {
+               t.testSerialize("json5l");
+       }
+
+       @ParameterizedTest
+       @MethodSource("testers")
+       public void a47_parseJson5l(ComboRoundTrip_Tester<?> t) throws 
Exception {
+               t.testParse("json5l");
+       }
+
+       @ParameterizedTest
+       @MethodSource("testers")
+       public void a48_parseJson5lJsonEquivalency(ComboRoundTrip_Tester<?> t) 
throws Exception {
+               t.testParseJsonEquivalency("json5l");
+       }
+
+       @ParameterizedTest
+       @MethodSource("testers")
+       public void a49_verifyJson5l(ComboRoundTrip_Tester<?> t) throws 
Exception {
+               t.testParseVerify("json5l");
+       }
+
        
//-----------------------------------------------------------------------------------------------------------------
        // XML
        
//-----------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java
index 0e3b0ea617..117b81f588 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/ComboRoundTrip_Tester.java
@@ -33,6 +33,7 @@ import org.apache.juneau.marshall.ini.*;
 import org.apache.juneau.marshall.jena.*;
 import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.marshall.json5.*;
+import org.apache.juneau.marshall.json5l.*;
 import org.apache.juneau.marshall.jsonl.*;
 import org.apache.juneau.marshall.markdown.*;
 import org.apache.juneau.marshall.msgpack.*;
@@ -114,6 +115,7 @@ public class ComboRoundTrip_Tester<T> {
                public Builder<T> json5T(String value) { expected.put("json5T", 
value); return this; }
                public Builder<T> json5R(String value) { expected.put("json5R", 
value); return this; }
                public Builder<T> jsonl(String value) { expected.put("jsonl", 
value); return this; }
+               public Builder<T> json5l(String value) { expected.put("json5l", 
value); return this; }
                public Builder<T> xml(String value) { expected.put("xml", 
value); return this; }
                public Builder<T> xmlT(String value) { expected.put("xmlT", 
value); return this; }
                public Builder<T> xmlR(String value) { expected.put("xmlR", 
value); return this; }
@@ -188,6 +190,7 @@ public class ComboRoundTrip_Tester<T> {
                serializers.put("json5T", create(b, 
Json5Serializer.create().typePropertyName("t").addBeanTypes().addRootType()));
                serializers.put("json5R", create(b, 
Json5Serializer.DEFAULT_READABLE.copy().addBeanTypes().addRootType()));
                serializers.put("jsonl", create(b, 
JsonlSerializer.create().keepNullProperties().addBeanTypes().addRootType()));
+               serializers.put("json5l", create(b, 
Json5lSerializer.create().keepNullProperties().addBeanTypes().addRootType()));
                serializers.put("xml", create(b, 
XmlSerializer.DEFAULT_SQ.copy().addBeanTypes().addRootType()));
                serializers.put("xmlT", create(b, 
XmlSerializer.create().sq().typePropertyName("t").addBeanTypes().addRootType()));
                serializers.put("xmlR", create(b, 
XmlSerializer.DEFAULT_SQ_READABLE.copy().addBeanTypes().addRootType()));
@@ -234,6 +237,7 @@ public class ComboRoundTrip_Tester<T> {
                parsers.put("json5T", create(b, 
Json5Parser.create().typePropertyName("t")));
                parsers.put("json5R", create(b, Json5Parser.DEFAULT.copy()));
                parsers.put("jsonl", create(b, JsonlParser.create()));
+               parsers.put("json5l", create(b, Json5lParser.create()));
                parsers.put("xml", create(b, XmlParser.DEFAULT.copy()));
                parsers.put("xmlT", create(b, 
XmlParser.create().typePropertyName("t")));
                parsers.put("xmlR", create(b, XmlParser.DEFAULT.copy()));
diff --git 
a/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
 
b/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
index 61b4ca417c..3618cb649c 100644
--- 
a/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
+++ 
b/juneau-rest/juneau-rest-client-classic/src/main/java/org/apache/juneau/rest/client/classic/RestClient.java
@@ -95,6 +95,7 @@ import org.apache.juneau.marshall.ini.*;
 import org.apache.juneau.marshall.jcs.*;
 import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.marshall.json5.*;
+import org.apache.juneau.marshall.json5l.*;
 import org.apache.juneau.marshall.jsonl.*;
 import org.apache.juneau.marshall.markdown.*;
 import org.apache.juneau.marshall.marshaller.*;
@@ -5359,6 +5360,7 @@ public class RestClient extends MarshallingContextable 
implements HttpClient, Cl
                                        JsonSerializer.class,
                                        JcsSerializer.class,
                                        Json5Serializer.class,
+                                       Json5lSerializer.class,
                                        JsonlSerializer.class,
                                        HtmlSerializer.class,
                                        XmlSerializer.class,
@@ -5380,6 +5382,7 @@ public class RestClient extends MarshallingContextable 
implements HttpClient, Cl
                                .parsers(
                                        JsonParser.class,
                                        Json5Parser.class,
+                                       Json5lParser.class,
                                        JsonlParser.class,
                                        XmlParser.class,
                                        HtmlParser.class,
diff --git 
a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/ContentType.java
 
b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/ContentType.java
index 890e474dc4..43d7cc8a64 100644
--- 
a/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/ContentType.java
+++ 
b/juneau-rest/juneau-rest-common/src/main/java/org/apache/juneau/http/header/ContentType.java
@@ -54,6 +54,8 @@ public class ContentType extends HttpMediaTypeHeader {
        public static final ContentType APPLICATION_JSON = new 
ContentType("application/json");
        /** Content-Type for {@code application/json5} (Juneau {@code 
Json5Serializer}/{@code Json5Parser}). */
        public static final ContentType APPLICATION_JSON5 = new 
ContentType("application/json5");
+       /** Content-Type for {@code application/json5l} (Juneau {@code 
Json5lSerializer}/{@code Json5lParser}). */
+       public static final ContentType APPLICATION_JSON5L = new 
ContentType("application/json5l");
        /** Content-Type for {@code application/jsonl} (Juneau {@code 
JsonlSerializer}/{@code JsonlParser}). */
        public static final ContentType APPLICATION_JSONL = new 
ContentType("application/jsonl");
        /** Content-Type for {@code application/json-patch+json} (RFC 6902). */
diff --git 
a/juneau-rest/juneau-rest-common/src/test/java/org/apache/juneau/http/header/ContentType_Test.java
 
b/juneau-rest/juneau-rest-common/src/test/java/org/apache/juneau/http/header/ContentType_Test.java
index 3ecfb70f4c..915b58c0c5 100644
--- 
a/juneau-rest/juneau-rest-common/src/test/java/org/apache/juneau/http/header/ContentType_Test.java
+++ 
b/juneau-rest/juneau-rest-common/src/test/java/org/apache/juneau/http/header/ContentType_Test.java
@@ -72,6 +72,7 @@ class ContentType_Test extends TestBase {
                        Arguments.of("APPLICATION_JCS_JSON", 
ContentType.APPLICATION_JCS_JSON, "application/jcs+json"),
                        Arguments.of("APPLICATION_JSON", 
ContentType.APPLICATION_JSON, "application/json"),
                        Arguments.of("APPLICATION_JSON5", 
ContentType.APPLICATION_JSON5, "application/json5"),
+                       Arguments.of("APPLICATION_JSON5L", 
ContentType.APPLICATION_JSON5L, "application/json5l"),
                        Arguments.of("APPLICATION_JSONL", 
ContentType.APPLICATION_JSONL, "application/jsonl"),
                        Arguments.of("APPLICATION_JSON_PATCH", 
ContentType.APPLICATION_JSON_PATCH, "application/json-patch+json"),
                        Arguments.of("APPLICATION_LD_JSON", 
ContentType.APPLICATION_LD_JSON, "application/ld+json"),
diff --git 
a/juneau-rest/juneau-rest-server-reactive/src/main/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor.java
 
b/juneau-rest/juneau-rest-server-reactive/src/main/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor.java
index 60b3b4e0f7..defe4d8daa 100644
--- 
a/juneau-rest/juneau-rest-server-reactive/src/main/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor.java
+++ 
b/juneau-rest/juneau-rest-server-reactive/src/main/java/org/apache/juneau/rest/server/reactive/ReactiveResponseProcessor.java
@@ -72,7 +72,7 @@ import jakarta.servlet.http.*;
  *     <li><b>SSE</b> ({@code text/event-stream}) &mdash; each element is 
emitted as a Server-Sent-Events
  *             frame. {@link SseEvent} elements are written verbatim; any 
other element type is JSON-encoded
  *             into the {@code data:} field.
- *     <li><b>NDJSON</b> ({@code application/x-ndjson}, {@code 
application/jsonl}) &mdash; each element is
+ *     <li><b>NDJSON</b> ({@code application/x-ndjson}, {@code 
application/jsonl}, {@code application/json5l}) &mdash; each element is
  *             JSON-encoded on its own line.
  *     <li><b>Buffer</b> (default, any other media type) &mdash; all elements 
are collected into a
  *             {@link java.util.List List} and serialized through the normal 
serializer chain (e.g. a JSON
@@ -312,7 +312,7 @@ public class ReactiveResponseProcessor implements 
ResponseProcessor {
                var probe = ((ct == null ? "" : ct) + "," + (accept == null ? 
"" : accept)).toLowerCase(Locale.ROOT);
                if (probe.contains("event-stream"))
                        return Shape.SSE;
-               if (probe.contains("ndjson") || probe.contains("jsonl") || 
probe.contains("json-seq"))
+               if (probe.contains("ndjson") || probe.contains("jsonl") || 
probe.contains("json5l") || probe.contains("json-seq"))
                        return Shape.NDJSON;
                return Shape.BUFFER;
        }
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/config/BasicUniversalConfig.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/config/BasicUniversalConfig.java
index 16a2de8d2a..49d5cbe6ce 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/config/BasicUniversalConfig.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/server/config/BasicUniversalConfig.java
@@ -28,6 +28,7 @@ import org.apache.juneau.marshall.ini.*;
 import org.apache.juneau.marshall.jcs.*;
 import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.marshall.json5.*;
+import org.apache.juneau.marshall.json5l.*;
 import org.apache.juneau.marshall.jsonl.*;
 import org.apache.juneau.marshall.markdown.*;
 import org.apache.juneau.marshall.msgpack.*;
@@ -62,6 +63,7 @@ import org.apache.juneau.rest.server.servlet.*;
  *                                     <li class='jc'>{@link 
HtmlSchemaDocSerializer}
  *                                     <li class='jc'>{@link JsonSerializer}
  *                                     <li class='jc'>{@link Json5Serializer}
+ *                                     <li class='jc'>{@link Json5lSerializer}
  *                                     <li class='jc'>{@link 
JsonSchemaSerializer}
  *                                     <li class='jc'>{@link XmlDocSerializer}
  *                                     <li class='jc'>{@link UonSerializer}
@@ -76,6 +78,7 @@ import org.apache.juneau.rest.server.servlet.*;
  *                             <ul class='javatree'>
  *                                     <li class='jc'>{@link JsonParser}
  *                                     <li class='jc'>{@link Json5Parser}
+ *                                     <li class='jc'>{@link Json5lParser}
  *                                     <li class='jc'>{@link XmlParser}
  *                                     <li class='jc'>{@link HtmlParser}
  *                                     <li class='jc'>{@link UonParser}
@@ -153,6 +156,7 @@ import org.apache.juneau.rest.server.servlet.*;
                JsonSerializer.class,
                JcsSerializer.class,
                Json5Serializer.class,
+               Json5lSerializer.class,
                JsonlSerializer.class,
                JsonSchemaSerializer.class,
                XmlDocSerializer.class,
@@ -177,6 +181,7 @@ import org.apache.juneau.rest.server.servlet.*;
        parsers={
                JsonParser.class,
                Json5Parser.class,
+               Json5lParser.class,
                JsonlParser.class,
                XmlParser.class,
                HtmlParser.class,

Reply via email to