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