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 019 020import java.io.*; 021import java.util.*; 022 023import org.apache.juneau.marshall.json.*; 024import org.apache.juneau.marshall.serializer.*; 025import org.apache.juneau.marshall.stream.*; 026import static org.apache.juneau.commons.utils.Shorts.*; 027 028/** 029 * Session object that lives for the duration of a single use of {@link JsonlSerializer}. 030 * 031 * <h5 class='section'>Notes:</h5><ul> 032 * <li class='warn'>This class is not thread safe and is typically discarded after one use. 033 * </ul> 034 * 035 * <h5 class='section'>See Also:</h5><ul> 036 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JsonlSupport">JSONL</a> 037 * </ul> 038 */ 039@SuppressWarnings({ 040 "java:S110", // Inheritance depth acceptable 041 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 042 "resource" // Resource management handled externally 043}) 044public class JsonlSerializerSession extends JsonSerializerSession { 045 046 /** 047 * Builder class. 048 */ 049 @SuppressWarnings({ 050 "unused" // ctx passed to super(); parent uses it; Builder stores for constructor chain 051 }) 052 public static class Builder extends JsonSerializerSession.Builder<Builder> { 053 054 private final JsonlSerializer ctx; 055 056 /** 057 * Constructor 058 * 059 * @param ctx The context creating this session. 060 * <br>Cannot be <jk>null</jk>. 061 */ 062 protected Builder(JsonlSerializer ctx) { 063 super(reqnn("ctx", ctx)); 064 this.ctx = ctx; 065 } 066 067 @Override 068 public JsonlSerializerSession build() { 069 return new JsonlSerializerSession(this); 070 } 071 } 072 073 /** 074 * Creates a new builder for this object. 075 * 076 * @param ctx The context creating this session. 077 * <br>Cannot be <jk>null</jk>. 078 * @return A new builder. 079 */ 080 public static Builder create(JsonlSerializer ctx) { 081 return new Builder(reqnn("ctx", ctx)); 082 } 083 084 /** 085 * Constructor. 086 * 087 * @param builder The builder for this object. 088 */ 089 protected JsonlSerializerSession(Builder builder) { 090 super(builder); 091 } 092 093 /** 094 * Opens a low-level push generator that emits JSONL one structural event at a time, with a 095 * trailing newline after every top-level value, bound to this live session. 096 * 097 * <p> 098 * Same honored/ignored builder properties as {@link JsonSerializerSession#writeTokens(Object)} 099 * except that <c>useWhitespace</c> is forced to <jk>false</jk> — pretty-printed JSONL is 100 * not standard. 101 * 102 * @param output The output. 103 * @return A new {@link JsonlTokenWriter}. 104 * @throws IOException If the output type is not supported or could not be opened. 105 */ 106 @Override /* JsonSerializerSession */ 107 public TokenWriter writeTokens(Object output) throws IOException { 108 var walk = new PojoWalker.Options( 109 isKeepNullProperties(), 110 isTrimEmptyMaps(), 111 isTrimEmptyCollections(), 112 isSortMaps(), 113 isSortCollections(), 114 isTrimStrings(), 115 getMarshallingContext()); 116 var settings = new JsonTokenWriter.Settings( 117 false /* useWhitespace — JSONL is one-record-per-line, no pretty-print */, 118 getMaxIndent(), 119 getQuoteChar(), 120 isEscapeSolidus(), 121 isTrimStrings(), 122 false /* simpleAttrs — JSONL uses RFC-8259 strict per line */, 123 walk, 124 false /* disableObject */); 125 return JsonlTokenWriter.forOutput(output, settings); 126 } 127 128 /** 129 * Streaming array-element {@link RecordWriter} for JSONL. 130 * 131 * <p> 132 * Unlike the inherited {@link JsonSerializerSession#writeArrayRecords(Object)} — which wraps 133 * the writes in a bracketed top-level <c>[...]</c> array and is invalid for line-delimited JSONL — 134 * this aliases the JSONL line record stream: the {@link JsonlTokenWriter} returned by 135 * {@link #writeTokens(Object)} is itself a {@link RecordWriter} whose 136 * {@link RecordWriter#write(Object) write(...)} emits one top-level value per line (no surrounding 137 * brackets). Memory is O(1) in the number of records. 138 * 139 * @param output The output. 140 * @return A new line-delimited {@link RecordWriter}. 141 * @throws IOException If a problem occurred opening the underlying output. 142 */ 143 @Override /* ArrayRecordWritable */ 144 public RecordWriter writeArrayRecords(Object output) throws IOException { 145 return writeTokens(output); 146 } 147 148 @Override /* Overridden from JsonSerializerSession */ 149 protected void doWrite(SerializerPipe out, Object o) throws IOException, SerializeException { 150 var w = getJsonWriter(out); 151 var cm = getClassMetaForObject(o); 152 var depth = getInitialDepth(); 153 154 if (cm != null && (cm.isCollection() || cm.isArray())) { 155 var c = cm.isArray() ? toList(cm.inner(), o) : (Collection<?>) o; 156 var elementType = cm.getElementType(); 157 for (Object item : c) { 158 writeAnything(w.i(depth), item, elementType, "root", null); 159 w.w('\n'); 160 } 161 } else if (cm != null && cm.isStreamable()) { 162 var elementType = cm.getElementType(); 163 forEachStreamableEntry(o, cm, item -> { 164 writeAnything(w.i(depth), item, elementType, "root", null); 165 w.w('\n'); 166 }); 167 } else { 168 writeAnything(w.i(depth), o, getExpectedRootType(o), "root", null); 169 w.w('\n'); 170 } 171 } 172}