001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.marshall.jsonl;
018
019import org.apache.juneau.marshall.json.*;
020import org.apache.juneau.marshall.swap.spi.*;
021
022/**
023 * Serializes POJO models to JSONL (JSON Lines).
024 *
025 * <h5 class='topic'>Media types</h5>
026 * <p>
027 * Handles <c>Accept</c> types:  <bc>application/jsonl, application/jsonlines, application/x-ndjson, text/jsonl</bc>
028 * <p>
029 * Produces <c>Content-Type</c> types:  <bc>application/jsonl</bc>
030 *
031 * <h5 class='topic'>Description</h5>
032 * <p>
033 * Serializes POJOs to JSONL format where each top-level value is written as a compact JSON
034 * value on its own line.  The conversion is as follows...
035 * <ul class='spaced-list'>
036 *    <li>Maps and beans are converted to JSON objects, one per line.
037 *    <li>Collections and arrays are converted to one JSON value per line (no wrapping brackets).
038 *    <li>Strings are converted to JSON strings, one per line.
039 *    <li>Numbers are converted to JSON numbers, one per line.
040 *    <li>Booleans are converted to JSON booleans, one per line.
041 *    <li>Nulls are converted to JSON nulls, one per line.
042 *    <li>Nested objects and collections remain compact on a single line per outer element.
043 * </ul>
044 * <p>
045 * Non-JSON-primitive types are transformed through
046 * {@link ObjectSwap ObjectSwaps}.
047 *
048 * <h5 class='section'>Example:</h5>
049 * <p class='bjava'>
050 *    <jc>// Use the default serializer to serialize a list of POJOs</jc>
051 *    String <jv>jsonl</jv> = JsonlSerializer.<jsf>DEFAULT</jsf>.write(<jv>myList</jv>);
052 *
053 *    <jc>// Create a custom serializer</jc>
054 *    JsonlSerializer <jv>serializer</jv> = JsonlSerializer.<jsm>create</jsm>().build();
055 *
056 *    <jc>// Clone an existing serializer</jc>
057 *    <jv>serializer</jv> = JsonlSerializer.<jsf>DEFAULT</jsf>.copy().build();
058 *
059 *    <jc>// Serialize a list of POJOs to JSONL</jc>
060 *    String <jv>jsonl</jv> = <jv>serializer</jv>.write(<jv>myList</jv>);
061 * </p>
062 *
063 * <h5 class='figure'>Example output (List of beans):</h5>
064 * <p class='bjson'>
065 * {"name":"Alice","age":30}
066 * {"name":"Bob","age":25}
067 * {"name":"Carol","age":35}
068 * </p>
069 *
070 * <h5 class='figure'>Complex (bean with nested object and array):</h5>
071 * <p class='bjson'>
072 * {"name":"Alice","age":30,"address":{"street":"123 Main St","city":"Boston"},"tags":["a","b","c"]}
073 * {"name":"Bob","age":25,"address":{"street":"456 Oak Ave","city":"Portland"},"tags":["d","e"]}
074 * </p>
075 *
076 * <h5 class='section'>Limitations compared to JSON</h5>
077 * <p>
078 * <ul class='spaced-list'>
079 *    <li><b>No pretty-printing</b>: {@link org.apache.juneau.marshall.serializer.WriterSerializer.Builder#useWhitespace() useWhitespace()}
080 *       is always suppressed.  JSONL requires each JSON value to fit on a single line;
081 *       applying intra-line pretty-printing would embed newlines and produce invalid JSONL.
082 *       The setting is silently ignored regardless of its configured value.
083 *    <li><b>Top-level semantics differ</b>: Collections and arrays are unwrapped — each element
084 *       becomes one line.  A single non-collection object produces one JSONL line.
085 * </ul>
086 *
087 * <h5 class='section'>Notes:</h5><ul>
088 *    <li class='note'>This class is thread safe and reusable.
089 * </ul>
090 *
091 * <h5 class='section'>See Also:</h5><ul>
092 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JsonlSupport">JSONL</a>
093 * </ul>
094 */
095@SuppressWarnings({
096   "java:S110", // Inheritance depth acceptable
097   "java:S9149" // Per-format static factories intentionally shadow the parent's.
098})
099public class JsonlSerializer extends JsonSerializer {
100
101   /** Default serializer. */
102   public static final JsonlSerializer DEFAULT = new JsonlSerializer(create());
103
104   /**
105    * Creates a new builder for this object.
106    *
107    * @return A new builder.
108    */
109   @SuppressWarnings({
110      "java:S1452" // Builder<?> wildcard return intentional; callers chain via fluent API without needing the concrete type
111   })
112   public static JsonSerializer.Builder<?> create() {
113      return JsonSerializer.create()
114         .produces("application/jsonl")
115         .accept("application/jsonl,application/jsonlines,application/x-ndjson,text/jsonl")
116         .type(JsonlSerializer.class)
117         .useWhitespace(false);
118   }
119
120   /**
121    * Constructor.
122    *
123    * @param builder The builder for this object.
124    */
125   public JsonlSerializer(JsonSerializer.Builder<?> builder) {
126      super(builder.useWhitespace(false));
127   }
128
129   @Override /* Overridden from Context */
130   public JsonlSerializerSession.Builder createSession() {
131      return JsonlSerializerSession.create(this);
132   }
133
134}