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.serializer;
018
019import static java.util.Collections.*;
020import static org.apache.juneau.commons.utils.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.Shorts.*;
022import static org.apache.juneau.commons.utils.StringUtils.*;
023import static org.apache.juneau.commons.utils.SystemUtils.*;
024
025import java.io.*;
026import java.net.*;
027import java.util.*;
028import java.util.function.*;
029
030import org.apache.juneau.commons.collections.*;
031import org.apache.juneau.commons.http.*;
032import org.apache.juneau.marshall.*;
033import org.apache.juneau.marshall.json5.*;
034import org.apache.juneau.marshall.soap.*;
035import org.apache.juneau.marshall.stream.*;
036
037/**
038 * Parent class for all Juneau serializers.
039 *
040 * <h5 class='topic'>Description</h5>
041 * <p>
042 * Base serializer class that serves as the parent class for all serializers.
043 *
044 * <p>
045 * The purpose of this class is:
046 * <ul>
047 *    <li>Maintain a read-only configuration state of a serializer.
048 *    <li>Create session objects used for serializing POJOs (i.e. {@link SerializerSession}).
049 *    <li>Provide convenience methods for serializing POJOs without having to construct session objects.
050 * </ul>
051 *
052 * <p>
053 * Subclasses should (but are not required to) extend directly from {@link OutputStreamSerializer} or {@link WriterSerializer} depending on
054 * whether it's a stream or character based serializer.
055 *
056 * <p>
057 * Subclasses must implement serialization via one of the following methods:
058 * <ul class='javatree'>
059 *    <li class='jmp'>{@link #doWrite(SerializerSession, SerializerPipe, Object)}
060 *    <li class='jmp'>{@link SerializerSession#doWrite(SerializerPipe, Object)}
061 * </ul>
062 * <br>
063 *
064 * <h5 class='section'>Notes:</h5><ul>
065 *    <li class='note'>This class is thread safe and reusable.
066 * </ul>
067 *
068 * <h5 class='section'>See Also:</h5><ul>
069 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a>
070
071 * </ul>
072 */
073@SuppressWarnings({
074   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
075   "rawtypes" // createSerializerBuilder() returns the raw Builder because the concrete SELF type is only known reflectively via Context.createBuilder(c)
076})
077public class Serializer extends MarshallingTraverseContext {
078
079   /**
080    * Builder class.
081    */
082   @SuppressWarnings({
083      "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
084   })
085   public abstract static class Builder<SELF extends Builder<SELF>> extends MarshallingTraverseContext.Builder<SELF> {
086
087      private boolean addBeanTypes;
088      private boolean addRootType;
089      private boolean keepNullProperties;
090      private boolean nonDefault;
091      private boolean sortCollections;
092      private boolean sortMaps;
093      private boolean trimEmptyCollections;
094      private boolean trimEmptyMaps;
095      private boolean trimStrings;
096      private Class<? extends SerializerListener> listener;
097      private String accept;
098      private String produces;
099      private UriContext uriContext;
100      private UriRelativity uriRelativity;
101      private UriResolution uriResolution;
102
103      /**
104       * Constructor, default settings.
105       */
106      protected Builder() {
107         produces = null;
108         accept = null;
109         addBeanTypes = env("Serializer.addBeanTypes", false);
110         addRootType = env("Serializer.addRootType", false);
111         keepNullProperties = env("Serializer.keepNullProperties", false);
112         nonDefault = env("Serializer.nonDefault", false);
113         sortCollections = env("Serializer.sortCollections", false);
114         sortMaps = env("Serializer.sortMaps", false);
115         trimEmptyCollections = env("Serializer.trimEmptyCollections", false);
116         trimEmptyMaps = env("Serializer.trimEmptyMaps", false);
117         trimStrings = env("Serializer.trimStrings", false);
118         uriContext = UriContext.DEFAULT;
119         uriRelativity = UriRelativity.RESOURCE;
120         uriResolution = UriResolution.NONE;
121         listener = null;
122      }
123
124      /**
125       * Copy constructor.
126       *
127       * @param copyFrom The builder to copy from.
128       *    <br>Cannot be <jk>null</jk>.
129       */
130      protected Builder(Builder<?> copyFrom) {
131         super(reqnn("copyFrom", copyFrom));
132         produces = copyFrom.produces;
133         accept = copyFrom.accept;
134         addBeanTypes = copyFrom.addBeanTypes;
135         addRootType = copyFrom.addRootType;
136         keepNullProperties = copyFrom.keepNullProperties;
137         nonDefault = copyFrom.nonDefault;
138         sortCollections = copyFrom.sortCollections;
139         sortMaps = copyFrom.sortMaps;
140         trimEmptyCollections = copyFrom.trimEmptyCollections;
141         trimEmptyMaps = copyFrom.trimEmptyMaps;
142         trimStrings = copyFrom.trimStrings;
143         uriContext = copyFrom.uriContext;
144         uriRelativity = copyFrom.uriRelativity;
145         uriResolution = copyFrom.uriResolution;
146         listener = copyFrom.listener;
147      }
148
149      /**
150       * Copy constructor.
151       *
152       * @param copyFrom The bean to copy from.
153       *    <br>Cannot be <jk>null</jk>.
154       */
155      protected Builder(Serializer copyFrom) {
156         super(reqnn("copyFrom", copyFrom));
157         produces = copyFrom.produces;
158         accept = copyFrom.accept;
159         addBeanTypes = copyFrom.addBeanTypes;
160         addRootType = copyFrom.addRootType;
161         keepNullProperties = copyFrom.keepNullProperties;
162         nonDefault = copyFrom.nonDefault;
163         sortCollections = copyFrom.sortCollections;
164         sortMaps = copyFrom.sortMaps;
165         trimEmptyCollections = copyFrom.trimEmptyCollections;
166         trimEmptyMaps = copyFrom.trimEmptyMaps;
167         trimStrings = copyFrom.trimStrings;
168         uriContext = copyFrom.getUriContext();
169         uriRelativity = copyFrom.getUriRelativity();
170         uriResolution = copyFrom.getUriResolution();
171         listener = copyFrom.listener;
172      }
173
174      /**
175       *    Specifies the accept media types that the serializer can handle.
176       *
177       *    <p>
178       *    Can contain meta-characters per the <c>media-type</c> specification of <a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">RFC2616/14.1</a>
179       *    <p>
180       *    If empty, then assumes the only media type supported is <c>produces</c>.
181       *    <p>
182       *    For example, if this serializer produces <js>"application/json"</js> but should handle media types of
183       *    <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be:
184       *    <p class='bjava'>
185       *       <jv>builder</jv>.produces(<js>"application/json"</js>);
186       *       <jv>builder</jv>.accept(<js>"application/json,text/json"</js>);
187       *    </p>
188       * <p>
189       * The accept value can also contain q-values.
190       *
191       * @param value The value for this setting.
192       *    <br>Can be <jk>null</jk> (will default to the value specified by {@link #produces(String)}).
193       * @return This object.
194       */
195      public SELF accept(String value) {
196         accept = value;
197         return self();
198      }
199
200      /**
201       * Add <js>"_type"</js> properties when needed.
202       *
203       * <p>
204       * When enabled, <js>"_type"</js> properties will be added to beans if their type cannot be inferred
205       * through reflection.
206       *
207       * <p>
208       * This is used to recreate the correct objects during parsing if the object types cannot be inferred.
209       * <br>For example, when serializing a <c>Map&lt;String,Object&gt;</c> field where the bean class cannot be determined from
210       * the type of the values.
211       *
212       * <p>
213       * Note the differences between the following settings:
214       * <ul class='javatree'>
215       *    <li class='jf'>{@link #addRootType()} - Affects whether <js>'_type'</js> is added to root node.
216       *    <li class='jf'>{@link #addBeanTypes()} - Affects whether <js>'_type'</js> is added to any nodes.
217       * </ul>
218       *
219       * <h5 class='section'>Example:</h5>
220       * <p class='bjava'>
221       *    <jc>// Create a serializer that adds _type to nodes.</jc>
222       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
223       *       .<jsm>create</jsm>()
224       *       .addBeanTypes()
225       *       .build();
226       *
227       *    <jc>// Our map of beans to serialize.</jc>
228       *    <ja>@Marshalled</ja>(typeName=<js>"mybean"</js>)
229       *    <jk>public class</jk> MyBean {
230       *       <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
231       *    }
232       *    JsonMap <jv>myMap</jv> = JsonMap.of(<js>"foo"</js>, <jk>new</jk> MyBean());
233       *
234       *    <jc>// Will contain:  {"foo":{"_type":"mybean","foo":"bar"}}</jc>
235       *    String <jv>json</jv> = <jv>serializer</jv>.write(<jv>myMap</jv>);
236       * </p>
237       *
238       * <p>
239       * <b>Token streams:</b> the cursor's raw structural methods do not emit <c>"_type"</c>;
240       * however this setting IS honored by
241       * {@link TokenWriter#object(Object) TokenWriter.object(Object)}
242       * when walking a bean.
243       *
244       * @return This object.
245       */
246      public SELF addBeanTypes() {
247         return addBeanTypes(true);
248      }
249
250      /**
251       * Same as {@link #addBeanTypes()} but allows you to explicitly specify the value.
252       *
253       * @param value The value for this setting.
254       * @return This object.
255       */
256      public SELF addBeanTypes(boolean value) {
257         addBeanTypes = value;
258         return self();
259      }
260
261      /**
262       * Add type attribute to root nodes.
263       *
264       * <p>
265       * When enabled, <js>"_type"</js> properties will be added to top-level beans.
266       *
267       * <p>
268       * When disabled, it is assumed that the parser knows the exact Java POJO type being parsed, and therefore top-level
269       * type information that might normally be included to determine the data type will not be serialized.
270       *
271       * <p>
272       * For example, when serializing a top-level POJO with a {@link Marshalled#typeName() @Marshalled(typeName)} value, a
273       * <js>'_type'</js> attribute will only be added when this setting is enabled.
274       *
275       * <p>
276       * Note the differences between the following settings:
277       * <ul class='javatree'>
278       *    <li class='jf'>{@link #addRootType()} - Affects whether <js>'_type'</js> is added to root node.
279       *    <li class='jf'>{@link #addBeanTypes()} - Affects whether <js>'_type'</js> is added to any nodes.
280       * </ul>
281       *
282       * <h5 class='section'>Example:</h5>
283       * <p class='bjava'>
284       *    <jc>// Create a serializer that adds _type to root node.</jc>
285       *    WriterSerializer <jv>serializer</jv>= JsonSerializer
286       *       .<jsm>create</jsm>()
287       *       .addRootType()
288       *       .build();
289       *
290       *    <jc>// Our bean to serialize.</jc>
291       *    <ja>@Marshalled</ja>(typeName=<js>"mybean"</js>)
292       *    <jk>public class</jk> MyBean {
293       *       <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
294       *    }
295       *
296       *    <jc>// Will contain:  {"_type":"mybean","foo":"bar"}</jc>
297       *    String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean());
298       * </p>
299       *
300       * <p>
301       * <b>Token streams:</b> the cursor's raw structural methods do not emit <c>"_type"</c>;
302       * however this setting IS honored by
303       * {@link TokenWriter#object(Object) TokenWriter.object(Object)}
304       * for the root value.
305       *
306       * @return This object.
307       */
308      public SELF addRootType() {
309         return addRootType(true);
310      }
311
312      /**
313       * Same as {@link #addRootType()} but allows you to explicitly specify the value.
314       *
315       * @param value The value for this setting.
316       * @return This object.
317       */
318      public SELF addRootType(boolean value) {
319         addRootType = value;
320         return self();
321      }
322
323      @Override /* Overridden from Context.Builder<?> */
324      public Serializer build() {
325         return build(Serializer.class);
326      }
327
328      @Override /* Overridden from Context.Builder<?> */
329      public abstract SELF copy();
330
331      /**
332       * Returns the current value for the 'accept' property.
333       *
334       * @return The current value for the 'accept' property.
335       */
336      public String getAccept() { return accept; }
337
338      /**
339       * Returns the current value for the 'produces' property.
340       *
341       * @return The current value for the 'produces' property.
342       */
343      public String getProduces() { return produces; }
344
345      @Override /* Overridden from Context.Builder<?> */
346      public HashKey hashKey() {
347         // @formatter:off
348         return HashKey.of(
349            super.hashKey(),
350            produces,
351            accept,
352            addBeanTypes,
353            addRootType,
354            keepNullProperties,
355            nonDefault,
356            sortCollections,
357            sortMaps,
358            trimEmptyCollections,
359            trimEmptyMaps,
360            trimStrings,
361            uriContext,
362            uriRelativity,
363            uriResolution,
364            listener
365         );
366         // @formatter:on
367      }
368
369      /**
370       * Don't trim null bean property values.
371       *
372       * <p>
373       * When enabled, null bean values will be serialized to the output.
374       *
375       * <h5 class='section'>Notes:</h5><ul>
376       *    <li class='note'>Not enabling this setting will cause <c>Map</c>s with <jk>null</jk> values to be lost during parsing.
377       * </ul>
378       *
379       * <h5 class='section'>Example:</h5>
380       * <p class='bjava'>
381       *    <jc>// Create a serializer that serializes null properties.</jc>
382       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
383       *       .<jsm>create</jsm>()
384       *       .keepNullProperties()
385       *       .build();
386       *
387       *    <jc>// Our bean to serialize.</jc>
388       *    <jk>public class</jk> MyBean {
389       *       <jk>public</jk> String <jf>foo</jf> = <jk>null</jk>;
390       *    }
391       *
392       *    <jc>// Will contain "{foo:null}".</jc>
393       *    String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean());
394       * </p>
395       *
396       * <p>
397       * <b>Token streams:</b> the cursor's raw structural methods always emit what the caller
398       * asks for; however this setting IS honored by
399       * {@link TokenWriter#object(Object) TokenWriter.object(Object)}
400       * during a bean walk.
401       *
402       * @return This object.
403       */
404      public SELF keepNullProperties() {
405         return keepNullProperties(true);
406      }
407
408      /**
409       * Same as {@link #keepNullProperties()} but allows you to explicitly specify the value.
410       *
411       * @param value The value for this setting.
412       * @return This object.
413       */
414      public SELF keepNullProperties(boolean value) {
415         keepNullProperties = value;
416         return self();
417      }
418
419      /**
420       * Don't serialize bean properties whose values equal the default for their type.
421       *
422       * <p>
423       * When enabled, bean properties whose current value equals the type's natural default are omitted from
424       * the serialized output. This is the Juneau analog of Jackson's <c>JsonInclude.Include.NON_DEFAULT</c>
425       * inclusion mode.
426       *
427       * <p>
428       * The notion of "default" depends on the property type:
429       * <ul class='spaced-list'>
430       *    <li>Primitives and primitive wrappers (<code><jk>int</jk></code>, <code>Integer</code>,
431       *       <code><jk>boolean</jk></code>, <code>Boolean</code>, etc.) use the Java type default
432       *       (<c>0</c>, <c>false</c>, <code>'\0'</code>, <jk>null</jk>).
433       *    <li>Bean properties on bean-typed property values use a <i>reference instance</i> of the bean type —
434       *       instantiated once via the no-arg constructor and cached on the {@link ClassMeta} — and the
435       *       property is omitted when its current value equals that reference's value.
436       *    <li>When no reference instance can be built (no accessible no-arg constructor, or instantiation
437       *       throws), <c>nonDefault</c> is silently skipped for that bean — the property is emitted normally
438       *       rather than failing the serialize.
439       * </ul>
440       *
441       * <p>
442       * <b>Equality semantics:</b> value-equals-default uses {@link Objects#equals}, with numeric values
443       * compared by mathematical value (BigDecimal.compareTo-style) so <c>1.0</c> equals <c>1</c>,
444       * <c>0.0</c> equals <c>-0.0</c>, and <code>BigDecimal(<js>"1.0"</js>)</code> equals
445       * <code>BigDecimal(<js>"1"</js>)</code>.
446       *
447       * <p>
448       * <b>Precedence:</b> the serialize-side inclusion knobs short-circuit in a fixed order — first "omit" wins:
449       * <ol>
450       *    <li><jk>null</jk> values (including {@link Optional#empty()}) are governed by
451       *       {@link #keepNullProperties()}.
452       *    <li>Non-<jk>null</jk> string values are normalized by {@link #trimStrings()}.
453       *    <li>Empty collections / arrays / maps are omitted by {@link #trimEmptyCollections()} /
454       *       {@link #trimEmptyMaps()}.
455       *    <li>Surviving values that equal their type's default are omitted by <c>nonDefault</c>.
456       * </ol>
457       * <jk>null</jk> is resolved by <c>keepNullProperties</c>, not treated as a <c>nonDefault</c> case.
458       *
459       * <h5 class='section'>Example:</h5>
460       * <p class='bjava'>
461       *    <jc>// Create a serializer that omits default-valued properties.</jc>
462       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
463       *       .<jsm>create</jsm>()
464       *       .nonDefault()
465       *       .build();
466       *
467       *    <jc>// Our bean to serialize.</jc>
468       *    <jk>public class</jk> MyBean {
469       *       <jk>public int</jk> <jf>count</jf>;            <jc>// Default 0; omitted unless set to non-zero.</jc>
470       *       <jk>public</jk> String <jf>name</jf> = <js>"x"</js>; <jc>// Bean-default is "x"; only omitted when value equals "x".</jc>
471       *    }
472       *
473       *    <jc>// Produces "{}" because both fields equal their defaults.</jc>
474       *    String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean());
475       * </p>
476       *
477       * <h5 class='section'>See Also:</h5><ul>
478       *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/NullAndInclusionPolicies">Null &amp; Inclusion Policies</a>
479       * </ul>
480       *
481       * @return This object.
482       * @since 10.0.0
483       */
484      public SELF nonDefault() {
485         return nonDefault(true);
486      }
487
488      /**
489       * Same as {@link #nonDefault()} but allows you to explicitly specify the value.
490       *
491       * @param value The value for this setting.
492       * @return This object.
493       * @since 10.0.0
494       */
495      public SELF nonDefault(boolean value) {
496         nonDefault = value;
497         return self();
498      }
499
500      /**
501       * Serializer listener.
502       *
503       * <p>
504       * Class used to listen for errors and warnings that occur during serialization.
505       *
506       * <h5 class='section'>Example:</h5>
507       * <p class='bjava'>
508       *    <jc>// Define our serializer listener.</jc>
509       *    <jc>// Simply captures all errors.</jc>
510       *    <jk>public class</jk> MySerializerListener <jk>extends</jk> SerializerListener {
511       *
512       *       <jc>// A simple property to store our events.</jc>
513       *       <jk>public</jk> List&lt;String&gt; <jf>events</jf> = <jk>new</jk> LinkedList&lt;&gt;();
514       *
515       *       <ja>@Override</ja>
516       *       <jk>public</jk> &lt;T&gt; <jk>void</jk> onError(SerializerSession <jv>session</jv>, Throwable <jv>throwable</jv>, String <jv>msg</jv>) {
517       *          <jf>events</jf>.add(<jv>session</jv>.getLastLocation() + <js>","</js> + <jv>msg</jv> + <js>","</js> + <jv>throwable</jv>);
518       *       }
519       *    }
520       *
521       *    <jc>// Create a serializer using our listener.</jc>
522       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
523       *       .<jsm>create</jsm>()
524       *       .listener(MySerializerListener.<jk>class</jk>)
525       *       .build();
526       *
527       *    <jc>// Create a session object.</jc>
528       *    <jc>// Needed because listeners are created per-session.</jc>
529       *    <jk>try</jk> (WriterSerializerSession <jv>session</jv> = <jv>serializer</jv>.createSession()) {
530       *
531       *       <jc>// Serialize a bean.</jc>
532       *       String <jv>json</jv> = <jv>session</jv>.write(<jk>new</jk> MyBean());
533       *
534       *       <jc>// Get the listener.</jc>
535       *       MySerializerListener <jv>listener</jv> = <jv>session</jv>.getListener(MySerializerListener.<jk>class</jk>);
536       *
537       *       <jc>// Dump the results to the console.</jc>
538       *       Json5.<jsf>DEFAULT</jsf>.println(<jv>listener</jv>.<jf>events</jf>);
539       *    }
540       * </p>
541       *
542       * <p>
543       * <b>Token streams:</b> this setting is not honored by {@link TokenWritable#writeTokens(Object) writeTokens}
544       * &mdash; listeners are POJO-level observers with no token-layer analog.
545       *
546       * @param value
547       *    The new value for this property.
548       *    <br>Can be <jk>null</jk> (no listener will be used, listener methods will not be called).
549       * @return This object.
550       */
551      public SELF listener(Class<? extends SerializerListener> value) {
552         listener = value;
553         return self();
554      }
555
556      /**
557       * Specifies the media type that this serializer produces.
558       *
559       * @param value The value for this setting.
560       *    <br>Can be <jk>null</jk> (clears the produces media type).
561       * @return This object.
562       */
563      public SELF produces(String value) {
564         produces = value;
565         return self();
566      }
567
568      /**
569       * Sort arrays and collections alphabetically.
570       *
571       * <p>
572       * When enabled, copies and sorts the contents of arrays and collections before serializing them.
573       *
574       * <p>
575       * Note that this introduces a performance penalty since it requires copying the existing collection.
576       *
577       * <h5 class='section'>Example:</h5>
578       * <p class='bjava'>
579       *    <jc>// Create a serializer that sorts arrays and collections before serialization.</jc>
580       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
581       *       .<jsm>create</jsm>()
582       *       .sortCollections()
583       *       .build();
584       *
585       *    <jc>// An unsorted array</jc>
586       *    String[] <jv>myArray</jv> = {<js>"foo"</js>,<js>"bar"</js>,<js>"baz"</js>};
587       *
588       *    <jc>// Produces ["bar","baz","foo"]</jc>
589       *    String <jv>json</jv> = <jv>serializer</jv>.write(<jv>myArray</jv>);
590       * </p>
591       *
592       * <p>
593       * <b>Token streams:</b> the cursor's raw structural methods emit elements in the order the
594       * caller emits them.  This setting IS honored by
595       * {@link TokenWriter#object(Object) TokenWriter.object(Object)}
596       * when walking a {@link Collection} or array.
597       *
598       * @return This object.
599       */
600      public SELF sortCollections() {
601         return sortCollections(true);
602      }
603
604      /**
605       * Same as {@link #sortCollections()} but allows you to explicitly specify the value.
606       *
607       * @param value The value for this setting.
608       * @return This object.
609       */
610      public SELF sortCollections(boolean value) {
611         sortCollections = value;
612         return self();
613      }
614
615      /**
616       * Sort maps alphabetically.
617       *
618       * <p>
619       * When enabled, copies and sorts the contents of maps by their keys before serializing them.
620       *
621       * <p>
622       * Note that this introduces a performance penalty.
623       *
624       * <h5 class='section'>Example:</h5>
625       * <p class='bjava'>
626       *    <jc>// Create a serializer that sorts maps before serialization.</jc>
627       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
628       *       .<jsm>create</jsm>()
629       *       .sortMaps()
630       *       .build();
631       *
632       *    <jc>// An unsorted map.</jc>
633       *    JsonMap <jv>myMap</jv> = JsonMap.<jsm>of</jsm>(<js>"foo"</js>,1,<js>"bar"</js>,2,<js>"baz"</js>,3);
634       *
635       *    <jc>// Produces {"bar":2,"baz":3,"foo":1}</jc>
636       *    String <jv>json</jv> = <jv>serializer</jv>.write(<jv>myMap</jv>);
637       * </p>
638       *
639       * <p>
640       * <b>Token streams:</b> the cursor's raw structural methods emit map / bean entries in the
641       * order the caller emits them.  This setting IS honored by
642       * {@link TokenWriter#object(Object) TokenWriter.object(Object)}
643       * when walking a {@link Map} or bean.
644       *
645       * @return This object.
646       */
647      public SELF sortMaps() {
648         return sortMaps(true);
649      }
650
651      /**
652       * Same as {@link #sortMaps()} but allows you to explicitly specify the value.
653       *
654       * @param value The value for this setting.
655       * @return This object.
656       */
657      public SELF sortMaps(boolean value) {
658         sortMaps = value;
659         return self();
660      }
661
662      /**
663       * Trim empty lists and arrays.
664       *
665       * <p>
666       * When enabled, empty lists and arrays will not be serialized.
667       *
668       * <p>
669       * Note that enabling this setting has the following effects on parsing:
670       * <ul class='spaced-list'>
671       *    <li>
672       *       Map entries with empty list values will be lost.
673       *    <li>
674       *       Bean properties with empty list values will not be set.
675       * </ul>
676       *
677       * <h5 class='section'>Example:</h5>
678       * <p class='bjava'>
679       *    <jc>// Create a serializer that skips empty arrays and collections.</jc>
680       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
681       *       .<jsm>create</jsm>()
682       *       .trimEmptyCollections()
683       *       .build();
684       *
685       *    <jc>// A bean with a field with an empty array.</jc>
686       *    <jk>public class</jk> MyBean {
687       *       <jk>public</jk> String[] <jf>foo</jf> = {};
688       *    }
689       *
690       *    <jc>// Produces {}</jc>
691       *    String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean());
692       * </p>
693       *
694       * <p>
695       * <b>Token streams:</b> the cursor's raw structural methods emit what the caller asks for.
696       * This setting IS honored by
697       * {@link TokenWriter#object(Object) TokenWriter.object(Object)}
698       * during a bean walk &mdash; bean / map fields whose value is an empty collection or array
699       * are skipped.
700       *
701       * @return This object.
702       */
703      public SELF trimEmptyCollections() {
704         return trimEmptyCollections(true);
705      }
706
707      /**
708       * Same as {@link #trimEmptyCollections()} but allows you to explicitly specify the value.
709       *
710       * @param value The value for this setting.
711       * @return This object.
712       */
713      public SELF trimEmptyCollections(boolean value) {
714         trimEmptyCollections = value;
715         return self();
716      }
717
718      /**
719       * Trim empty maps.
720       *
721       * <p>
722       * When enabled, empty map values will not be serialized to the output.
723       *
724       * <p>
725       * Note that enabling this setting has the following effects on parsing:
726       * <ul class='spaced-list'>
727       *    <li>
728       *       Bean properties with empty map values will not be set.
729       * </ul>
730       *
731       * <h5 class='section'>Example:</h5>
732       * <p class='bjava'>
733       *    <jc>// Create a serializer that skips empty maps.</jc>
734       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
735       *       .<jsm>create</jsm>()
736       *       .trimEmptyMaps()
737       *       .build();
738       *
739       *    <jc>// A bean with a field with an empty map.</jc>
740       *    <jk>public class</jk> MyBean {
741       *       <jk>public</jk> JsonMap <jf>foo</jf> = JsonMap.<jsm>of</jsm>();
742       *    }
743       *
744       *    <jc>// Produces {}</jc>
745       *    String <jv>json</jv> = <jv>serializer</jv>.write(<jk>new</jk> MyBean());
746       * </p>
747       *
748       * <p>
749       * <b>Token streams:</b> the cursor's raw structural methods emit what the caller asks for.
750       * This setting IS honored by
751       * {@link TokenWriter#object(Object) TokenWriter.object(Object)}
752       * during a bean walk &mdash; bean / map fields whose value is an empty map are skipped.
753       *
754       * @return This object.
755       */
756      public SELF trimEmptyMaps() {
757         return trimEmptyMaps(true);
758      }
759
760      /**
761       * Same as {@link #trimEmptyMaps()} but allows you to explicitly specify the value.
762       *
763       * @param value The value for this setting.
764       * @return This object.
765       */
766      public SELF trimEmptyMaps(boolean value) {
767         trimEmptyMaps = value;
768         return self();
769      }
770
771      /**
772       * Trim strings.
773       *
774       * <p>
775       * When enabled, string values will be trimmed of whitespace using {@link String#trim()} before being serialized.
776       *
777       * <h5 class='section'>Example:</h5>
778       * <p class='bjava'>
779       *    <jc>// Create a serializer that trims strings before serialization.</jc>
780       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
781       *       .<jsm>create</jsm>()
782       *       .trimStrings()
783       *       .build();
784       *
785       * <jc>// A map with space-padded keys/values</jc>
786       *    JsonMap <jv>myMap</jv> = JsonMap.<jsm>of</jsm>(<js>" foo "</js>, <js>" bar "</js>);
787       *
788       *    <jc>// Produces "{foo:'bar'}"</jc>
789       *    String <jv>json</jv> = <jv>serializer</jv>.toString(<jv>myMap</jv>);
790       * </p>
791       *
792       * @return This object.
793       */
794      public SELF trimStrings() {
795         return trimStrings(true);
796      }
797
798      /**
799       * Same as {@link #trimStrings()} but allows you to explicitly specify the value.
800       *
801       * @param value The value for this setting.
802       * @return This object.
803       */
804      public SELF trimStrings(boolean value) {
805         trimStrings = value;
806         return self();
807      }
808
809      /**
810       * URI context bean.
811       *
812       * <p>
813       * Bean used for resolution of URIs to absolute or root-relative form.
814       *
815       * <h5 class='section'>Example:</h5>
816       * <p class='bjava'>
817       *    <jc>// Our URI contextual information.</jc>
818       *    String <jv>authority</jv> = <js>"http://localhost:10000"</js>;
819       *    String <jv>contextRoot</jv> = <js>"/myContext"</js>;
820       *    String <jv>servletPath</jv> = <js>"/myServlet"</js>;
821       *    String <jv>pathInfo</jv> = <js>"/foo"</js>;
822       *
823       *    <jc>// Create a UriContext object.</jc>
824       *    UriContext <jv>uriContext</jv> = <jk>new</jk> UriContext(<jv>authority</jv>, <jv>contextRoot</jv>, <jv>servletPath</jv>, <jv>pathInfo</jv>);
825       *
826       *    <jc>// Associate it with our serializer.</jc>
827       *    WriterSerializer <jv>serializer</jv> = JsonSerializer
828       *       .<jsm>create</jsm>()
829       *       .uriContext(<jv>uriContext</jv>)
830       *       .uriRelativity(<jsf>RESOURCE</jsf>)  <jc>// Assume relative paths are relative to servlet.</jc>
831       *       .uriResolution(<jsf>ABSOLUTE</jsf>)  <jc>// Serialize URLs as absolute paths.</jc>
832       *       .build();
833       *
834       *    <jc>// A relative URL</jc>
835       *    URL <jv>myUrl</jv> = <jk>new</jk> URL(<js>"bar"</js>);
836       *
837       *    <jc>// Produces "http://localhost:10000/myContext/myServlet/foo/bar"</jc>
838       *    String <jv>json</jv> = <jv>serializer</jv>.toString(<jv>myUrl</jv>);
839       * </p>
840       *
841       * <h5 class='section'>See Also:</h5><ul>
842       *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a>
843       * </ul>
844       *
845       * <p>
846       * <b>Token streams:</b> this setting is not honored by {@link TokenWritable#writeTokens(Object) writeTokens}
847       * &mdash; URI rewriting is a databind concern; the token layer emits string values verbatim.
848       *
849       * @param value The new value for this property.
850       *    <br>Can be <jk>null</jk> (defaults to <c>UriContext.DEFAULT</c>).
851       * @return This object.
852       */
853      public SELF uriContext(UriContext value) {
854         uriContext = value;
855         return self();
856      }
857
858      /**
859       * URI relativity.
860       *
861       * <p>
862       * Defines what relative URIs are relative to when serializing any of the following:
863       * <ul>
864       *    <li>{@link URI}
865       *    <li>{@link URL}
866       *    <li>Properties and classes annotated with {@link Uri @Uri}
867       * </ul>
868       *
869       * <p>
870       * See {@link #uriContext(UriContext)} for examples.
871       *
872       * <ul class='values javatree'>
873       *    <li class='jf'>{@link UriRelativity#RESOURCE}
874       *       - Relative URIs should be considered relative to the servlet URI.
875       *    <li class='jf'>{@link UriRelativity#PATH_INFO}
876       *       - Relative URIs should be considered relative to the request URI.
877       * </ul>
878       *
879       * <h5 class='section'>See Also:</h5><ul>
880       *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a>
881       * </ul>
882       *
883       * <p>
884       * <b>Token streams:</b> this setting is not honored by {@link TokenWritable#writeTokens(Object) writeTokens}
885       * &mdash; URI rewriting is a databind concern.
886       *
887       * @param value
888       *    The new value for this property.
889       *    <br>The default is {@link UriRelativity#RESOURCE}
890       *    <br>Can be <jk>null</jk> (defaults to <c>UriRelativity.RESOURCE</c>).
891       * @return This object.
892       */
893      public SELF uriRelativity(UriRelativity value) {
894         uriRelativity = value;
895         return self();
896      }
897
898      /**
899       * URI resolution.
900       *
901       * <p>
902       * Defines the resolution level for URIs when serializing any of the following:
903       * <ul>
904       *    <li>{@link URI}
905       *    <li>{@link URL}
906       *    <li>Properties and classes annotated with {@link Uri @Uri}
907       * </ul>
908       *
909       * <p>
910       * See {@link #uriContext(UriContext)} for examples.
911       *
912       * <ul class='values javatree'>
913       *    <li class='jf'>{@link UriResolution#ABSOLUTE}
914       *       - Resolve to an absolute URL (e.g. <js>"http://host:port/context-root/servlet-path/path-info"</js>).
915       *    <li class='jf'>{@link UriResolution#ROOT_RELATIVE}
916       *       - Resolve to a root-relative URL (e.g. <js>"/context-root/servlet-path/path-info"</js>).
917       *    <li class='jf'>{@link UriResolution#NONE}
918       *       - Don't do any URL resolution.
919       * </ul>
920       *
921       * <h5 class='section'>See Also:</h5><ul>
922       *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/MarshallingUris">URIs</a>
923       * </ul>
924       *
925       * <p>
926       * <b>Token streams:</b> this setting is not honored by {@link TokenWritable#writeTokens(Object) writeTokens}
927       * &mdash; URI rewriting is a databind concern.
928       *
929       * @param value
930       *    The new value for this property.
931       *    <br>The default is {@link UriResolution#NONE}
932       *    <br>Can be <jk>null</jk> (defaults to <c>UriResolution.NONE</c>).
933       * @return This object.
934       */
935      public SELF uriResolution(UriResolution value) {
936         uriResolution = value;
937         return self();
938      }
939
940
941   }
942
943   /**
944    * Concrete default builder leaf for the non-subclassed {@link Serializer#create()} / {@link Serializer#copy()} path.
945    */
946   public static final class DefaultBuilder extends Builder<DefaultBuilder> {
947
948      DefaultBuilder() {}
949
950      DefaultBuilder(Serializer copyFrom) {
951         super(copyFrom);
952      }
953
954      DefaultBuilder(Builder<?> copyFrom) {
955         super(copyFrom);
956      }
957
958      @Override /* Overridden from Context.Builder<?> */
959      public DefaultBuilder copy() {
960         return new DefaultBuilder(this);
961      }
962   }
963
964   /**
965    * Represents no Serializer.
966    */
967   public abstract static class Null extends Serializer {
968      @SuppressWarnings({
969         "java:S1186" // Constructor required by Serializer parent class, even though Null is abstract and never instantiated directly
970      })
971      private Null(Builder<?> builder) {
972         super(builder);
973      }
974   }
975
976   /**
977    * Creates a new builder for this object.
978    *
979    * @return A new builder.
980    */
981   @SuppressWarnings({
982      "java:S1452" // Self-typed builder: Builder<?> is the only non-raw, leaf-free return; chaining + build() unaffected.
983   })
984   public static Builder<?> create() {
985      return new DefaultBuilder();
986   }
987
988   /**
989    * Instantiates a builder of the specified serializer class.
990    *
991    * <p>
992    * Looks for a public static method called <c>create</c> that returns an object that can be passed into a public
993    * or protected constructor of the class.
994    *
995    * @param c The serializer class to create a builder for.
996    *    <br>Must not be <jk>null</jk>.
997    * @return A new builder.
998    */
999   public static Builder createSerializerBuilder(Class<? extends Serializer> c) {
1000      return (Builder)Context.createBuilder(c);
1001   }
1002
1003   protected final boolean addBeanTypes;
1004   protected final boolean addRootType;
1005   protected final boolean keepNullProperties;
1006   protected final boolean nonDefault;
1007   protected final boolean sortCollections;
1008   protected final boolean sortMaps;
1009   protected final boolean trimEmptyCollections;
1010   protected final boolean trimEmptyMaps;
1011   protected final boolean trimStrings;
1012   protected final Class<? extends SerializerListener> listener;
1013   protected final String accept;
1014   protected final String produces;
1015   private final UriContext uriContext;
1016   private final UriRelativity uriRelativity;
1017   private final UriResolution uriResolution;
1018   private final MediaRanges acceptRanges;
1019   private final List<MediaType> acceptMediaTypes;
1020   private final MediaType producesMediaType;
1021
1022   /**
1023    * Constructor
1024    *
1025    * @param builder The builder for this object.
1026    *    <br>Cannot be <jk>null</jk>.
1027    */
1028   protected Serializer(Builder<?> builder) {
1029      super(builder);
1030
1031      accept = builder.accept;
1032      addBeanTypes = builder.addBeanTypes;
1033      addRootType = builder.addRootType;
1034      keepNullProperties = builder.keepNullProperties;
1035      listener = builder.listener;
1036      nonDefault = builder.nonDefault;
1037      produces = builder.produces;
1038      sortCollections = builder.sortCollections;
1039      sortMaps = builder.sortMaps;
1040      trimEmptyCollections = builder.trimEmptyCollections;
1041      trimEmptyMaps = builder.trimEmptyMaps;
1042      trimStrings = builder.trimStrings;
1043      uriContext = builder.uriContext;
1044      uriRelativity = builder.uriRelativity;
1045      uriResolution = builder.uriResolution;
1046
1047      this.producesMediaType = MediaType.of(produces);
1048      this.acceptRanges = MediaRanges.of(nn(accept) ? accept : produces);
1049      this.acceptMediaTypes = u(l(nn(builder.accept) ? MediaType.ofAll(splita(builder.accept)) : new MediaType[]{this.producesMediaType}));
1050   }
1051
1052   @Override /* Overridden from Context */
1053   public Builder<?> copy() {
1054      return new DefaultBuilder(this);
1055   }
1056
1057   @Override /* Overridden from Context */
1058   public SerializerSession.Builder<?> createSession() {
1059      return SerializerSession.create(this);
1060   }
1061
1062   /**
1063    * Performs an action on the media types handled based on the value of the <c>accept</c> parameter passed into the constructor.
1064    *
1065    * <p>
1066    * The order of the media types are the same as those in the <c>accept</c> parameter.
1067    *
1068    * @param action The action to perform on the media types.
1069    *    <br>Must not be <jk>null</jk>.
1070    * @return This object.
1071    */
1072   public final Serializer forEachAcceptMediaType(Consumer<MediaType> action) {
1073      for (var m : acceptMediaTypes)
1074         action.accept(m);
1075      return this;
1076   }
1077
1078   /**
1079    * Returns the media types handled based on the value of the <c>accept</c> parameter passed into the constructor.
1080    *
1081    * <p>
1082    * Note that the order of these ranges are from high to low q-value.
1083    *
1084    * @return The list of media types.  Never <jk>null</jk>.
1085    */
1086   public final MediaRanges getMediaTypeRanges() { return acceptRanges; }
1087
1088   /**
1089    * Returns the first entry in the <c>accept</c> parameter passed into the constructor.
1090    *
1091    * <p>
1092    * This signifies the 'primary' media type for this serializer.
1093    *
1094    * @return The media type.  Never <jk>null</jk>.
1095    */
1096   public final MediaType getPrimaryMediaType() { return first(acceptMediaTypes).orElseThrow(() -> new IllegalStateException("No accept media types available")); }
1097
1098   /**
1099    * Optional method that returns the response <c>Content-Type</c> for this serializer if it is different from
1100    * the matched media type.
1101    *
1102    * <p>
1103    * This method is specified to override the content type for this serializer.
1104    * For example, the {@link Json5Serializer} class returns that it handles media type
1105    * <js>"text/json5"</js>, but returns <js>"text/json"</js> as the actual content type.
1106    * This allows clients to request specific 'flavors' of content using specialized <c>Accept</c> header values.
1107    *
1108    * <p>
1109    * This method is typically meaningless if the serializer is being used stand-alone (i.e. outside of a REST server
1110    * or client).
1111    *
1112    * @return The response content type.  If <jk>null</jk>, then the matched media type is used.
1113    */
1114   public final MediaType getResponseContentType() { return producesMediaType; }
1115
1116   /**
1117    * Optional method that specifies HTTP request headers for this serializer.
1118    *
1119    * <p>
1120    * For example, {@link SoapXmlSerializer} needs to set a <c>SOAPAction</c> header.
1121    *
1122    * <p>
1123    * This method is typically meaningless if the serializer is being used stand-alone (i.e. outside of a REST server
1124    * or client).
1125    *
1126    * @param session The current session.
1127    * @return
1128    *    The HTTP headers to set on HTTP requests.
1129    *    Never <jk>null</jk>.
1130    */
1131   public Map<String,String> getResponseHeaders(SerializerSession session) {
1132      return emptyMap();
1133   }
1134
1135   @Override /* Overridden from Context */
1136   public SerializerSession getSession() { return createSession().build(); }
1137
1138   /**
1139    * Returns <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}.
1140    *
1141    * @return <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}.
1142    */
1143   public boolean isWriterSerializer() { return false; }
1144
1145   /**
1146    * Shortcut method for serializing objects directly to either a <c>String</c> or <code><jk>byte</jk>[]</code>
1147    * depending on the serializer type.
1148    *
1149    * @param o The object to serialize.
1150    * @return
1151    *    The serialized object.
1152    *    <br>Character-based serializers will return a <c>String</c>
1153    *    <br>Stream-based serializers will return a <code><jk>byte</jk>[]</code>
1154    * @throws SerializeException If a problem occurred trying to convert the output.
1155    */
1156   public Object write(Object o) throws SerializeException {
1157      return getSession().write(o);
1158   }
1159
1160   /**
1161    * Serializes a POJO to the specified output stream or writer.
1162    *
1163    * <p>
1164    * Equivalent to calling <c>serializer.createSession().write(o, output);</c>
1165    *
1166    * @param o The object to serialize.
1167    * @param output
1168    *    The output object.
1169    *    <br>Character-based serializers can handle the following output class types:
1170    *    <ul>
1171    *       <li>{@link Writer}
1172    *       <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream.
1173    *    </ul>
1174    *    <br>Stream-based serializers can handle the following output class types:
1175    *    <ul>
1176    *       <li>{@link OutputStream}
1177    *    </ul>
1178    * @throws SerializeException If a problem occurred trying to convert the output.
1179    * @throws IOException Thrown by the underlying stream.
1180    */
1181   public final void write(Object o, Object output) throws SerializeException, IOException {
1182      getSession().write(o, output);
1183   }
1184
1185   /**
1186    * Convenience method for serializing an object to a String.
1187    *
1188    * <p>
1189    * For writer-based serializers, this is identical to calling {@link #write(Object)}.
1190    * <br>For stream-based serializers, this converts the returned byte array to a string based on
1191    * the {@link OutputStreamSerializer.Builder#binaryFormat(BinaryFormat)} setting.
1192    *
1193    * @param o The object to serialize.
1194    * @return The output serialized to a string.
1195    * @throws SerializeException If a problem occurred trying to convert the output.
1196    */
1197   public final String writeToString(Object o) throws SerializeException {
1198      return getSession().writeToString(o);
1199   }
1200
1201   /**
1202    * Serializes a POJO to the specified pipe.
1203    *
1204    * @param session The current session.
1205    * @param pipe Where to send the output from the serializer.
1206    * @param o The object to serialize.
1207    * @throws IOException Thrown by underlying stream.
1208    * @throws SerializeException Problem occurred trying to serialize object.
1209    */
1210   protected void doWrite(SerializerSession session, SerializerPipe pipe, Object o) throws IOException, SerializeException {
1211      throw uoex();
1212   }
1213
1214   /**
1215    * Serializer listener.
1216    *
1217    * @see Serializer.Builder#listener(Class)
1218    * @return
1219    *    Class used to listen for errors and warnings that occur during serialization.
1220    */
1221   protected final Class<? extends SerializerListener> getListener() { return listener; }
1222
1223   /**
1224    * URI context bean.
1225    *
1226    * @see Serializer.Builder#uriContext(UriContext)
1227    * @return
1228    *    Bean used for resolution of URIs to absolute or root-relative form.
1229    */
1230   protected final UriContext getUriContext() { return uriContext; }
1231
1232   /**
1233    * URI relativity.
1234    *
1235    * @see Serializer.Builder#uriRelativity(UriRelativity)
1236    * @return
1237    *    Defines what relative URIs are relative to when serializing any of the following:
1238    */
1239   protected final UriRelativity getUriRelativity() { return uriRelativity; }
1240
1241   /**
1242    * URI resolution.
1243    *
1244    * @see Serializer.Builder#uriResolution(UriResolution)
1245    * @return
1246    *    Defines the resolution level for URIs when serializing URIs.
1247    */
1248   protected final UriResolution getUriResolution() { return uriResolution; }
1249
1250   /**
1251    * Add <js>"_type"</js> properties when needed.
1252    *
1253    * @see Serializer.Builder#addBeanTypes()
1254    * @return
1255    *    <jk>true</jk> if <js>"_type"</js> properties added to beans if their type cannot be inferred
1256    *    through reflection.
1257    */
1258   protected boolean isAddBeanTypes() { return addBeanTypes; }
1259
1260   /**
1261    * Add type attribute to root nodes.
1262    *
1263    * @see Serializer.Builder#addRootType()
1264    * @return
1265    *    <jk>true</jk> if type property should be added to root node.
1266    */
1267   protected final boolean isAddRootType() { return addRootType; }
1268
1269   /**
1270    * Don't trim null bean property values.
1271    *
1272    * @see Serializer.Builder#keepNullProperties()
1273    * @return
1274    *    <jk>true</jk> if null bean values are serialized to the output.
1275    */
1276   protected final boolean isKeepNullProperties() { return keepNullProperties; }
1277
1278   /**
1279    * Don't serialize bean properties whose values equal the default for their type.
1280    *
1281    * @see Serializer.Builder#nonDefault()
1282    * @return
1283    *    <jk>true</jk> if bean properties whose value equals the type / bean-constructed default are omitted.
1284    * @since 10.0.0
1285    */
1286   protected final boolean isNonDefault() { return nonDefault; }
1287
1288   /**
1289    * Sort arrays and collections alphabetically.
1290    *
1291    * @see Serializer.Builder#sortCollections()
1292    * @return
1293    *    <jk>true</jk> if arrays and collections are copied and sorted before serialization.
1294    */
1295   protected final boolean isSortCollections() { return sortCollections; }
1296
1297   /**
1298    * Sort maps alphabetically.
1299    *
1300    * @see Serializer.Builder#sortMaps()
1301    * @return
1302    *    <jk>true</jk> if maps are copied and sorted before serialization.
1303    */
1304   protected final boolean isSortMaps() { return sortMaps; }
1305
1306   /**
1307    * Trim empty lists and arrays.
1308    *
1309    * @see Serializer.Builder#trimEmptyCollections()
1310    * @return
1311    *    <jk>true</jk> if empty lists and arrays are not serialized to the output.
1312    */
1313   protected final boolean isTrimEmptyCollections() { return trimEmptyCollections; }
1314
1315   /**
1316    * Trim empty maps.
1317    *
1318    * @see Serializer.Builder#trimEmptyMaps()
1319    * @return
1320    *    <jk>true</jk> if empty map values are not serialized to the output.
1321    */
1322   protected final boolean isTrimEmptyMaps() { return trimEmptyMaps; }
1323
1324   /**
1325    * Trim strings.
1326    *
1327    * @see Serializer.Builder#trimStrings()
1328    * @return
1329    *    <jk>true</jk> if string values will be trimmed of whitespace using {@link String#trim()} before being serialized.
1330    */
1331   protected final boolean isTrimStrings() { return trimStrings; }
1332
1333   @Override /* Overridden from MarshallingTraverseContext */
1334   protected FluentMap<String,Object> properties() {
1335      return super.properties()
1336         .a("addBeanTypes", addBeanTypes)
1337         .a("addRootType", addRootType)
1338         .a("keepNullProperties", keepNullProperties)
1339         .a("listener", listener)
1340         .a("nonDefault", nonDefault)
1341         .a("sortCollections", sortCollections)
1342         .a("sortMaps", sortMaps)
1343         .a("trimEmptyCollections", trimEmptyCollections)
1344         .a("trimEmptyMaps", trimEmptyMaps)
1345         .a("trimStrings", trimStrings)
1346         .a("uriContext", uriContext)
1347         .a("uriRelativity", uriRelativity)
1348         .a("uriResolution", uriResolution);
1349   }
1350}