001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.serializer;
014
015import java.io.*;
016
017import org.apache.juneau.*;
018import org.apache.juneau.annotation.*;
019import org.apache.juneau.collections.*;
020import org.apache.juneau.http.*;
021import org.apache.juneau.internal.*;
022
023/**
024 * Parent class for all Juneau serializers.
025 *
026 * <h5 class='topic'>Description</h5>
027 *
028 * Base serializer class that serves as the parent class for all serializers.
029 *
030 * <p>
031 * The purpose of this class is:
032 * <ul>
033 *    <li>Maintain a read-only configuration state of a serializer.
034 *    <li>Create session objects used for serializing POJOs (i.e. {@link SerializerSession}).
035 *    <li>Provide convenience methods for serializing POJOs without having to construct session objects.
036 * </ul>
037 *
038 * <p>
039 * Subclasses should extend directly from {@link OutputStreamSerializer} or {@link WriterSerializer} depending on
040 * whether it's a stream or character based serializer.
041 */
042@ConfigurableContext
043public abstract class Serializer extends BeanTraverseContext {
044
045   /**
046    * Represents no Serializer.
047    */
048   public static abstract class Null extends Serializer {
049      private Null(PropertyStore ps, String produces, String accept) {
050         super(ps, produces, accept);
051      }
052   }
053
054   //-------------------------------------------------------------------------------------------------------------------
055   // Configurable properties
056   //-------------------------------------------------------------------------------------------------------------------
057
058   static final String PREFIX = "Serializer";
059
060   /**
061    * Configuration property:  Add <js>"_type"</js> properties when needed.
062    *
063    * <h5 class='section'>Property:</h5>
064    * <ul class='spaced-list'>
065    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_addBeanTypes SERIALIZER_addBeanTypes}
066    *    <li><b>Name:</b>  <js>"Serializer.addBeanTypes.b"</js>
067    *    <li><b>Data type:</b>  <jk>boolean</jk>
068    *    <li><b>System property:</b>  <c>Serializer.addBeanTypes</c>
069    *    <li><b>Environment variable:</b>  <c>SERIALIZER_ADDBEANTYPES</c>
070    *    <li><b>Default:</b>  <jk>false</jk>
071    *    <li><b>Session property:</b>  <jk>false</jk>
072    *    <li><b>Annotations:</b>
073    *       <ul>
074    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#addBeanTypes()}
075    *       </ul>
076    *    <li><b>Methods:</b>
077    *       <ul>
078    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#addBeanTypes()}
079    *       </ul>
080    * </ul>
081    *
082    * <h5 class='section'>Description:</h5>
083    *
084    * <p>
085    * When enabled, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred
086    * through reflection.
087    *
088    * <p>
089    * This is used to recreate the correct objects during parsing if the object types cannot be inferred.
090    * <br>For example, when serializing a <c>Map&lt;String,Object&gt;</c> field where the bean class cannot be determined from
091    * the type of the values.
092    *
093    * <p>
094    * Note the differences between the following settings:
095    * <ul class='javatree'>
096    *    <li class='jf'>{@link #SERIALIZER_addRootType} - Affects whether <js>'_type'</js> is added to root node.
097    *    <li class='jf'>{@link #SERIALIZER_addBeanTypes} - Affects whether <js>'_type'</js> is added to any nodes.
098    * </ul>
099    *
100    * <h5 class='section'>Example:</h5>
101    * <p class='bcode w800'>
102    *    <jc>// Create a serializer that adds _type to nodes.</jc>
103    *    WriterSerializer s = JsonSerializer
104    *       .<jsm>create</jsm>()
105    *       .addBeanTypes()
106    *       .build();
107    *
108    *    <jc>// Same, but use property.</jc>
109    *    WriterSerializer s = JsonSerializer
110    *       .<jsm>create</jsm>()
111    *       .set(<jsf>SERIALIZER_addBeanTypes</jsf>, <jk>true</jk>)
112    *       .build();
113    *
114    *    <jc>// Our map of beans to serialize.</jc>
115    *    <ja>@Bean</ja>(typeName=<js>"mybean"</js>)
116    *    <jk>public class</jk> MyBean {
117    *       <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
118    *    }
119    *    OMap map = OMap.of(<js>"foo"</js>, <jk>new</jk> MyBean());
120    *
121    *    <jc>// Will contain:  {"foo":{"_type":"mybean","foo":"bar"}}</jc>
122    *    String json = s.serialize(map);
123    * </p>
124    */
125   public static final String SERIALIZER_addBeanTypes = PREFIX + ".addBeanTypes.b";
126
127   /**
128    * Configuration property:  Add type attribute to root nodes.
129    *
130    * <h5 class='section'>Property:</h5>
131    * <ul class='spaced-list'>
132    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_addRootType SERIALIZER_addRootType}
133    *    <li><b>Name:</b>  <js>"Serializer.addRootType.b"</js>
134    *    <li><b>Data type:</b>  <jk>boolean</jk>
135    *    <li><b>System property:</b>  <c>Serializer.addRootType</c>
136    *    <li><b>Environment variable:</b>  <c>SERIALIZER_ADDROOTTYPE</c>
137    *    <li><b>Default:</b>  <jk>false</jk>
138    *    <li><b>Session property:</b>  <jk>false</jk>
139    *    <li><b>Annotations:</b>
140    *       <ul>
141    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#addRootType()}
142    *       </ul>
143    *    <li><b>Methods:</b>
144    *       <ul>
145    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#addRootType()}
146    *       </ul>
147    * </ul>
148    *
149    * <h5 class='section'>Description:</h5>
150    *
151    * <p>
152    * When enabled, <js>"_type"</js> properties will be added to top-level beans.
153    *
154    * <p>
155    * When disabled, it is assumed that the parser knows the exact Java POJO type being parsed, and therefore top-level
156    * type information that might normally be included to determine the data type will not be serialized.
157    *
158    * <p>
159    * For example, when serializing a top-level POJO with a {@link Bean#typeName() @Bean(typeName)} value, a
160    * <js>'_type'</js> attribute will only be added when this setting is enabled.
161    *
162    * <p>
163    * Note the differences between the following settings:
164    * <ul class='javatree'>
165    *    <li class='jf'>{@link #SERIALIZER_addRootType} - Affects whether <js>'_type'</js> is added to root node.
166    *    <li class='jf'>{@link #SERIALIZER_addBeanTypes} - Affects whether <js>'_type'</js> is added to any nodes.
167    * </ul>
168    *
169    * <h5 class='section'>Example:</h5>
170    * <p class='bcode w800'>
171    *    <jc>// Create a serializer that adds _type to root node.</jc>
172    *    WriterSerializer s = JsonSerializer
173    *       .<jsm>create</jsm>()
174    *       .addRootType()
175    *       .build();
176    *
177    *    <jc>// Same, but use property.</jc>
178    *    WriterSerializer s = JsonSerializer
179    *       .<jsm>create</jsm>()
180    *       .set(<jsf>SERIALIZER_addRootType</jsf>, <jk>true</jk>)
181    *       .build();
182    *
183    *    <jc>// Our bean to serialize.</jc>
184    *    <ja>@Bean</ja>(typeName=<js>"mybean"</js>)
185    *    <jk>public class</jk> MyBean {
186    *       <jk>public</jk> String <jf>foo</jf> = <js>"bar"</js>;
187    *    }
188    *
189    *    <jc>// Will contain:  {"_type":"mybean","foo":"bar"}</jc>
190    *    String json = s.serialize(<jk>new</jk> MyBean());
191    * </p>
192    */
193   public static final String SERIALIZER_addRootType = PREFIX + ".addRootType.b";
194
195   /**
196    * Configuration property:  Serializer listener.
197    *
198    * <h5 class='section'>Property:</h5>
199    * <ul class='spaced-list'>
200    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_listener SERIALIZER_listener}
201    *    <li><b>Name:</b>  <js>"Serializer.listener.c"</js>
202    *    <li><b>Data type:</b>  <c>Class&lt;{@link org.apache.juneau.serializer.SerializerListener}&gt;</c>
203    *    <li><b>Default:</b>  <jk>null</jk>
204    *    <li><b>Session property:</b>  <jk>false</jk>
205    *    <li><b>Annotations:</b>
206    *       <ul>
207    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#listener()}
208    *       </ul>
209    *    <li><b>Methods:</b>
210    *       <ul>
211    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#listener(Class)}
212    *       </ul>
213    * </ul>
214    *
215    * <h5 class='section'>Description:</h5>
216    *
217    * <p>
218    * Class used to listen for errors and warnings that occur during serialization.
219    *
220    * <h5 class='section'>Example:</h5>
221    * <p class='bcode w800'>
222    *    <jc>// Define our serializer listener.</jc>
223    *    <jc>// Simply captures all errors.</jc>
224    *    <jk>public class</jk> MySerializerListener <jk>extends</jk> SerializerListener {
225    *
226    *       <jc>// A simple property to store our events.</jc>
227    *       <jk>public</jk> List&lt;String&gt; <jf>events</jf> = <jk>new</jk> LinkedList&lt;&gt;();
228    *
229    *       <ja>@Override</ja>
230    *       <jk>public</jk> &lt;T&gt; <jk>void</jk> onError(SerializerSession session, Throwable t, String msg) {
231    *          <jf>events</jf>.add(session.getLastLocation() + <js>","</js> + msg + <js>","</js> + t);
232    *       }
233    *    }
234    *
235    *    <jc>// Create a serializer using our listener.</jc>
236    *    WriterSerializer s = JsonSerializer
237    *       .<jsm>create</jsm>()
238    *       .listener(MySerializerListener.<jk>class</jk>)
239    *       .build();
240    *
241    *    <jc>// Same, but use property.</jc>
242    *    WriterSerializer s = JsonSerializer
243    *       .<jsm>create</jsm>()
244    *       .set(<jsf>SERIALIZER_listener</jsf>, MySerializerListener.<jk>class</jk>)
245    *       .build();
246    *
247    *    <jc>// Create a session object.</jc>
248    *    <jc>// Needed because listeners are created per-session.</jc>
249    *    <jk>try</jk> (WriterSerializerSession ss = s.createSession()) {
250    *
251    *       <jc>// Serialize a bean.</jc>
252    *       String json = ss.serialize(<jk>new</jk> MyBean());
253    *
254    *       <jc>// Get the listener.</jc>
255    *       MySerializerListener l = ss.getListener(MySerializerListener.<jk>class</jk>);
256    *
257    *       <jc>// Dump the results to the console.</jc>
258    *       SimpleJsonSerializer.<jsf>DEFAULT</jsf>.println(l.<jf>events</jf>);
259    *    }
260    * </p>
261    */
262   public static final String SERIALIZER_listener = PREFIX + ".listener.c";
263
264   /**
265    * Configuration property:  Don't trim null bean property values.
266    *
267    * <h5 class='section'>Property:</h5>
268    * <ul class='spaced-list'>
269    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_keepNullProperties SERIALIZER_keepNullProperties}
270    *    <li><b>Name:</b>  <js>"Serializer.keepNullProperties.b"</js>
271    *    <li><b>Data type:</b>  <jk>boolean</jk>
272    *    <li><b>System property:</b>  <c>Serializer.keepNullProperties</c>
273    *    <li><b>Environment variable:</b>  <c>SERIALIZER_KEEPNULLPROPERTIES</c>
274    *    <li><b>Default:</b>  <jk>false</jk>
275    *    <li><b>Session property:</b>  <jk>false</jk>
276    *    <li><b>Annotations:</b>
277    *       <ul>
278    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#keepNullProperties()}
279    *       </ul>
280    *    <li><b>Methods:</b>
281    *       <ul>
282    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#keepNullProperties()}
283    *       </ul>
284    * </ul>
285    *
286    * <h5 class='section'>Description:</h5>
287    *
288    * <p>
289    * When enabled, null bean values will be serialized to the output.
290    *
291    * <ul class='notes'>
292    *    <li>Not enabling this setting will cause Maps with <jk>null</jk> values to be lost during parsing.
293    * </ul>
294    *
295    * <h5 class='section'>Example:</h5>
296    * <p class='bcode w800'>
297    *    <jc>// Create a serializer that serializes null properties.</jc>
298    *    WriterSerializer s = JsonSerializer
299    *       .<jsm>create</jsm>()
300    *       .keepNullProperties()
301    *       .build();
302    *
303    *    <jc>// Same, but use property.</jc>
304    *    WriterSerializer s = JsonSerializer
305    *       .<jsm>create</jsm>()
306    *       .set(<jsf>SERIALIZER_keepNullProperties</jsf>, <jk>true</jk>)
307    *       .build();
308    *
309    *    <jc>// Our bean to serialize.</jc>
310    *    <jk>public class</jk> MyBean {
311    *       <jk>public</jk> String <jf>foo</jf> = <jk>null</jk>;
312    *    }
313    *
314    *    <jc>// Will contain "{foo:null}".</jc>
315    *    String json = s.serialize(<jk>new</jk> MyBean());
316    * </p>
317    */
318   public static final String SERIALIZER_keepNullProperties = PREFIX + ".keepNullProperties.b";
319
320   /**
321    * Configuration property:  Sort arrays and collections alphabetically.
322    *
323    * <h5 class='section'>Property:</h5>
324    * <ul class='spaced-list'>
325    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_sortCollections SERIALIZER_sortCollections}
326    *    <li><b>Name:</b>  <js>"Serializer.sortCollections.b"</js>
327    *    <li><b>Data type:</b>  <jk>boolean</jk>
328    *    <li><b>System property:</b>  <c>Serializer.sortCollections</c>
329    *    <li><b>Environment variable:</b>  <c>SERIALIZER_SORTCOLLECTIONS</c>
330    *    <li><b>Default:</b>  <jk>false</jk>
331    *    <li><b>Session property:</b>  <jk>false</jk>
332    *    <li><b>Annotations:</b>
333    *       <ul>
334    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#sortCollections()}
335    *       </ul>
336    *    <li><b>Methods:</b>
337    *       <ul>
338    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#sortCollections()}
339    *       </ul>
340    * </ul>
341    *
342    * <h5 class='section'>Description:</h5>
343    *
344    * <p>
345    * When enabled, copies and sorts the contents of arrays and collections before serializing them.
346    *
347    * <p>
348    * Note that this introduces a performance penalty since it requires copying the existing collection.
349    *
350    * <h5 class='section'>Example:</h5>
351    * <p class='bcode w800'>
352    *    <jc>// Create a serializer that sorts arrays and collections before serialization.</jc>
353    *    WriterSerializer s = JsonSerializer
354    *       .<jsm>create</jsm>()
355    *       .sortCollections()
356    *       .build();
357    *
358    *    <jc>// Same, but use property.</jc>
359    *    WriterSerializer s = JsonSerializer
360    *       .<jsm>create</jsm>()
361    *       .set(<jsf>SERIALIZER_sortCollections</jsf>, <jk>true</jk>)
362    *       .build();
363    *
364    *    <jc>// An unsorted array</jc>
365    *    String[] array = {<js>"foo"</js>,<js>"bar"</js>,<js>"baz"</js>}
366    *
367    *    <jc>// Produces ["bar","baz","foo"]</jc>
368    *    String json = s.serialize(array);
369    * </p>
370    */
371   public static final String SERIALIZER_sortCollections = PREFIX + ".sortCollections.b";
372
373   /**
374    * Configuration property:  Sort maps alphabetically.
375    *
376    * <h5 class='section'>Property:</h5>
377    * <ul class='spaced-list'>
378    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_sortMaps SERIALIZER_sortMaps}
379    *    <li><b>Name:</b>  <js>"Serializer.sortMaps.b"</js>
380    *    <li><b>Data type:</b>  <jk>boolean</jk>
381    *    <li><b>System property:</b>  <c>Serializer.sortMaps</c>
382    *    <li><b>Environment variable:</b>  <c>SERIALIZER_SORTMAPS</c>
383    *    <li><b>Default:</b>  <jk>false</jk>
384    *    <li><b>Session property:</b>  <jk>false</jk>
385    *    <li><b>Annotations:</b>
386    *       <ul>
387    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#sortMaps()}
388    *       </ul>
389    *    <li><b>Methods:</b>
390    *       <ul>
391    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#sortMaps()}
392    *       </ul>
393    * </ul>
394    *
395    * <h5 class='section'>Description:</h5>
396    *
397    * <p>
398    * When enabled, copies and sorts the contents of maps by their keys before serializing them.
399    *
400    * <p>
401    * Note that this introduces a performance penalty.
402    *
403    * <h5 class='section'>Example:</h5>
404    * <p class='bcode w800'>
405    *    <jc>// Create a serializer that sorts maps before serialization.</jc>
406    *    WriterSerializer s = JsonSerializer
407    *       .<jsm>create</jsm>()
408    *       .sortMaps()
409    *       .build();
410    *
411    *    <jc>// Same, but use property.</jc>
412    *    WriterSerializer s = JsonSerializer
413    *       .<jsm>create</jsm>()
414    *       .set(<jsf>SERIALIZER_sortMaps</jsf>, <jk>true</jk>)
415    *       .build();
416    *
417    *    <jc>// An unsorted map.</jc>
418    *    OMap map = OMap.<jsm>of</jsm>(<js>"foo"</js>,1,<js>"bar"</js>,2,<js>"baz"</js>,3);
419    *
420    *    <jc>// Produces {"bar":2,"baz":3,"foo":1}</jc>
421    *    String json = s.serialize(map);
422    * </p>
423    */
424   public static final String SERIALIZER_sortMaps = PREFIX + ".sortMaps.b";
425
426   /**
427    * Configuration property:  Trim empty lists and arrays.
428    *
429    * <h5 class='section'>Property:</h5>
430    * <ul class='spaced-list'>
431    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_trimEmptyCollections SERIALIZER_trimEmptyCollections}
432    *    <li><b>Name:</b>  <js>"Serializer.trimEmptyCollections.b"</js>
433    *    <li><b>Data type:</b>  <jk>boolean</jk>
434    *    <li><b>System property:</b>  <c>Serializer.trimEmptyCollections</c>
435    *    <li><b>Environment variable:</b>  <c>SERIALIZER_TRIMEMPTYCOLLECTIONS</c>
436    *    <li><b>Default:</b>  <jk>false</jk>
437    *    <li><b>Session property:</b>  <jk>false</jk>
438    *    <li><b>Annotations:</b>
439    *       <ul>
440    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#trimEmptyCollections()}
441    *       </ul>
442    *    <li><b>Methods:</b>
443    *       <ul>
444    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#trimEmptyCollections()}
445    *       </ul>
446    * </ul>
447    *
448    * <h5 class='section'>Description:</h5>
449    *
450    * <p>
451    * When enabled, empty lists and arrays will not be serialized.
452    *
453    * <p>
454    * Note that enabling this setting has the following effects on parsing:
455    * <ul class='spaced-list'>
456    *    <li>
457    *       Map entries with empty list values will be lost.
458    *    <li>
459    *       Bean properties with empty list values will not be set.
460    * </ul>
461    *
462    * <h5 class='section'>Example:</h5>
463    * <p class='bcode w800'>
464    *    <jc>// Create a serializer that skips empty arrays and collections.</jc>
465    *    WriterSerializer s = JsonSerializer
466    *       .<jsm>create</jsm>()
467    *       .trimEmptyCollections()
468    *       .build();
469    *
470    *    <jc>// Same, but use property.</jc>
471    *    WriterSerializer s = JsonSerializer
472    *       .<jsm>create</jsm>()
473    *       .set(<jsf>SERIALIZER_trimEmptyCollections</jsf>, <jk>true</jk>)
474    *       .build();
475    *
476    *    <jc>// A bean with a field with an empty array.</jc>
477    *    <jk>public class</jk> MyBean {
478    *       <jk>public</jk> String[] <jf>foo</jf> = {};
479    *    }
480    *
481    *    <jc>// Produces {}</jc>
482    *    String json = s.serialize(<jk>new</jk> MyBean());
483    * </p>
484    */
485   public static final String SERIALIZER_trimEmptyCollections = PREFIX + ".trimEmptyCollections.b";
486
487   /**
488    * Configuration property:  Trim empty maps.
489    *
490    * <h5 class='section'>Property:</h5>
491    * <ul class='spaced-list'>
492    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_trimEmptyMaps SERIALIZER_trimEmptyMaps}
493    *    <li><b>Name:</b>  <js>"Serializer.trimEmptyMaps.b"</js>
494    *    <li><b>Data type:</b>  <jk>boolean</jk>
495    *    <li><b>System property:</b>  <c>Serializer.trimEmptyMaps</c>
496    *    <li><b>Environment variable:</b>  <c>SERIALIZER_TRIMEMPTYMAPS</c>
497    *    <li><b>Default:</b>  <jk>false</jk>
498    *    <li><b>Session property:</b>  <jk>false</jk>
499    *    <li><b>Annotations:</b>
500    *       <ul>
501    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#trimEmptyMaps()}
502    *       </ul>
503    *    <li><b>Methods:</b>
504    *       <ul>
505    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#trimEmptyMaps()}
506    *       </ul>
507    * </ul>
508    *
509    * <h5 class='section'>Description:</h5>
510    *
511    * <p>
512    * When enabled, empty map values will not be serialized to the output.
513    *
514    * <p>
515    * Note that enabling this setting has the following effects on parsing:
516    * <ul class='spaced-list'>
517    *    <li>
518    *       Bean properties with empty map values will not be set.
519    * </ul>
520    *
521    * <h5 class='section'>Example:</h5>
522    * <p class='bcode w800'>
523    *    <jc>// Create a serializer that skips empty maps.</jc>
524    *    WriterSerializer s = JsonSerializer
525    *       .<jsm>create</jsm>()
526    *       .trimEmptyMaps()
527    *       .build();
528    *
529    *    <jc>// Same, but use property.</jc>
530    *    WriterSerializer s = JsonSerializer
531    *       .<jsm>create</jsm>()
532    *       .set(<jsf>SERIALIZER_trimEmptyMaps</jsf>, <jk>true</jk>)
533    *       .build();
534    *
535    *    <jc>// A bean with a field with an empty map.</jc>
536    *    <jk>public class</jk> MyBean {
537    *       <jk>public</jk> OMap <jf>foo</jf> = OMap.<jsm>of</jsm>();
538    *    }
539    *
540    *    <jc>// Produces {}</jc>
541    *    String json = s.serialize(<jk>new</jk> MyBean());
542    * </p>
543    */
544   public static final String SERIALIZER_trimEmptyMaps = PREFIX + ".trimEmptyMaps.b";
545
546   /**
547    * Configuration property:  Trim null bean property values.
548    *
549    * <h5 class='section'>Property:</h5>
550    * <ul class='spaced-list'>
551    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_trimNullProperties SERIALIZER_trimNullProperties}
552    *    <li><b>Name:</b>  <js>"Serializer.trimNullProperties.b"</js>
553    *    <li><b>Data type:</b>  <jk>boolean</jk>
554    *    <li><b>System property:</b>  <c>Serializer.trimNullProperties</c>
555    *    <li><b>Environment variable:</b>  <c>SERIALIZER_TRIMNULLPROPERTIES</c>
556    *    <li><b>Default:</b>  <jk>true</jk>
557    *    <li><b>Session property:</b>  <jk>false</jk>
558    *    <li><b>Annotations:</b>
559    *       <ul>
560    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#trimNullProperties()}
561    *       </ul>
562    *    <li><b>Methods:</b>
563    *       <ul>
564    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#trimNullProperties(boolean)}
565    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#dontTrimNullProperties()}
566    *       </ul>
567    * </ul>
568    *
569    * <h5 class='section'>Description:</h5>
570    *
571    * <p>
572    * When enabled, null bean values will not be serialized to the output.
573    *
574    * <p>
575    * Note that enabling this setting has the following effects on parsing:
576    * <ul class='spaced-list'>
577    *    <li>
578    *       Map entries with <jk>null</jk> values will be lost.
579    * </ul>
580    *
581    * <h5 class='section'>Example:</h5>
582    * <p class='bcode w800'>
583    *    <jc>// Create a serializer that serializes null properties.</jc>
584    *    WriterSerializer s = JsonSerializer
585    *       .<jsm>create</jsm>()
586    *       .dontTrimNullProperties()
587    *       .build();
588    *
589    *    <jc>// Same, but use property.</jc>
590    *    WriterSerializer s = JsonSerializer
591    *       .<jsm>create</jsm>()
592    *       .set(<jsf>SERIALIZER_trimNullProperties</jsf>, <jk>false</jk>)
593    *       .build();
594    * </p>
595    * @deprecated Use {@link #SERIALIZER_keepNullProperties}
596    */
597   @Deprecated
598   public static final String SERIALIZER_trimNullProperties = PREFIX + ".trimNullProperties.b";
599
600   /**
601    * Configuration property:  Trim strings.
602    *
603    * <h5 class='section'>Property:</h5>
604    * <ul class='spaced-list'>
605    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_trimStrings SERIALIZER_trimStrings}
606    *    <li><b>Name:</b>  <js>"Serializer.trimStrings.b"</js>
607    *    <li><b>Data type:</b>  <jk>boolean</jk>
608    *    <li><b>System property:</b>  <c>Serializer.trimStrings</c>
609    *    <li><b>Environment variable:</b>  <c>SERIALIZER_TRIMSTRINGS</c>
610    *    <li><b>Default:</b>  <jk>false</jk>
611    *    <li><b>Session property:</b>  <jk>false</jk>
612    *    <li><b>Annotations:</b>
613    *       <ul>
614    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#trimStrings()}
615    *       </ul>
616    *    <li><b>Methods:</b>
617    *       <ul>
618    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#trimStrings()}
619    *       </ul>
620    * </ul>
621    *
622    * <h5 class='section'>Description:</h5>
623    *
624    * <p>
625    * When enabled, string values will be trimmed of whitespace using {@link String#trim()} before being serialized.
626    *
627    * <h5 class='section'>Example:</h5>
628    * <p class='bcode w800'>
629    *    <jc>// Create a serializer that trims strings before serialization.</jc>
630    *    WriterSerializer s = JsonSerializer
631    *       .<jsm>create</jsm>()
632    *       .trimStrings()
633    *       .build();
634    *
635    *    <jc>// Same, but use property.</jc>
636    *    WriterSerializer s = JsonSerializer
637    *       .<jsm>create</jsm>()
638    *       .set(<jsf>SERIALIZER_trimStrings</jsf>, <jk>true</jk>)
639    *       .build();
640    *
641    * <jc>// A map with space-padded keys/values</jc>
642    *    OMap map = OMap.<jsm>of</jsm>(<js>" foo "</js>, <js>" bar "</js>);
643    *
644    *    <jc>// Produces "{foo:'bar'}"</jc>
645    *    String json = s.toString(map);
646    * </p>
647    */
648   public static final String SERIALIZER_trimStrings = PREFIX + ".trimStrings.b";
649
650   /**
651    * Configuration property:  URI context bean.
652    *
653    * <h5 class='section'>Property:</h5>
654    * <ul class='spaced-list'>
655    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_uriContext SERIALIZER_uriContext}
656    *    <li><b>Name:</b>  <js>"Serializer.uriContext.s"</js>
657    *    <li><b>Data type:</b>  {@link org.apache.juneau.UriContext}
658    *    <li><b>System property:</b>  <c>Serializer.uriContext</c>
659    *    <li><b>Environment variable:</b>  <c>SERIALIZER_URICONTEXT</c>
660    *    <li><b>Default:</b>  <js>"{}"</js>
661    *    <li><b>Session property:</b>  <jk>true</jk>
662    *    <li><b>Annotations:</b>
663    *       <ul>
664    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#uriContext()}
665    *       </ul>
666    *    <li><b>Methods:</b>
667    *       <ul>
668    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#uriContext(UriContext)}
669    *       </ul>
670    * </ul>
671    *
672    * <h5 class='section'>Description:</h5>
673    *
674    * <p>
675    * Bean used for resolution of URIs to absolute or root-relative form.
676    *
677    * <h5 class='section'>Example:</h5>
678    * <p class='bcode w800'>
679    *    <jc>// Our URI contextual information.</jc>
680    *    String authority = <js>"http://localhost:10000"</js>;
681    *    String contextRoot = <js>"/myContext"</js>;
682    *    String servletPath = <js>"/myServlet"</js>;
683    *    String pathInfo = <js>"/foo"</js>;
684    *
685    *    <jc>// Create a UriContext object.</jc>
686    *    UriContext uriContext = <jk>new</jk> UriContext(authority, contextRoot, servletPath, pathInfo);
687    *
688    *    <jc>// Associate it with our serializer.</jc>
689    *    WriterSerializer s = JsonSerializer
690    *       .<jsm>create</jsm>()
691    *       .uriContext(uriContext)
692    *       .uriRelativity(<jsf>RESOURCE</jsf>)  <jc>// Assume relative paths are relative to servlet.</jc>
693    *       .uriResolution(<jsf>ABSOLUTE</jsf>)  <jc>// Serialize URLs as absolute paths.</jc>
694    *       .build();
695    *
696    *    <jc>// Same, but use properties.</jc>
697    *    WriterSerializer s = JsonSerializer
698    *       .<jsm>create</jsm>()
699    *       .set(<jsf>SERIALIZER_uriContext</jsf>, uriContext)
700    *       .set(<jsf>SERIALIZER_uriRelativity</jsf>, <js>"RESOURCE"</js>)
701    *       .set(<jsf>SERIALIZER_uriResolution</jsf>, <js>"ABSOLUTE"</js>)
702    *       .build();
703    *
704    *    <jc>// Same, but define it on the session args instead.</jc>
705    *    SerializerSessionArgs sessionArgs = <jk>new</jk> SerializerSessionArgs().uriContext(uriContext);
706    *    <jk>try</jk> (WriterSerializerSession session = s.createSession(sessionArgs)) {
707    *       ...
708    *    }
709    *
710    *    <jc>// A relative URL</jc>
711    *    URL url = <jk>new</jk> URL(<js>"bar"</js>);
712    *
713    *    <jc>// Produces "http://localhost:10000/myContext/myServlet/foo/bar"</jc>
714    *    String json = s.toString(url);
715    * </p>
716    *
717    * <ul class='seealso'>
718    *    <li class='link'>{@doc MarshallingUris}
719    * </ul>
720    */
721   public static final String SERIALIZER_uriContext = PREFIX + ".uriContext.s";
722
723   /**
724    * Configuration property:  URI relativity.
725    *
726    * <h5 class='section'>Property:</h5>
727    * <ul class='spaced-list'>
728    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_uriRelativity SERIALIZER_uriRelativity}
729    *    <li><b>Name:</b>  <js>"Serializer.uriRelativity.s"</js>
730    *    <li><b>Data type:</b>  {@link org.apache.juneau.UriRelativity}
731    *    <li><b>System property:</b>  <c>Serializer.uriRelativity</c>
732    *    <li><b>Environment variable:</b>  <c>SERIALIZER_URIRELATIVITY</c>
733    *    <li><b>Default:</b>  <js>"RESOURCE"</js>
734    *    <li><b>Session property:</b>  <jk>false</jk>
735    *    <li><b>Annotations:</b>
736    *       <ul>
737    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#uriRelativity()}
738    *       </ul>
739    *    <li><b>Methods:</b>
740    *       <ul>
741    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#uriRelativity(UriRelativity)}
742    *       </ul>
743    * </ul>
744    *
745    * <h5 class='section'>Description:</h5>
746    *
747    * <p>
748    * Defines what relative URIs are relative to when serializing any of the following:
749    * <ul>
750    *    <li>{@link java.net.URI}
751    *    <li>{@link java.net.URL}
752    *    <li>Properties and classes annotated with {@link org.apache.juneau.annotation.URI @URI}
753    * </ul>
754    *
755    * <p>
756    * Possible values are:
757    * <ul class='javatree'>
758    *    <li class='jf'>{@link org.apache.juneau.UriRelativity#RESOURCE}
759    *       - Relative URIs should be considered relative to the servlet URI.
760    *    <li class='jf'>{@link org.apache.juneau.UriRelativity#PATH_INFO}
761    *       - Relative URIs should be considered relative to the request URI.
762    * </ul>
763    *
764    * <p>
765    * See {@link org.apache.juneau.serializer.Serializer#SERIALIZER_uriContext} for examples.
766    *
767    * <ul class='seealso'>
768    *    <li class='link'>{@doc MarshallingUris}
769    * </ul>
770    */
771   public static final String SERIALIZER_uriRelativity = PREFIX + ".uriRelativity.s";
772
773   /**
774    * Configuration property:  URI resolution.
775    *
776    * <h5 class='section'>Property:</h5>
777    * <ul class='spaced-list'>
778    *    <li><b>ID:</b>  {@link org.apache.juneau.serializer.Serializer#SERIALIZER_uriResolution SERIALIZER_uriResolution}
779    *    <li><b>Name:</b>  <js>"Serializer.uriResolution.s"</js>
780    *    <li><b>Data type:</b>  {@link org.apache.juneau.UriResolution}
781    *    <li><b>System property:</b>  <c>Serializer.uriResolution</c>
782    *    <li><b>Environment variable:</b>  <c>SERIALIZER_URIRESOLUTION</c>
783    *    <li><b>Default:</b>  <js>"NONE"</js>
784    *    <li><b>Session property:</b>  <jk>false</jk>
785    *    <li><b>Annotations:</b>
786    *       <ul>
787    *          <li class='ja'>{@link org.apache.juneau.serializer.annotation.SerializerConfig#uriResolution()}
788    *       </ul>
789    *    <li><b>Methods:</b>
790    *       <ul>
791    *          <li class='jm'>{@link org.apache.juneau.serializer.SerializerBuilder#uriResolution(UriResolution)}
792    *       </ul>
793    * </ul>
794    *
795    * <h5 class='section'>Description:</h5>
796    *
797    * <p>
798    * Defines the resolution level for URIs when serializing any of the following:
799    * <ul>
800    *    <li>{@link java.net.URI}
801    *    <li>{@link java.net.URL}
802    *    <li>Properties and classes annotated with {@link org.apache.juneau.annotation.URI @URI}
803    * </ul>
804    *
805    * <p>
806    * Possible values are:
807    * <ul>
808    *    <li class='jf'>{@link UriResolution#ABSOLUTE}
809    *       - Resolve to an absolute URL (e.g. <js>"http://host:port/context-root/servlet-path/path-info"</js>).
810    *    <li class='jf'>{@link UriResolution#ROOT_RELATIVE}
811    *       - Resolve to a root-relative URL (e.g. <js>"/context-root/servlet-path/path-info"</js>).
812    *    <li class='jf'>{@link UriResolution#NONE}
813    *       - Don't do any URL resolution.
814    * </ul>
815    *
816    * <p>
817    * See {@link org.apache.juneau.serializer.Serializer#SERIALIZER_uriContext} for examples.
818    *
819    * <ul class='seealso'>
820    *    <li class='link'>{@doc MarshallingUris}
821    * </ul>
822    */
823   public static final String SERIALIZER_uriResolution = PREFIX + ".uriResolution.s";
824
825   static final Serializer DEFAULT = new Serializer(PropertyStore.create().build(), "", "") {
826      @Override
827      public SerializerSession createSession(SerializerSessionArgs args) {
828         throw new NoSuchMethodError();
829      }
830   };
831
832   //-------------------------------------------------------------------------------------------------------------------
833   // Instance
834   //-------------------------------------------------------------------------------------------------------------------
835
836   private final boolean
837      addBeanTypes,
838      keepNullProperties,
839      trimEmptyCollections,
840      trimEmptyMaps,
841      trimStrings,
842      sortCollections,
843      sortMaps,
844      addRootType;
845   private final UriContext uriContext;
846   private final UriResolution uriResolution;
847   private final UriRelativity uriRelativity;
848   private final Class<? extends SerializerListener> listener;
849
850   private final MediaRanges accept;
851   private final MediaType[] accepts;
852   private final MediaType produces;
853
854   /**
855    * Constructor
856    *
857    * @param ps
858    *    The property store containing all the settings for this object.
859    * @param produces
860    *    The media type that this serializer produces.
861    * @param accept
862    *    The accept media types that the serializer can handle.
863    *    <p>
864    *    Can contain meta-characters per the <c>media-type</c> specification of {@doc ExtRFC2616.section14.1}
865    *    <p>
866    *    If empty, then assumes the only media type supported is <c>produces</c>.
867    *    <p>
868    *    For example, if this serializer produces <js>"application/json"</js> but should handle media types of
869    *    <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be:
870    *    <p class='bcode w800'>
871    *    <jk>super</jk>(ps, <js>"application/json"</js>, <js>"application/json,text/json"</js>);
872    *    </p>
873    *    <br>...or...
874    *    <p class='bcode w800'>
875    *    <jk>super</jk>(ps, <js>"application/json"</js>, <js>"*&#8203;/json"</js>);
876    *    </p>
877    * <p>
878    * The accept value can also contain q-values.
879    */
880   protected Serializer(PropertyStore ps, String produces, String accept) {
881      super(ps);
882
883      addBeanTypes = getBooleanProperty(SERIALIZER_addBeanTypes, false);
884      keepNullProperties = getBooleanProperty(SERIALIZER_keepNullProperties, ! getBooleanProperty(SERIALIZER_trimNullProperties, true));
885      trimEmptyCollections = getBooleanProperty(SERIALIZER_trimEmptyCollections, false);
886      trimEmptyMaps = getBooleanProperty(SERIALIZER_trimEmptyMaps, false);
887      trimStrings = getBooleanProperty(SERIALIZER_trimStrings, false);
888      sortCollections = getBooleanProperty(SERIALIZER_sortCollections, false);
889      sortMaps = getBooleanProperty(SERIALIZER_sortMaps, false);
890      addRootType = getBooleanProperty(SERIALIZER_addRootType, false);
891      uriContext = getProperty(SERIALIZER_uriContext, UriContext.class, UriContext.DEFAULT);
892      uriResolution = getProperty(SERIALIZER_uriResolution, UriResolution.class, UriResolution.NONE);
893      uriRelativity = getProperty(SERIALIZER_uriRelativity, UriRelativity.class, UriRelativity.RESOURCE);
894      listener = getClassProperty(SERIALIZER_listener, SerializerListener.class, null);
895
896      this.produces = MediaType.of(produces);
897      this.accept = accept == null ? MediaRanges.of(produces) : MediaRanges.of(accept);
898      this.accepts = accept == null ? new MediaType[] {this.produces} : MediaType.ofAll(StringUtils.split(accept, ','));
899   }
900
901   @Override /* Context */
902   public SerializerBuilder builder() {
903      return null;
904   }
905
906   //-----------------------------------------------------------------------------------------------------------------
907   // Abstract methods
908   //-----------------------------------------------------------------------------------------------------------------
909
910   /**
911    * Returns <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}.
912    *
913    * @return <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}.
914    */
915   public boolean isWriterSerializer() {
916      return true;
917   }
918
919   /**
920    * Create the session object used for actual serialization of objects.
921    *
922    * @param args
923    *    Runtime arguments.
924    *    These specify session-level information such as locale and URI context.
925    *    It also include session-level properties that override the properties defined on the bean and serializer
926    *    contexts.
927    * @return
928    *    The new session object.
929    */
930   public abstract SerializerSession createSession(SerializerSessionArgs args);
931
932
933   //-----------------------------------------------------------------------------------------------------------------
934   // Convenience methods
935   //-----------------------------------------------------------------------------------------------------------------
936
937   @Override /* Context */
938   public SerializerSession createSession() {
939      return createSession(createDefaultSessionArgs());
940   }
941
942   @Override /* Context */
943   public final SerializerSessionArgs createDefaultSessionArgs() {
944      return new SerializerSessionArgs().mediaType(getResponseContentType());
945   }
946
947   /**
948    * Serializes a POJO to the specified output stream or writer.
949    *
950    * <p>
951    * Equivalent to calling <c>serializer.createSession().serialize(o, output);</c>
952    *
953    * @param o The object to serialize.
954    * @param output
955    *    The output object.
956    *    <br>Character-based serializers can handle the following output class types:
957    *    <ul>
958    *       <li>{@link Writer}
959    *       <li>{@link OutputStream} - Output will be written as UTF-8 encoded stream.
960    *       <li>{@link File} - Output will be written as system-default encoded stream.
961    *       <li>{@link StringBuilder} - Output will be written to the specified string builder.
962    *    </ul>
963    *    <br>Stream-based serializers can handle the following output class types:
964    *    <ul>
965    *       <li>{@link OutputStream}
966    *       <li>{@link File}
967    *    </ul>
968    * @throws SerializeException If a problem occurred trying to convert the output.
969    * @throws IOException Thrown by the underlying stream.
970    */
971   public final void serialize(Object o, Object output) throws SerializeException, IOException {
972      createSession().serialize(o, output);
973   }
974
975   /**
976    * Shortcut method for serializing objects directly to either a <c>String</c> or <code><jk>byte</jk>[]</code>
977    * depending on the serializer type.
978    *
979    * @param o The object to serialize.
980    * @return
981    *    The serialized object.
982    *    <br>Character-based serializers will return a <c>String</c>
983    *    <br>Stream-based serializers will return a <code><jk>byte</jk>[]</code>
984    * @throws SerializeException If a problem occurred trying to convert the output.
985    */
986   public Object serialize(Object o) throws SerializeException {
987      return createSession().serialize(o);
988   }
989
990   /**
991    * Convenience method for serializing an object to a String.
992    *
993    * <p>
994    * For writer-based serializers, this is identical to calling {@link #serialize(Object)}.
995    * <br>For stream-based serializers, this converts the returned byte array to a string based on
996    * the {@link OutputStreamSerializer#OSSERIALIZER_binaryFormat} setting.
997    *
998    * @param o The object to serialize.
999    * @return The output serialized to a string.
1000    * @throws SerializeException If a problem occurred trying to convert the output.
1001    */
1002   public final String serializeToString(Object o) throws SerializeException {
1003      return createSession().serializeToString(o);
1004   }
1005
1006   //-----------------------------------------------------------------------------------------------------------------
1007   // Other methods
1008   //-----------------------------------------------------------------------------------------------------------------
1009
1010   /**
1011    * Returns the media types handled based on the value of the <c>accept</c> parameter passed into the constructor.
1012    *
1013    * <p>
1014    * Note that the order of these ranges are from high to low q-value.
1015    *
1016    * @return The list of media types.  Never <jk>null</jk>.
1017    */
1018   public final MediaRanges getMediaTypeRanges() {
1019      return accept;
1020   }
1021
1022   /**
1023    * Returns the first entry in the <c>accept</c> parameter passed into the constructor.
1024    *
1025    * <p>
1026    * This signifies the 'primary' media type for this serializer.
1027    *
1028    * @return The media type.  Never <jk>null</jk>.
1029    */
1030   public final MediaType getPrimaryMediaType() {
1031      return accepts[0];
1032   }
1033
1034   /**
1035    * Returns the media types handled based on the value of the <c>accept</c> parameter passed into the constructor.
1036    *
1037    * <p>
1038    * The order of the media types are the same as those in the <c>accept</c> parameter.
1039    *
1040    * @return The list of media types.  Never <jk>null</jk>.
1041    */
1042   public final MediaType[] getAcceptMediaTypes() {
1043      return accepts;
1044   }
1045
1046   /**
1047    * Optional method that returns the response <c>Content-Type</c> for this serializer if it is different from
1048    * the matched media type.
1049    *
1050    * <p>
1051    * This method is specified to override the content type for this serializer.
1052    * For example, the {@link org.apache.juneau.json.SimpleJsonSerializer} class returns that it handles media type
1053    * <js>"text/json+simple"</js>, but returns <js>"text/json"</js> as the actual content type.
1054    * This allows clients to request specific 'flavors' of content using specialized <c>Accept</c> header values.
1055    *
1056    * <p>
1057    * This method is typically meaningless if the serializer is being used stand-alone (i.e. outside of a REST server
1058    * or client).
1059    *
1060    * @return The response content type.  If <jk>null</jk>, then the matched media type is used.
1061    */
1062   public final MediaType getResponseContentType() {
1063      return produces;
1064   }
1065
1066   //-----------------------------------------------------------------------------------------------------------------
1067   // Properties
1068   //-----------------------------------------------------------------------------------------------------------------
1069
1070   /**
1071    * Add <js>"_type"</js> properties when needed.
1072    *
1073    * @see #SERIALIZER_addBeanTypes
1074    * @return
1075    *    <jk>true</jk> if <js>"_type"</js> properties added to beans if their type cannot be inferred
1076    *    through reflection.
1077    */
1078   protected boolean isAddBeanTypes() {
1079      return addBeanTypes;
1080   }
1081
1082   /**
1083    * Add type attribute to root nodes.
1084    *
1085    * @see #SERIALIZER_addRootType
1086    * @return
1087    *    <jk>true</jk> if type property should be added to root node.
1088    */
1089   protected final boolean isAddRootType() {
1090      return addRootType;
1091   }
1092
1093   /**
1094    * Serializer listener.
1095    *
1096    * @see #SERIALIZER_listener
1097    * @return
1098    *    Class used to listen for errors and warnings that occur during serialization.
1099    */
1100   protected final Class<? extends SerializerListener> getListener() {
1101      return listener;
1102   }
1103
1104   /**
1105    * Sort arrays and collections alphabetically.
1106    *
1107    * @see #SERIALIZER_sortCollections
1108    * @return
1109    *    <jk>true</jk> if arrays and collections are copied and sorted before serialization.
1110    */
1111   protected final boolean isSortCollections() {
1112      return sortCollections;
1113   }
1114
1115   /**
1116    * Sort maps alphabetically.
1117    *
1118    * @see #SERIALIZER_sortMaps
1119    * @return
1120    *    <jk>true</jk> if maps are copied and sorted before serialization.
1121    */
1122   protected final boolean isSortMaps() {
1123      return sortMaps;
1124   }
1125
1126   /**
1127    * Trim empty lists and arrays.
1128    *
1129    * @see #SERIALIZER_trimEmptyCollections
1130    * @return
1131    *    <jk>true</jk> if empty lists and arrays are not serialized to the output.
1132    */
1133   protected final boolean isTrimEmptyCollections() {
1134      return trimEmptyCollections;
1135   }
1136
1137   /**
1138    * Trim empty maps.
1139    *
1140    * @see #SERIALIZER_trimEmptyMaps
1141    * @return
1142    *    <jk>true</jk> if empty map values are not serialized to the output.
1143    */
1144   protected final boolean isTrimEmptyMaps() {
1145      return trimEmptyMaps;
1146   }
1147
1148   /**
1149    * Don't trim null bean property values.
1150    *
1151    * @see #SERIALIZER_keepNullProperties
1152    * @return
1153    *    <jk>true</jk> if null bean values are serialized to the output.
1154    */
1155   protected final boolean isKeepNullProperties() {
1156      return keepNullProperties;
1157   }
1158
1159   /**
1160    * Trim strings.
1161    *
1162    * @see #SERIALIZER_trimStrings
1163    * @return
1164    *    <jk>true</jk> if string values will be trimmed of whitespace using {@link String#trim()} before being serialized.
1165    */
1166   protected final boolean isTrimStrings() {
1167      return trimStrings;
1168   }
1169
1170   /**
1171    * URI context bean.
1172    *
1173    * @see #SERIALIZER_uriContext
1174    * @return
1175    *    Bean used for resolution of URIs to absolute or root-relative form.
1176    */
1177   protected final UriContext getUriContext() {
1178      return uriContext;
1179   }
1180
1181   /**
1182    * URI relativity.
1183    *
1184    * @see #SERIALIZER_uriRelativity
1185    * @return
1186    *    Defines what relative URIs are relative to when serializing any of the following:
1187    */
1188   protected final UriRelativity getUriRelativity() {
1189      return uriRelativity;
1190   }
1191
1192   /**
1193    * URI resolution.
1194    *
1195    * @see #SERIALIZER_uriResolution
1196    * @return
1197    *    Defines the resolution level for URIs when serializing URIs.
1198    */
1199   protected final UriResolution getUriResolution() {
1200      return uriResolution;
1201   }
1202
1203   //-----------------------------------------------------------------------------------------------------------------
1204   // Other methods
1205   //-----------------------------------------------------------------------------------------------------------------
1206
1207   @Override /* Context */
1208   public OMap toMap() {
1209      return super.toMap()
1210         .a("Serializer", new DefaultFilteringOMap()
1211            .a("addBeanTypes", addBeanTypes)
1212            .a("keepNullProperties", keepNullProperties)
1213            .a("trimEmptyCollections", trimEmptyCollections)
1214            .a("trimEmptyMaps", trimEmptyMaps)
1215            .a("trimStrings", trimStrings)
1216            .a("sortCollections", sortCollections)
1217            .a("sortMaps", sortMaps)
1218            .a("addRootType", addRootType)
1219            .a("uriContext", uriContext)
1220            .a("uriResolution", uriResolution)
1221            .a("uriRelativity", uriRelativity)
1222            .a("listener", listener)
1223         );
1224   }
1225}