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.xml;
014
015import org.apache.juneau.*;
016import org.apache.juneau.internal.*;
017import org.apache.juneau.json.*;
018import org.apache.juneau.serializer.*;
019import org.apache.juneau.xmlschema.XmlSchemaSerializer;
020
021/**
022 * Serializes POJO models to XML.
023 *
024 * <h5 class='topic'>Media types</h5>
025 *
026 * Handles <code>Accept</code> types:  <code><b>text/xml</b></code>
027 * <p>
028 * Produces <code>Content-Type</code> types:  <code><b>text/xml</b></code>
029 *
030 * <h5 class='topic'>Description</h5>
031 *
032 * See the {@link JsonSerializer} class for details on how Java models map to JSON.
033 *
034 * <p>
035 * For example, the following JSON...
036 * <p class='bcode w800'>
037 *    {
038 *       name:<js>'John Smith'</js>,
039 *       address: {
040 *          streetAddress: <js>'21 2nd Street'</js>,
041 *          city: <js>'New York'</js>,
042 *          state: <js>'NY'</js>,
043 *          postalCode: <js>10021</js>
044 *       },
045 *       phoneNumbers: [
046 *          <js>'212 555-1111'</js>,
047 *          <js>'212 555-2222'</js>
048 *       ],
049 *       additionalInfo: <jk>null</jk>,
050 *       remote: <jk>false</jk>,
051 *       height: <js>62.4</js>,
052 *       <js>'fico score'</js>:  <js>' &gt; 640'</js>
053 *    }
054 * <p>
055 *    ...maps to the following XML using the default serializer...
056 * <p class='bcode w800'>
057 *    <xt>&lt;object&gt;</xt>
058 *       <xt>&lt;name&gt;</xt>John Smith<xt>&lt;/name&gt;</xt>
059 *       <xt>&lt;address&gt;</xt>
060 *          <xt>&lt;streetAddress&gt;</xt>21 2nd Street<xt>&lt;/streetAddress&gt;</xt>
061 *          <xt>&lt;city&gt;</xt>New York<xt>&lt;/city&gt;</xt>
062 *          <xt>&lt;state&gt;</xt>NY<xt>&lt;/state&gt;</xt>
063 *          <xt>&lt;postalCode&gt;</xt>10021<xt>&lt;/postalCode&gt;</xt>
064 *       <xt>&lt;/address&gt;</xt>
065 *       <xt>&lt;phoneNumbers&gt;</xt>
066 *          <xt>&lt;string&gt;</xt>212 555-1111<xt>&lt;/string&gt;</xt>
067 *          <xt>&lt;string&gt;</xt>212 555-2222<xt>&lt;/string&gt;</xt>
068 *       <xt>&lt;/phoneNumbers&gt;</xt>
069 *       <xt>&lt;additionalInfo</xt> <xa>_type</xa>=<xs>'null'</xs><xt>&gt;&lt;/additionalInfo&gt;</xt>
070 *       <xt>&lt;remote&gt;</xt>false<xt>&lt;/remote&gt;</xt>
071 *       <xt>&lt;height&gt;</xt>62.4<xt>&lt;/height&gt;</xt>
072 *       <xt>&lt;fico_x0020_score&gt;</xt> &amp;gt; 640<xt>&lt;/fico_x0020_score&gt;</xt>
073 *    <xt>&lt;/object&gt;</xt>
074 *
075 * <p>
076 * An additional "add-json-properties" mode is also provided to prevent loss of JSON data types...
077 * <p class='bcode w800'>
078 *       <xt>&lt;name</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>John Smith<xt>&lt;/name&gt;</xt>
079 *       <xt>&lt;address</xt> <xa>_type</xa>=<xs>'object'</xs><xt>&gt;</xt>
080 *          <xt>&lt;streetAddress</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>21 2nd Street<xt>&lt;/streetAddress&gt;</xt>
081 *          <xt>&lt;city</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>New York<xt>&lt;/city&gt;</xt>
082 *          <xt>&lt;state</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt>NY<xt>&lt;/state&gt;</xt>
083 *          <xt>&lt;postalCode</xt> <xa>_type</xa>=<xs>'number'</xs><xt>&gt;</xt>10021<xt>&lt;/postalCode&gt;</xt>
084 *       <xt>&lt;/address&gt;</xt>
085 *       <xt>&lt;phoneNumbers</xt> <xa>_type</xa>=<xs>'array'</xs><xt>&gt;</xt>
086 *          <xt>&lt;string&gt;</xt>212 555-1111<xt>&lt;/string&gt;</xt>
087 *          <xt>&lt;string&gt;</xt>212 555-2222<xt>&lt;/string&gt;</xt>
088 *       <xt>&lt;/phoneNumbers&gt;</xt>
089 *       <xt>&lt;additionalInfo</xt> <xa>_type</xa>=<xs>'null'</xs><xt>&gt;&lt;/additionalInfo&gt;</xt>
090 *       <xt>&lt;remote</xt> <xa>_type</xa>=<xs>'boolean'</xs><xt>&gt;</xt>false<xt>&lt;/remote&gt;</xt>
091 *       <xt>&lt;height</xt> <xa>_type</xa>=<xs>'number'</xs><xt>&gt;</xt>62.4<xt>&lt;/height&gt;</xt>
092 *       <xt>&lt;fico_x0020_score</xt> <xa>_type</xa>=<xs>'string'</xs><xt>&gt;</xt> &amp;gt; 640<xt>&lt;/fico_x0020_score&gt;</xt>
093 *    <xt>&lt;/object&gt;</xt>
094 * </p>
095 *
096 * <p>
097 * This serializer provides several serialization options.
098 * Typically, one of the predefined <jsf>DEFAULT</jsf> serializers will be sufficient.
099 * However, custom serializers can be constructed to fine-tune behavior.
100 *
101 * <p>
102 * If an attribute name contains any non-valid XML element characters, they will be escaped using standard
103 * {@code _x####_} notation.
104 *
105 * <h5 class='topic'>Behavior-specific subclasses</h5>
106 *
107 * The following direct subclasses are provided for convenience:
108 * <ul>
109 *    <li>{@link Sq} - Default serializer, single quotes.
110 *    <li>{@link SqReadable} - Default serializer, single quotes, whitespace added.
111 * </ul>
112 */
113public class XmlSerializer extends WriterSerializer {
114
115   //-------------------------------------------------------------------------------------------------------------------
116   // Configurable properties
117   //-------------------------------------------------------------------------------------------------------------------
118
119   private static final String PREFIX = "XmlSerializer.";
120
121   /**
122    * Configuration property:  Add <js>"_type"</js> properties when needed.
123    *
124    * <h5 class='section'>Property:</h5>
125    * <ul>
126    *    <li><b>Name:</b>  <js>"XmlSerializer.addBeanTypes.b"</js>
127    *    <li><b>Data type:</b>  <code>Boolean</code>
128    *    <li><b>Default:</b>  <jk>false</jk>
129    *    <li><b>Session property:</b>  <jk>false</jk>
130    *    <li><b>Methods:</b>
131    *       <ul>
132    *          <li class='jm'>{@link XmlSerializerBuilder#addBeanTypes(boolean)}
133    *       </ul>
134    * </ul>
135    *
136    * <h5 class='section'>Description:</h5>
137    * <p>
138    * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred
139    * through reflection.
140    *
141    * <p>
142    * When present, this value overrides the {@link #SERIALIZER_addBeanTypes} setting and is
143    * provided to customize the behavior of specific serializers in a {@link SerializerGroup}.
144    */
145   public static final String XML_addBeanTypes = PREFIX + "addBeanTypes.b";
146
147   /**
148    * Configuration property:  Add namespace URLs to the root element.
149    *
150    * <h5 class='section'>Property:</h5>
151    * <ul>
152    *    <li><b>Name:</b>  <js>"XmlSerializer.addNamespaceUrisToRoot.b"</js>
153    *    <li><b>Data type:</b>  <code>Boolean</code>
154    *    <li><b>Default:</b>  <jk>false</jk>
155    *    <li><b>Session property:</b>  <jk>false</jk>
156    *    <li><b>Methods:</b>
157    *       <ul>
158    *          <li class='jm'>{@link XmlSerializerBuilder#addNamespaceUrisToRoot(boolean)}
159    *          <li class='jm'>{@link XmlSerializerBuilder#addNamespaceUrisToRoot()}
160    *       </ul>
161    * </ul>
162    *
163    * <h5 class='section'>Description:</h5>
164    * <p>
165    * Use this setting to add {@code xmlns:x} attributes to the root element for the default and all mapped namespaces.
166    *
167    * <p>
168    * This setting is ignored if {@link #XML_enableNamespaces} is not enabled.
169    *
170    * <h5 class='section'>See Also:</h5>
171    * <ul>
172    *    <li class='link'>{@doc juneau-marshall.XmlDetails.Namespaces}
173    * </ul>
174    */
175   public static final String XML_addNamespaceUrisToRoot = PREFIX + "addNamespaceUrisToRoot.b";
176
177   /**
178    * Configuration property:  Auto-detect namespace usage.
179    *
180    * <h5 class='section'>Property:</h5>
181    * <ul>
182    *    <li><b>Name:</b>  <js>"XmlSerializer.autoDetectNamespaces.b"</js>
183    *    <li><b>Data type:</b>  <code>Boolean</code>
184    *    <li><b>Default:</b>  <jk>true</jk>
185    *    <li><b>Session property:</b>  <jk>false</jk>
186    *    <li><b>Methods:</b>
187    *       <ul>
188    *          <li class='jm'>{@link XmlSerializerBuilder#autoDetectNamespaces(boolean)}
189    *       </ul>
190    * </ul>
191    *
192    * <h5 class='section'>Description:</h5>
193    * <p>
194    * Detect namespace usage before serialization.
195    *
196    * <p>
197    * Used in conjunction with {@link #XML_addNamespaceUrisToRoot} to reduce the list of namespace URLs appended to the
198    * root element to only those that will be used in the resulting document.
199    *
200    * <p>
201    * If enabled, then the data structure will first be crawled looking for namespaces that will be encountered before
202    * the root element is serialized.
203    *
204    * <p>
205    * This setting is ignored if {@link #XML_enableNamespaces} is not enabled.
206    *
207    * <h5 class='section'>Notes:</h5>
208    * <ul class='spaced-list'>
209    *    <li>
210    *       Auto-detection of namespaces can be costly performance-wise.
211    *       <br>In high-performance environments, it's recommended that namespace detection be
212    *       disabled, and that namespaces be manually defined through the {@link #XML_namespaces} property.
213    * </ul>
214    *
215    * <h5 class='section'>See Also:</h5>
216    * <ul>
217    *    <li class='link'>{@doc juneau-marshall.XmlDetails.Namespaces}
218    * </ul>
219    */
220   public static final String XML_autoDetectNamespaces = PREFIX + "autoDetectNamespaces.b";
221
222   /**
223    * Configuration property:  Default namespace.
224    *
225    * <h5 class='section'>Property:</h5>
226    * <ul>
227    *    <li><b>Name:</b>  <js>"XmlSerializer.defaultNamespace.s"</js>
228    *    <li><b>Data type:</b>  <code>String</code> ({@link Namespace})
229    *    <li><b>Default:</b>  <js>"juneau: http://www.apache.org/2013/Juneau"</js>
230    *    <li><b>Session property:</b>  <jk>false</jk>
231    *    <li><b>Methods:</b>
232    *       <ul>
233    *          <li class='jm'>{@link XmlSerializerBuilder#defaultNamespace(String)}
234    *       </ul>
235    * </ul>
236    *
237    * <h5 class='section'>Description:</h5>
238    * <p>
239    * Specifies the default namespace URI for this document.
240    *
241    * <h5 class='section'>See Also:</h5>
242    * <ul>
243    *    <li class='link'>{@doc juneau-marshall.XmlDetails.Namespaces}
244    * </ul>
245    */
246   public static final String XML_defaultNamespace = PREFIX + "defaultNamespace.s";
247
248   /**
249    * Configuration property:  Enable support for XML namespaces.
250    *
251    * <h5 class='section'>Property:</h5>
252    * <ul>
253    *    <li><b>Name:</b>  <js>"XmlSerializer.enableNamespaces.b"</js>
254    *    <li><b>Data type:</b>  <code>Boolean</code>
255    *    <li><b>Default:</b>  <jk>false</jk>
256    *    <li><b>Session property:</b>  <jk>false</jk>
257    *    <li><b>Methods:</b>
258    *       <ul>
259    *          <li class='jm'>{@link XmlSerializerBuilder#enableNamespaces(boolean)}
260    *       </ul>
261    * </ul>
262    *
263    * <h5 class='section'>Description:</h5>
264    * <p>
265    * If not enabled, XML output will not contain any namespaces regardless of any other settings.
266    *
267    * <h5 class='section'>See Also:</h5>
268    * <ul>
269    *    <li class='link'>{@doc juneau-marshall.XmlDetails.Namespaces}
270    * </ul>
271    */
272   public static final String XML_enableNamespaces = PREFIX + "enableNamespaces.b";
273
274   /**
275    * Configuration property:  Default namespaces.
276    *
277    * <h5 class='section'>Property:</h5>
278    * <ul>
279    *    <li><b>Name:</b>  <js>"XmlSerializer.namespaces.ls"</js>
280    *    <li><b>Data type:</b>  <code>Set&lt;String&gt;</code> ({@link Namespace})
281    *    <li><b>Default:</b>  empty set
282    *    <li><b>Session property:</b>  <jk>false</jk>
283    *    <li><b>Methods:</b>
284    *       <ul>
285    *          <li class='jm'>{@link XmlSerializerBuilder#defaultNamespace(String)}
286    *       </ul>
287    * </ul>
288    *
289    * <h5 class='section'>Description:</h5>
290    * <p>
291    * The default list of namespaces associated with this serializer.
292    *
293    * <h5 class='section'>See Also:</h5>
294    * <ul>
295    *    <li class='link'>{@doc juneau-marshall.XmlDetails.Namespaces}
296    * </ul>
297    */
298   public static final String XML_namespaces = PREFIX + "namespaces.ls";
299
300   /**
301    * Configuration property:  XMLSchema namespace.
302    *
303    * <h5 class='section'>Property:</h5>
304    * <ul>
305    *    <li><b>Name:</b>  <js>"XmlSerializer.xsNamespace.s"</js>
306    *    <li><b>Data type:</b>  <code>String</code> ({@link Namespace})
307    *    <li><b>Default:</b>  <js>"xs: http://www.w3.org/2001/XMLSchema"</js>
308    *    <li><b>Session property:</b>  <jk>false</jk>
309    *    <li><b>Methods:</b>
310    *       <ul>
311    *          <li class='jm'>{@link XmlSerializerBuilder#xsNamespace(Namespace)}
312    *       </ul>
313    * </ul>
314    *
315    * <h5 class='section'>Description:</h5>
316    * <p>
317    * Specifies the namespace for the <code>XMLSchema</code> namespace, used by the schema generated by the
318    * {@link XmlSchemaSerializer} class.
319    *
320    * <h5 class='section'>See Also:</h5>
321    * <ul>
322    *    <li class='link'>{@doc juneau-marshall.XmlDetails.Namespaces}
323    * </ul>
324    */
325   public static final String XML_xsNamespace = PREFIX + "xsNamespace.s";
326
327
328   //-------------------------------------------------------------------------------------------------------------------
329   // Predefined instances
330   //-------------------------------------------------------------------------------------------------------------------
331
332   /** Default serializer without namespaces. */
333   public static final XmlSerializer DEFAULT = new XmlSerializer(PropertyStore.DEFAULT);
334
335   /** Default serializer without namespaces, with single quotes. */
336   public static final XmlSerializer DEFAULT_SQ = new Sq(PropertyStore.DEFAULT);
337
338   /** Default serializer without namespaces, with single quotes, whitespace added. */
339   public static final XmlSerializer DEFAULT_SQ_READABLE = new SqReadable(PropertyStore.DEFAULT);
340
341   /** Default serializer, all default settings. */
342   public static final XmlSerializer DEFAULT_NS = new Ns(PropertyStore.DEFAULT);
343
344   /** Default serializer, single quotes. */
345   public static final XmlSerializer DEFAULT_NS_SQ = new NsSq(PropertyStore.DEFAULT);
346
347   /** Default serializer, single quotes, whitespace added. */
348   public static final XmlSerializer DEFAULT_NS_SQ_READABLE = new NsSqReadable(PropertyStore.DEFAULT);
349
350
351   //-------------------------------------------------------------------------------------------------------------------
352   // Predefined subclasses
353   //-------------------------------------------------------------------------------------------------------------------
354
355   /** Default serializer, single quotes. */
356   public static class Sq extends XmlSerializer {
357
358      /**
359       * Constructor.
360       *
361       * @param ps The property store containing all the settings for this object.
362       */
363      public Sq(PropertyStore ps) {
364         super(
365            ps.builder()
366               .set(WSERIALIZER_quoteChar, '\'')
367               .build()
368            );
369      }
370   }
371
372   /** Default serializer, single quotes, whitespace added. */
373   public static class SqReadable extends XmlSerializer {
374
375      /**
376       * Constructor.
377       *
378       * @param ps The property store containing all the settings for this object.
379       */
380      public SqReadable(PropertyStore ps) {
381         super(
382            ps.builder()
383               .set(WSERIALIZER_quoteChar, '\'')
384               .set(SERIALIZER_useWhitespace, true)
385               .build()
386            );
387      }
388   }
389
390   /** Default serializer without namespaces. */
391   public static class Ns extends XmlSerializer {
392
393      /**
394       * Constructor.
395       *
396       * @param ps The property store containing all the settings for this object.
397       */
398      public Ns(PropertyStore ps) {
399         super(
400            ps.builder()
401               .set(XML_enableNamespaces, true)
402               .build(),
403            "text/xml",
404            "text/xml+simple"
405         );
406      }
407   }
408
409   /** Default serializer without namespaces, single quotes. */
410   public static class NsSq extends XmlSerializer {
411
412      /**
413       * Constructor.
414       *
415       * @param ps The property store containing all the settings for this object.
416       */
417      public NsSq(PropertyStore ps) {
418         super(
419            ps.builder()
420               .set(XML_enableNamespaces, true)
421               .set(WSERIALIZER_quoteChar, '\'')
422               .build()
423            );
424      }
425   }
426
427   /** Default serializer without namespaces, single quotes, with whitespace. */
428   public static class NsSqReadable extends XmlSerializer {
429
430      /**
431       * Constructor.
432       *
433       * @param ps The property store containing all the settings for this object.
434       */
435      public NsSqReadable(PropertyStore ps) {
436         super(
437            ps.builder()
438               .set(XML_enableNamespaces, true)
439               .set(WSERIALIZER_quoteChar, '\'')
440               .set(SERIALIZER_useWhitespace, true)
441               .build()
442            );
443      }
444   }
445
446   @SuppressWarnings("javadoc")
447   protected static final Namespace
448      DEFAULT_JUNEAU_NAMESPACE = Namespace.create("juneau", "http://www.apache.org/2013/Juneau"),
449      DEFAULT_XS_NAMESPACE = Namespace.create("xs", "http://www.w3.org/2001/XMLSchema");
450
451   //-------------------------------------------------------------------------------------------------------------------
452   // Instance
453   //-------------------------------------------------------------------------------------------------------------------
454
455   private final boolean
456      autoDetectNamespaces,
457      enableNamespaces,
458      addNamespaceUrlsToRoot,
459      addBeanTypes;
460   private final Namespace defaultNamespace;
461   private final Namespace
462      xsNamespace;
463   private final Namespace[] namespaces;
464
465   private volatile XmlSchemaSerializer schemaSerializer;
466
467   /**
468    * Constructor.
469    *
470    * @param ps
471    *    The property store containing all the settings for this object.
472    */
473   public XmlSerializer(PropertyStore ps) {
474      this(ps, "text/xml", (String)null);
475   }
476
477   /**
478    * Constructor.
479    *
480    * @param ps
481    *    The property store containing all the settings for this object.
482    * @param produces
483    *    The media type that this serializer produces.
484    * @param accept
485    *    The accept media types that the serializer can handle.
486    *    <p>
487    *    Can contain meta-characters per the <code>media-type</code> specification of {@doc RFC2616.section14.1}
488    *    <p>
489    *    If empty, then assumes the only media type supported is <code>produces</code>.
490    *    <p>
491    *    For example, if this serializer produces <js>"application/json"</js> but should handle media types of
492    *    <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be:
493    *    <p class='bcode w800'>
494    *    <jk>super</jk>(ps, <js>"application/json"</js>, <js>"application/json,text/json"</js>);
495    *    </p>
496    *    <br>...or...
497    *    <p class='bcode w800'>
498    *    <jk>super</jk>(ps, <js>"application/json"</js>, <js>"*&#8203;/json"</js>);
499    *    </p>
500    * <p>
501    * The accept value can also contain q-values.
502    */
503   public XmlSerializer(PropertyStore ps, String produces, String accept) {
504      super(ps, produces, accept);
505      autoDetectNamespaces = getBooleanProperty(XML_autoDetectNamespaces, true);
506      enableNamespaces = getBooleanProperty(XML_enableNamespaces, false);
507      addNamespaceUrlsToRoot = getBooleanProperty(XML_addNamespaceUrisToRoot, false);
508      defaultNamespace = getInstanceProperty(XML_defaultNamespace, Namespace.class, DEFAULT_JUNEAU_NAMESPACE);
509      addBeanTypes = getBooleanProperty(XML_addBeanTypes, getBooleanProperty(SERIALIZER_addBeanTypes, false));
510      xsNamespace = getInstanceProperty(XML_xsNamespace, Namespace.class, DEFAULT_XS_NAMESPACE);
511      namespaces = getInstanceArrayProperty(XML_namespaces, Namespace.class, new Namespace[0]);
512   }
513
514   @Override /* Context */
515   public XmlSerializerBuilder builder() {
516      return new XmlSerializerBuilder(getPropertyStore());
517   }
518
519   /**
520    * Instantiates a new clean-slate {@link XmlSerializerBuilder} object.
521    *
522    * <p>
523    * This is equivalent to simply calling <code><jk>new</jk> XmlSerializerBuilder()</code>.
524    *
525    * <p>
526    * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies
527    * the settings of the object called on.
528    *
529    * @return A new {@link XmlSerializerBuilder} object.
530    */
531   public static XmlSerializerBuilder create() {
532      return new XmlSerializerBuilder();
533   }
534
535   /**
536    * Returns the schema serializer based on the settings of this serializer.
537    * @return The schema serializer.
538    */
539   public XmlSerializer getSchemaSerializer() {
540      if (schemaSerializer == null)
541         schemaSerializer = builder().build(XmlSchemaSerializer.class);
542      return schemaSerializer;
543   }
544
545   @Override /* Serializer */
546   public WriterSerializerSession createSession(SerializerSessionArgs args) {
547      return new XmlSerializerSession(this, args);
548   }
549
550   //-----------------------------------------------------------------------------------------------------------------
551   // Properties
552   //-----------------------------------------------------------------------------------------------------------------
553
554   /**
555    * Configuration property:  Auto-detect namespace usage.
556    *
557    * @see #XML_autoDetectNamespaces
558    * @return
559    *    <jk>true</jk> if namespace usage is detected before serialization.
560    */
561   protected final boolean isAutoDetectNamespaces() {
562      return autoDetectNamespaces;
563   }
564
565   /**
566    * Configuration property:  Enable support for XML namespaces.
567    *
568    * @see #XML_enableNamespaces
569    * @return
570    *    <jk>false</jk> if XML output will not contain any namespaces regardless of any other settings.
571    */
572   protected final boolean isEnableNamespaces() {
573      return enableNamespaces;
574   }
575
576   /**
577    * Configuration property:  Add namespace URLs to the root element.
578    *
579    * @see #XML_addNamespaceUrisToRoot
580    * @return
581    *    <jk>true</jk> if {@code xmlns:x} attributes are added to the root element for the default and all mapped namespaces.
582    */
583   protected final boolean isAddNamespaceUrlsToRoot() {
584      return addNamespaceUrlsToRoot;
585   }
586
587   /**
588    * Configuration property:  Add <js>"_type"</js> properties when needed.
589    *
590    * @see #XML_addBeanTypes
591    * @return
592    *    <jk>true</jk> if<js>"_type"</js> properties will be added to beans if their type cannot be inferred
593    *    through reflection.
594    */
595   @Override
596   protected boolean isAddBeanTypes() {
597      return addBeanTypes;
598   }
599
600   /**
601    * Configuration property:  Default namespace.
602    *
603    * @see #XML_defaultNamespace
604    * @return
605    *    The default namespace URI for this document.
606    */
607   protected final Namespace getDefaultNamespace() {
608      return defaultNamespace;
609   }
610
611   /**
612    * Configuration property:  XMLSchema namespace.
613    *
614    * @see #XML_xsNamespace
615    * @return
616    *    The namespace for the <code>XMLSchema</code> namespace, used by the schema generated by the
617    *    {@link XmlSchemaSerializer} class.
618    */
619   protected final Namespace getXsNamespace() {
620      return xsNamespace;
621   }
622
623   /**
624    * Configuration property:  Default namespaces.
625    *
626    * @see #XML_namespaces
627    * @return
628    *    The default list of namespaces associated with this serializer.
629    */
630   protected final Namespace[] getNamespaces() {
631      return namespaces;
632   }
633
634   @Override /* Context */
635   public ObjectMap asMap() {
636      return super.asMap()
637         .append("XmlSerializer", new ObjectMap()
638            .append("autoDetectNamespaces", autoDetectNamespaces)
639            .append("enableNamespaces", enableNamespaces)
640            .append("addNamespaceUrlsToRoot", addNamespaceUrlsToRoot)
641            .append("defaultNamespace", defaultNamespace)
642            .append("xsNamespace", xsNamespace)
643            .append("namespaces", namespaces)
644            .append("addBeanTypes", addBeanTypes)
645         );
646   }
647
648   /**
649    * @deprecated Use {@link #XML_addBeanTypes}
650    */
651   @Deprecated
652   public static final String XML_addBeanTypeProperties = XML_addBeanTypes;
653
654   /**
655    * @deprecated Use {@link #XmlSerializer(PropertyStore, String, String)}
656    */
657   @SuppressWarnings("javadoc")
658   @Deprecated
659   public XmlSerializer(PropertyStore ps, String produces, String...accept) {
660      this(ps, produces, StringUtils.join(accept, ","));
661   }
662}