001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.marshall.xml; 018 019import static org.apache.juneau.commons.utils.CollectionUtils.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021import static org.apache.juneau.commons.utils.SystemUtils.*; 022 023import java.io.*; 024import java.util.*; 025import java.util.concurrent.*; 026 027import org.apache.juneau.commons.bean.*; 028import org.apache.juneau.commons.collections.*; 029import org.apache.juneau.marshall.*; 030import org.apache.juneau.marshall.json.*; 031import org.apache.juneau.marshall.serializer.*; 032import org.apache.juneau.marshall.stream.*; 033 034/** 035 * Serializes POJO models to XML. 036 * 037 * <h5 class='topic'>Media types</h5> 038 * <p> 039 * Handles <c>Accept</c> types: <bc>text/xml</bc> 040 * <p> 041 * Produces <c>Content-Type</c> types: <bc>text/xml</bc> 042 * 043 * <h5 class='topic'>Description</h5> 044 * <p> 045 * See the {@link JsonSerializer} class for details on how Java models map to JSON. 046 * 047 * <p> 048 * For example, the following JSON... 049 * <p class='bjson'> 050 * { 051 * name:<js>'John Smith'</js>, 052 * address: { 053 * streetAddress: <js>'21 2nd Street'</js>, 054 * city: <js>'New York'</js>, 055 * state: <js>'NY'</js>, 056 * postalCode: <js>10021</js> 057 * }, 058 * phoneNumbers: [ 059 * <js>'212 555-1111'</js>, 060 * <js>'212 555-2222'</js> 061 * ], 062 * additionalInfo: <jk>null</jk>, 063 * remote: <jk>false</jk>, 064 * height: <js>62.4</js>, 065 * <js>'fico score'</js>: <js>' > 640'</js> 066 * } 067 * <p> 068 * ...maps to the following XML using the default serializer... 069 * <p class='bxml'> 070 * <xt><object></xt> 071 * <xt><name></xt>John Smith<xt></name></xt> 072 * <xt><address></xt> 073 * <xt><streetAddress></xt>21 2nd Street<xt></streetAddress></xt> 074 * <xt><city></xt>New York<xt></city></xt> 075 * <xt><state></xt>NY<xt></state></xt> 076 * <xt><postalCode></xt>10021<xt></postalCode></xt> 077 * <xt></address></xt> 078 * <xt><phoneNumbers></xt> 079 * <xt><string></xt>212 555-1111<xt></string></xt> 080 * <xt><string></xt>212 555-2222<xt></string></xt> 081 * <xt></phoneNumbers></xt> 082 * <xt><additionalInfo</xt> <xa>_type</xa>=<xs>'null'</xs><xt>></additionalInfo></xt> 083 * <xt><remote></xt>false<xt></remote></xt> 084 * <xt><height></xt>62.4<xt></height></xt> 085 * <xt><fico_x0020_score></xt> &gt; 640<xt></fico_x0020_score></xt> 086 * <xt></object></xt> 087 * 088 * <p> 089 * An additional "add-json-properties" mode is also provided to prevent loss of JSON data types... 090 * <p class='bxml'> 091 * <xt><object></xt> 092 * <xt><name</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt>John Smith<xt></name></xt> 093 * <xt><address</xt> <xa>_type</xa>=<xs>'object'</xs><xt>></xt> 094 * <xt><streetAddress</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt>21 2nd Street<xt></streetAddress></xt> 095 * <xt><city</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt>New York<xt></city></xt> 096 * <xt><state</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt>NY<xt></state></xt> 097 * <xt><postalCode</xt> <xa>_type</xa>=<xs>'number'</xs><xt>></xt>10021<xt></postalCode></xt> 098 * <xt></address></xt> 099 * <xt><phoneNumbers</xt> <xa>_type</xa>=<xs>'array'</xs><xt>></xt> 100 * <xt><string></xt>212 555-1111<xt></string></xt> 101 * <xt><string></xt>212 555-2222<xt></string></xt> 102 * <xt></phoneNumbers></xt> 103 * <xt><additionalInfo</xt> <xa>_type</xa>=<xs>'null'</xs><xt>></additionalInfo></xt> 104 * <xt><remote</xt> <xa>_type</xa>=<xs>'boolean'</xs><xt>></xt>false<xt></remote></xt> 105 * <xt><height</xt> <xa>_type</xa>=<xs>'number'</xs><xt>></xt>62.4<xt></height></xt> 106 * <xt><fico_x0020_score</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt> &gt; 640<xt></fico_x0020_score></xt> 107 * <xt></object></xt> 108 * </p> 109 * 110 * <p> 111 * This serializer provides several serialization options. 112 * Typically, one of the predefined <jsf>DEFAULT</jsf> serializers will be sufficient. 113 * However, custom serializers can be constructed to fine-tune behavior. 114 * 115 * <p> 116 * If an attribute name contains any non-valid XML element characters, they will be escaped using standard 117 * {@code _x####_} notation. 118 * 119 * <h5 class='topic'>Behavior-specific subclasses</h5> 120 * <p> 121 * The following direct subclasses are provided for convenience: 122 * <ul> 123 * <li>{@link Sq} - Default serializer, single quotes. 124 * <li>{@link SqReadable} - Default serializer, single quotes, whitespace added. 125 * </ul> 126 * 127 * <h5 class='section'>Notes:</h5><ul> 128 * <li class='note'>This class is thread safe and reusable. 129 * </ul> 130 * 131 * <h5 class='section'>See Also:</h5><ul> 132 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlSupport">XML</a> 133 134 * </ul> 135 */ 136@SuppressWarnings({ 137 "java:S110", // Inheritance depth acceptable for this class hierarchy 138 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 139 "java:S9149", // Per-format static factories intentionally shadow the parent's. 140 "resource" // Closeable resources are owned by the caller's serializer session; Eclipse JDT @Owning warning is by design. 141}) 142public class XmlSerializer extends WriterSerializer implements XmlMetaProvider, RecordWritable { 143 144 /** 145 * Builder class. 146 */ 147 @SuppressWarnings({ 148 "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name. 149 }) 150 public abstract static class Builder<SELF extends Builder<SELF>> extends WriterSerializer.Builder<SELF> { 151 152 private static final Cache<HashKey,XmlSerializer> CACHE = Cache.of(HashKey.class, XmlSerializer.class).build(); 153 154 private boolean addBeanTypesXml; 155 private boolean addNamespaceUrisToRoot; 156 private boolean disableAutoDetectNamespaces; 157 private boolean disableJsonTags; 158 private boolean enableNamespaces; 159 private Namespace defaultNamespace; 160 private List<Namespace> namespaces; 161 private String textNodeDelimiter; 162 163 /** 164 * Constructor, default settings. 165 */ 166 protected Builder() { 167 produces("text/xml"); 168 addBeanTypesXml = env("XmlSerializer.addBeanTypes", false); 169 addNamespaceUrisToRoot = env("XmlSerializer.addNamespaceUrisToRoot", false); 170 disableAutoDetectNamespaces = env("XmlSerializer.disableAutoDetectNamespaces", false); 171 disableJsonTags = env("XmlSerializer.disableJsonTags", false); 172 enableNamespaces = env("XmlSerializer.enableNamespaces", false); 173 defaultNamespace = null; 174 namespaces = null; 175 textNodeDelimiter = env("XmlSerializer.textNodeDelimiter", ""); 176 } 177 178 /** 179 * Copy constructor. 180 * 181 * @param copyFrom The builder to copy from. 182 * <br>Cannot be <jk>null</jk>. 183 */ 184 protected Builder(Builder<?> copyFrom) { 185 super(reqnn("copyFrom", copyFrom)); 186 addBeanTypesXml = copyFrom.addBeanTypesXml; 187 addNamespaceUrisToRoot = copyFrom.addNamespaceUrisToRoot; 188 defaultNamespace = copyFrom.defaultNamespace; 189 disableAutoDetectNamespaces = copyFrom.disableAutoDetectNamespaces; 190 disableJsonTags = copyFrom.disableJsonTags; 191 enableNamespaces = copyFrom.enableNamespaces; 192 namespaces = copyFrom.namespaces == null ? null : new ArrayList<>(copyFrom.namespaces); 193 textNodeDelimiter = copyFrom.textNodeDelimiter; 194 } 195 196 /** 197 * Copy constructor. 198 * 199 * @param copyFrom The bean to copy from. 200 * <br>Cannot be <jk>null</jk>. 201 */ 202 protected Builder(XmlSerializer copyFrom) { 203 super(reqnn("copyFrom", copyFrom)); 204 addBeanTypesXml = copyFrom.addBeanTypesXml; 205 addNamespaceUrisToRoot = copyFrom.addNamespaceUrlsToRoot; 206 defaultNamespace = copyFrom.getDefaultNamespace(); 207 disableAutoDetectNamespaces = ! copyFrom.autoDetectNamespaces; 208 disableJsonTags = ! copyFrom.addJsonTags; 209 enableNamespaces = copyFrom.enableNamespaces; 210 var ctxNamespaces = copyFrom.getNamespaces(); 211 namespaces = ctxNamespaces.isEmpty() ? null : new ArrayList<>(ctxNamespaces); 212 textNodeDelimiter = copyFrom.getTextNodeDelimiter(); 213 } 214 215 /** 216 * Add <js>"_type"</js> properties when needed. 217 * 218 * <p> 219 * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred 220 * through reflection. 221 * 222 * <p> 223 * When present, this value overrides the {@link org.apache.juneau.marshall.serializer.Serializer.Builder#addBeanTypes() Serializer.Builder#addBeanTypes()} setting and is 224 * provided to customize the behavior of specific serializers in a {@link SerializerSet}. 225 * 226 * @return This object. 227 */ 228 public SELF addBeanTypesXml() { 229 return addBeanTypesXml(true); 230 } 231 232 /** 233 * Same as {@link #addBeanTypesXml()} but allows you to explicitly specify the value. 234 * 235 * @param value The value for this setting. 236 * @return This object. 237 */ 238 public SELF addBeanTypesXml(boolean value) { 239 addBeanTypesXml = value; 240 return self(); 241 } 242 243 /** 244 * Add namespace URLs to the root element. 245 * 246 * <p> 247 * Use this setting to add {@code xmlns:x} attributes to the root element for the default and all mapped namespaces. 248 * 249 * <p> 250 * This setting is ignored if {@link #enableNamespaces()} is not enabled. 251 * 252 * <h5 class='section'>See Also:</h5><ul> 253 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a> 254 * </ul> 255 * 256 * @return This object. 257 */ 258 public SELF addNamespaceUrisToRoot() { 259 return addNamespaceUrisToRoot(true); 260 } 261 262 /** 263 * Same as {@link #addNamespaceUrisToRoot()} but allows you to explicitly specify the value. 264 * 265 * @param value The value for this setting. 266 * @return This object. 267 */ 268 public SELF addNamespaceUrisToRoot(boolean value) { 269 addNamespaceUrisToRoot = value; 270 return self(); 271 } 272 273 @Override /* Overridden from Context.Builder<?> */ 274 public XmlSerializer build() { 275 return cache(CACHE).build(XmlSerializer.class); 276 } 277 278 @Override /* Overridden from Context.Builder<?> */ 279 public abstract SELF copy(); 280 281 /** 282 * Default namespace. 283 * 284 * <p> 285 * Specifies the default namespace URI for this document. 286 * 287 * <h5 class='section'>See Also:</h5><ul> 288 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a> 289 * </ul> 290 * 291 * @param value 292 * The new value for this property. 293 * <br>The default is <js>"juneau: http://www.apache.org/2013/Juneau"</js>. 294 * <br>Can be <jk>null</jk> to specify no namespace. 295 * @return This object. 296 */ 297 public SELF defaultNamespace(Namespace value) { 298 defaultNamespace = value; 299 return self(); 300 } 301 302 /** 303 * Don't auto-detect namespace usage. 304 * 305 * <p> 306 * Don't detect namespace usage before serialization. 307 * 308 * <p> 309 * Used in conjunction with {@link Builder#addNamespaceUrisToRoot()} to reduce the list of namespace URLs appended to the 310 * root element to only those that will be used in the resulting document. 311 * 312 * <p> 313 * If disabled, then the data structure will first be crawled looking for namespaces that will be encountered before 314 * the root element is serialized. 315 * 316 * <p> 317 * This setting is ignored if {@link Builder#enableNamespaces()} is not enabled. 318 * 319 * <h5 class='section'>Notes:</h5><ul> 320 * <li class='note'> 321 * Auto-detection of namespaces can be costly performance-wise. 322 * <br>In high-performance environments, it's recommended that namespace detection be 323 * disabled, and that namespaces be manually defined through the {@link Builder#namespaces(Namespace...)} property. 324 * </ul> 325 * 326 * <h5 class='section'>See Also:</h5><ul> 327 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a> 328 * </ul> 329 * 330 * @return This object. 331 */ 332 public SELF disableAutoDetectNamespaces() { 333 return disableAutoDetectNamespaces(true); 334 } 335 336 /** 337 * Same as {@link #disableAutoDetectNamespaces()} but allows you to explicitly specify the value. 338 * 339 * @param value The value for this setting. 340 * @return This object. 341 */ 342 public SELF disableAutoDetectNamespaces(boolean value) { 343 disableAutoDetectNamespaces = value; 344 return self(); 345 } 346 347 /** 348 * <i><l>XmlSerializer</l> configuration property: </i> Disable use of JSON type identifier tags. 349 * 350 * <p> 351 * When enabled, JSON type tags (e.g. <js>"<string>"</js>) tags and attributes will not be added to the output. 352 * Note that JSON type tags are used to ensure parsers are able to recreate the original data types passed 353 * into the serializer. Disabling JSON tags can cause different data to be parsed (e.g. strings instead of numbers). 354 * 355 * @return This object. 356 */ 357 public SELF disableJsonTags() { 358 return disableJsonTags(true); 359 } 360 361 /** 362 * Same as {@link #disableJsonTags()} but allows you to explicitly specify the value. 363 * 364 * @param value The value for this setting. 365 * @return This object. 366 */ 367 public SELF disableJsonTags(boolean value) { 368 disableJsonTags = value; 369 return self(); 370 } 371 372 /** 373 * Enable support for XML namespaces. 374 * 375 * <p> 376 * If not enabled, XML output will not contain any namespaces regardless of any other settings. 377 * 378 * <h5 class='section'>See Also:</h5><ul> 379 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a> 380 * </ul> 381 * 382 * @return This object. 383 */ 384 public SELF enableNamespaces() { 385 return enableNamespaces(true); 386 } 387 388 /** 389 * Same as {@link #enableNamespaces()} but allows you to explicitly specify the value. 390 * 391 * @param value The value for this setting. 392 * @return This object. 393 */ 394 public SELF enableNamespaces(boolean value) { 395 enableNamespaces = value; 396 return self(); 397 } 398 399 @Override /* Overridden from Context.Builder<?> */ 400 public HashKey hashKey() { 401 // @formatter:off 402 return HashKey.of( 403 super.hashKey(), 404 addBeanTypesXml, 405 addNamespaceUrisToRoot, 406 disableAutoDetectNamespaces, 407 disableJsonTags, 408 enableNamespaces, 409 defaultNamespace, 410 namespaces, 411 textNodeDelimiter 412 ); 413 // @formatter:on 414 } 415 416 /** 417 * Default namespaces. 418 * 419 * <p> 420 * The default list of namespaces associated with this serializer. 421 * 422 * @param values The new value for this property. 423 * <br>Cannot contain <jk>null</jk> values. 424 * @return This object. 425 */ 426 public SELF namespaces(Namespace...values) { 427 reqnns("values", values); 428 namespaces = addAll(namespaces, values); 429 return self(); 430 } 431 432 /** 433 * Enable support for XML namespaces. 434 * 435 * <p> 436 * Shortcut for calling <code>enableNamespaces(<jk>true</jk>)</code>. 437 * 438 * @return This object. 439 */ 440 public SELF ns() { 441 return enableNamespaces(); 442 } 443 444 /** 445 * Text node delimiter. 446 * 447 * <p> 448 * Specifies the delimiter string to insert between consecutive text nodes. 449 * This is useful for adding spacing between text elements to improve readability. 450 * 451 * <p> 452 * The default value is an empty string (no delimiter). 453 * 454 * <h5 class='section'>Example:</h5> 455 * <p class='bjava'> 456 * XmlSerializer.<jsm>create</jsm>() 457 * .textNodeDelimiter(<js>" "</js>) 458 * .build(); 459 * </p> 460 * 461 * <p> 462 * With this setting, serializing: 463 * <p class='bjava'> 464 * <jk>new</jk> Audio().children(<js>"a"</js>, <js>"b"</js>, <jk>new</jk> Strong(<js>"c"</js>)); 465 * </p> 466 * 467 * <p> 468 * Will produce: <code><audio>a b<strong>c</strong></audio></code> 469 * <br>Instead of: <code><audio>ab<strong>c</strong></audio></code> 470 * 471 * @param value 472 * The delimiter string. 473 * <br>Can be <jk>null</jk> (interpreted as empty string). 474 * @return This object. 475 */ 476 public SELF textNodeDelimiter(String value) { 477 textNodeDelimiter = value == null ? "" : value; 478 return self(); 479 } 480 481 482 } 483 484 /** 485 * Concrete default builder leaf for the non-subclassed {@link XmlSerializer#create()} / {@link XmlSerializer#copy()} path. 486 */ 487 public static final class DefaultBuilder extends Builder<DefaultBuilder> { 488 489 DefaultBuilder() {} 490 491 DefaultBuilder(XmlSerializer copyFrom) { 492 super(copyFrom); 493 } 494 495 DefaultBuilder(Builder<?> copyFrom) { 496 super(copyFrom); 497 } 498 499 @Override /* Overridden from Context.Builder<?> */ 500 public DefaultBuilder copy() { 501 return new DefaultBuilder(this); 502 } 503 } 504 505 /** Default serializer without namespaces. */ 506 public static class Ns extends XmlSerializer { 507 508 /** 509 * Constructor. 510 * 511 * @param builder The builder for this object. 512 * <br>Cannot be <jk>null</jk>. 513 */ 514 public Ns(Builder<?> builder) { 515 super(reqnn("builder", builder).enableNamespaces()); 516 } 517 } 518 519 /** Default serializer without namespaces, single quotes. */ 520 public static class NsSq extends XmlSerializer { 521 522 /** 523 * Constructor. 524 * 525 * @param builder The builder for this object. 526 * <br>Cannot be <jk>null</jk>. 527 */ 528 public NsSq(Builder<?> builder) { 529 super(reqnn("builder", builder).enableNamespaces().quoteChar('\'')); 530 } 531 } 532 533 /** Default serializer without namespaces, single quotes, with whitespace. */ 534 public static class NsSqReadable extends XmlSerializer { 535 536 /** 537 * Constructor. 538 * 539 * @param builder The builder for this object. 540 * <br>Cannot be <jk>null</jk>. 541 */ 542 public NsSqReadable(Builder<?> builder) { 543 super(reqnn("builder", builder).enableNamespaces().quoteChar('\'').useWhitespace()); 544 } 545 } 546 547 /** Default serializer, single quotes. */ 548 public static class Sq extends XmlSerializer { 549 550 /** 551 * Constructor. 552 * 553 * @param builder The builder for this object. 554 * <br>Cannot be <jk>null</jk>. 555 */ 556 public Sq(Builder<?> builder) { 557 super(reqnn("builder", builder).quoteChar('\'')); 558 } 559 } 560 561 /** Default serializer, single quotes, whitespace added. */ 562 public static class SqReadable extends XmlSerializer { 563 564 /** 565 * Constructor. 566 * 567 * @param builder The builder for this object. 568 * <br>Cannot be <jk>null</jk>. 569 */ 570 public SqReadable(Builder<?> builder) { 571 super(reqnn("builder", builder).quoteChar('\'').useWhitespace()); 572 } 573 } 574 575 576 /** Default serializer without namespaces. */ 577 public static final XmlSerializer DEFAULT = new XmlSerializer(create()); 578 /** Default serializer without namespaces, with single quotes. */ 579 public static final XmlSerializer DEFAULT_SQ = new Sq(create()); 580 581 /** Default serializer without namespaces, with single quotes, whitespace added. */ 582 public static final XmlSerializer DEFAULT_SQ_READABLE = new SqReadable(create()); 583 584 /** Default serializer, all default settings. */ 585 public static final XmlSerializer DEFAULT_NS = new Ns(create()); 586 587 /** Default serializer, single quotes. */ 588 public static final XmlSerializer DEFAULT_NS_SQ = new NsSq(create()); 589 590 /** Default serializer, single quotes, whitespace added. */ 591 public static final XmlSerializer DEFAULT_NS_SQ_READABLE = new NsSqReadable(create()); 592 593 protected static final Namespace DEFAULT_JUNEAU_NAMESPACE = Namespace.of("juneau", "http://www.apache.org/2013/Juneau"); 594 595 /** 596 * Creates a new builder for this object. 597 * 598 * @return A new builder. 599 */ 600 @SuppressWarnings({ 601 "java:S1452" // Builder<?> wildcard return intentional; callers chain via fluent API without needing the concrete type 602 }) 603 public static Builder<?> create() { 604 return new DefaultBuilder(); 605 } 606 607 protected final boolean addBeanTypesXml; 608 protected final boolean addJsonTags; 609 protected final boolean addNamespaceUrlsToRoot; 610 protected final boolean autoDetectNamespaces; 611 protected final boolean enableNamespaces; 612 private final Namespace defaultNamespace; 613 private final List<Namespace> namespaces; 614 private final String textNodeDelimiter; 615 616 private final boolean addBeanTypes2; 617 private final Map<ClassMeta<?>,XmlClassMeta> xmlClassMetas = new ConcurrentHashMap<>(); 618 private final Map<BeanMeta<?>,XmlBeanMeta> xmlBeanMetas = new ConcurrentHashMap<>(); 619 private final Map<BeanPropertyMeta,XmlBeanPropertyMeta> xmlBeanPropertyMetas = new ConcurrentHashMap<>(); 620 621 /** 622 * Constructor. 623 * 624 * @param builder 625 * The builder for this object. 626 */ 627 public XmlSerializer(Builder<?> builder) { 628 super(builder); 629 addBeanTypesXml = builder.addBeanTypesXml; 630 addJsonTags = ! builder.disableJsonTags; 631 addNamespaceUrlsToRoot = builder.addNamespaceUrisToRoot; 632 autoDetectNamespaces = ! builder.disableAutoDetectNamespaces; 633 defaultNamespace = nn(builder.defaultNamespace) ? builder.defaultNamespace : DEFAULT_JUNEAU_NAMESPACE; 634 enableNamespaces = builder.enableNamespaces; 635 namespaces = u(nn(builder.namespaces) ? new ArrayList<>(builder.namespaces) : l()); 636 textNodeDelimiter = builder.textNodeDelimiter; 637 addBeanTypes2 = addBeanTypesXml || super.isAddBeanTypes(); 638 } 639 640 @Override /* Overridden from Context */ 641 public Builder<?> copy() { 642 return new DefaultBuilder(this); 643 } 644 645 @Override /* Overridden from Context */ 646 public XmlSerializerSession.Builder<?> createSession() { 647 return XmlSerializerSession.create(this); 648 } 649 650 @Override /* Overridden from Context */ 651 public XmlSerializerSession getSession() { return createSession().build(); } 652 653 /** 654 * Convenience delegator for the whole-value {@link RecordWriter} using <b>default session 655 * arguments</b>. The real implementation lives on 656 * {@link XmlSerializerSession#writeRecords(Object)}. 657 * 658 * @param output The output. 659 * @return A new {@link RecordWriter}. 660 * @throws IOException If a problem occurred opening the underlying output. 661 */ 662 @Override /* RecordWritable */ 663 public RecordWriter writeRecords(Object output) throws IOException { 664 return getSession().writeRecords(output); 665 } 666 667 /** 668 * The XML record writer is buffered/{@link RecordAdapter}-backed, not O(1) streaming. 669 * 670 * @return Always <jk>false</jk>. 671 */ 672 @Override /* RecordWritable */ 673 public boolean isRecordStreaming() { return false; } 674 675 @Override /* Overridden from XmlMetaProvider */ 676 public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) { 677 return xmlBeanMetas.computeIfAbsent(bm, k -> new XmlBeanMeta(k, this)); 678 } 679 680 @Override /* Overridden from XmlMetaProvider */ 681 public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) { 682 return xmlBeanPropertyMetas.computeIfAbsent(bpm, k -> new XmlBeanPropertyMeta(k.getDelegateFor(), this)); 683 } 684 685 @Override /* Overridden from XmlMetaProvider */ 686 public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) { 687 return xmlClassMetas.computeIfAbsent(cm, k -> new XmlClassMeta(k, this)); 688 } 689 690 /** 691 * Default namespace. 692 * 693 * @see Builder#defaultNamespace(Namespace) 694 * @return 695 * The default namespace URI for this document. 696 */ 697 protected final Namespace getDefaultNamespace() { return defaultNamespace; } 698 699 /** 700 * Default namespaces. 701 * 702 * @see Builder#namespaces(Namespace...) 703 * @return 704 * The default list of namespaces associated with this serializer. 705 * <br>Never <jk>null</jk>. 706 * <br>List is unmodifiable. 707 */ 708 protected final List<Namespace> getNamespaces() { return namespaces; } 709 710 /** 711 * Text node delimiter. 712 * 713 * @see Builder#textNodeDelimiter(String) 714 * @return 715 * The delimiter string to insert between consecutive text nodes. 716 * <br>Never <jk>null</jk>. 717 */ 718 protected final String getTextNodeDelimiter() { return textNodeDelimiter; } 719 720 /** 721 * Add <js>"_type"</js> properties when needed. 722 * 723 * @see Builder#addBeanTypesXml() 724 * @return 725 * <jk>true</jk> if<js>"_type"</js> properties will be added to beans if their type cannot be inferred 726 * through reflection. 727 */ 728 @Override 729 protected boolean isAddBeanTypes() { return addBeanTypes2; } 730 731 /** 732 * Add namespace URLs to the root element. 733 * 734 * @see Builder#addNamespaceUrisToRoot() 735 * @return 736 * <jk>true</jk> if {@code xmlns:x} attributes are added to the root element for the default and all mapped namespaces. 737 */ 738 protected final boolean isAddNamespaceUrlsToRoot() { return addNamespaceUrlsToRoot; } 739 740 /** 741 * Auto-detect namespace usage. 742 * 743 * @see Builder#disableAutoDetectNamespaces() 744 * @return 745 * <jk>true</jk> if namespace usage is detected before serialization. 746 */ 747 protected final boolean isAutoDetectNamespaces() { return autoDetectNamespaces; } 748 749 /** 750 * Enable support for XML namespaces. 751 * 752 * @see Builder#enableNamespaces() 753 * @return 754 * <jk>false</jk> if XML output will not contain any namespaces regardless of any other settings. 755 */ 756 protected final boolean isEnableNamespaces() { return enableNamespaces; } 757 758 @Override /* Overridden from WriterSerializer */ 759 protected FluentMap<String,Object> properties() { 760 return super.properties() 761 .a("addBeanTypes", addBeanTypes2) 762 .a("addNamespaceUrlsToRoot", addNamespaceUrlsToRoot) 763 .a("autoDetectNamespaces", autoDetectNamespaces) 764 .a("defaultNamespace", defaultNamespace) 765 .a("enableNamespaces", enableNamespaces) 766 .a("namespaces", namespaces); 767 } 768}