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}