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