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.xml; 018 019import static org.apache.juneau.commons.utils.AssertionUtils.*; 020import static org.apache.juneau.commons.utils.CollectionUtils.*; 021import static org.apache.juneau.commons.utils.Utils.*; 022 023import java.lang.annotation.*; 024import java.nio.charset.*; 025import java.util.*; 026import java.util.concurrent.*; 027 028import org.apache.juneau.*; 029import org.apache.juneau.commons.collections.*; 030import org.apache.juneau.commons.function.*; 031import org.apache.juneau.commons.reflect.*; 032import org.apache.juneau.json.*; 033import org.apache.juneau.serializer.*; 034 035/** 036 * Serializes POJO models to XML. 037 * 038 * <h5 class='topic'>Media types</h5> 039 * <p> 040 * Handles <c>Accept</c> types: <bc>text/xml</bc> 041 * <p> 042 * Produces <c>Content-Type</c> types: <bc>text/xml</bc> 043 * 044 * <h5 class='topic'>Description</h5> 045 * <p> 046 * See the {@link JsonSerializer} class for details on how Java models map to JSON. 047 * 048 * <p> 049 * For example, the following JSON... 050 * <p class='bjson'> 051 * { 052 * name:<js>'John Smith'</js>, 053 * address: { 054 * streetAddress: <js>'21 2nd Street'</js>, 055 * city: <js>'New York'</js>, 056 * state: <js>'NY'</js>, 057 * postalCode: <js>10021</js> 058 * }, 059 * phoneNumbers: [ 060 * <js>'212 555-1111'</js>, 061 * <js>'212 555-2222'</js> 062 * ], 063 * additionalInfo: <jk>null</jk>, 064 * remote: <jk>false</jk>, 065 * height: <js>62.4</js>, 066 * <js>'fico score'</js>: <js>' > 640'</js> 067 * } 068 * <p> 069 * ...maps to the following XML using the default serializer... 070 * <p class='bxml'> 071 * <xt><object></xt> 072 * <xt><name></xt>John Smith<xt></name></xt> 073 * <xt><address></xt> 074 * <xt><streetAddress></xt>21 2nd Street<xt></streetAddress></xt> 075 * <xt><city></xt>New York<xt></city></xt> 076 * <xt><state></xt>NY<xt></state></xt> 077 * <xt><postalCode></xt>10021<xt></postalCode></xt> 078 * <xt></address></xt> 079 * <xt><phoneNumbers></xt> 080 * <xt><string></xt>212 555-1111<xt></string></xt> 081 * <xt><string></xt>212 555-2222<xt></string></xt> 082 * <xt></phoneNumbers></xt> 083 * <xt><additionalInfo</xt> <xa>_type</xa>=<xs>'null'</xs><xt>></additionalInfo></xt> 084 * <xt><remote></xt>false<xt></remote></xt> 085 * <xt><height></xt>62.4<xt></height></xt> 086 * <xt><fico_x0020_score></xt> &gt; 640<xt></fico_x0020_score></xt> 087 * <xt></object></xt> 088 * 089 * <p> 090 * An additional "add-json-properties" mode is also provided to prevent loss of JSON data types... 091 * <p class='bxml'> 092 * <xt><object></xt> 093 * <xt><name</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt>John Smith<xt></name></xt> 094 * <xt><address</xt> <xa>_type</xa>=<xs>'object'</xs><xt>></xt> 095 * <xt><streetAddress</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt>21 2nd Street<xt></streetAddress></xt> 096 * <xt><city</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt>New York<xt></city></xt> 097 * <xt><state</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt>NY<xt></state></xt> 098 * <xt><postalCode</xt> <xa>_type</xa>=<xs>'number'</xs><xt>></xt>10021<xt></postalCode></xt> 099 * <xt></address></xt> 100 * <xt><phoneNumbers</xt> <xa>_type</xa>=<xs>'array'</xs><xt>></xt> 101 * <xt><string></xt>212 555-1111<xt></string></xt> 102 * <xt><string></xt>212 555-2222<xt></string></xt> 103 * <xt></phoneNumbers></xt> 104 * <xt><additionalInfo</xt> <xa>_type</xa>=<xs>'null'</xs><xt>></additionalInfo></xt> 105 * <xt><remote</xt> <xa>_type</xa>=<xs>'boolean'</xs><xt>></xt>false<xt></remote></xt> 106 * <xt><height</xt> <xa>_type</xa>=<xs>'number'</xs><xt>></xt>62.4<xt></height></xt> 107 * <xt><fico_x0020_score</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt> &gt; 640<xt></fico_x0020_score></xt> 108 * <xt></object></xt> 109 * </p> 110 * 111 * <p> 112 * This serializer provides several serialization options. 113 * Typically, one of the predefined <jsf>DEFAULT</jsf> serializers will be sufficient. 114 * However, custom serializers can be constructed to fine-tune behavior. 115 * 116 * <p> 117 * If an attribute name contains any non-valid XML element characters, they will be escaped using standard 118 * {@code _x####_} notation. 119 * 120 * <h5 class='topic'>Behavior-specific subclasses</h5> 121 * <p> 122 * The following direct subclasses are provided for convenience: 123 * <ul> 124 * <li>{@link Sq} - Default serializer, single quotes. 125 * <li>{@link SqReadable} - Default serializer, single quotes, whitespace added. 126 * </ul> 127 * 128 * <h5 class='section'>Notes:</h5><ul> 129 * <li class='note'>This class is thread safe and reusable. 130 * </ul> 131 * 132 * <h5 class='section'>See Also:</h5><ul> 133 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlBasics">XML Basics</a> 134 135 * </ul> 136 */ 137public class XmlSerializer extends WriterSerializer implements XmlMetaProvider { 138 /** 139 * Builder class. 140 */ 141 public static class Builder extends WriterSerializer.Builder { 142 143 private static final Cache<HashKey,XmlSerializer> CACHE = Cache.of(HashKey.class, XmlSerializer.class).build(); 144 145 private boolean addBeanTypesXml; 146 private boolean addNamespaceUrisToRoot; 147 private boolean disableAutoDetectNamespaces; 148 private boolean disableJsonTags; 149 private boolean enableNamespaces; 150 private Namespace defaultNamespace; 151 private List<Namespace> namespaces; 152 private String textNodeDelimiter; 153 154 /** 155 * Constructor, default settings. 156 */ 157 protected Builder() { 158 produces("text/xml"); 159 addBeanTypesXml = env("XmlSerializer.addBeanTypes", false); 160 addNamespaceUrisToRoot = env("XmlSerializer.addNamespaceUrisToRoot", false); 161 disableAutoDetectNamespaces = env("XmlSerializer.disableAutoDetectNamespaces", false); 162 disableJsonTags = env("XmlSerializer.disableJsonTags", false); 163 enableNamespaces = env("XmlSerializer.enableNamespaces", false); 164 defaultNamespace = null; 165 namespaces = null; 166 textNodeDelimiter = env("XmlSerializer.textNodeDelimiter", ""); 167 } 168 169 /** 170 * Copy constructor. 171 * 172 * @param copyFrom The builder to copy from. 173 * <br>Cannot be <jk>null</jk>. 174 */ 175 protected Builder(Builder copyFrom) { 176 super(assertArgNotNull("copyFrom", copyFrom)); 177 addBeanTypesXml = copyFrom.addBeanTypesXml; 178 addNamespaceUrisToRoot = copyFrom.addNamespaceUrisToRoot; 179 defaultNamespace = copyFrom.defaultNamespace; 180 disableAutoDetectNamespaces = copyFrom.disableAutoDetectNamespaces; 181 disableJsonTags = copyFrom.disableJsonTags; 182 enableNamespaces = copyFrom.enableNamespaces; 183 namespaces = copyFrom.namespaces == null ? null : new ArrayList<>(copyFrom.namespaces); 184 textNodeDelimiter = copyFrom.textNodeDelimiter; 185 } 186 187 /** 188 * Copy constructor. 189 * 190 * @param copyFrom The bean to copy from. 191 * <br>Cannot be <jk>null</jk>. 192 */ 193 protected Builder(XmlSerializer copyFrom) { 194 super(assertArgNotNull("copyFrom", copyFrom)); 195 addBeanTypesXml = copyFrom.addBeanTypesXml; 196 addNamespaceUrisToRoot = copyFrom.addNamespaceUrlsToRoot; 197 defaultNamespace = copyFrom.getDefaultNamespace(); 198 disableAutoDetectNamespaces = ! copyFrom.autoDetectNamespaces; 199 disableJsonTags = ! copyFrom.addJsonTags; 200 enableNamespaces = copyFrom.enableNamespaces; 201 var ctxNamespaces = copyFrom.getNamespaces(); 202 namespaces = ctxNamespaces.isEmpty() ? null : new ArrayList<>(ctxNamespaces); 203 textNodeDelimiter = copyFrom.getTextNodeDelimiter(); 204 } 205 206 @Override /* Overridden from Builder */ 207 public Builder accept(String value) { 208 super.accept(value); 209 return this; 210 } 211 212 @Override /* Overridden from Builder */ 213 public Builder addBeanTypes() { 214 super.addBeanTypes(); 215 return this; 216 } 217 218 @Override /* Overridden from Builder */ 219 public Builder addBeanTypes(boolean value) { 220 super.addBeanTypes(value); 221 return this; 222 } 223 224 /** 225 * Add <js>"_type"</js> properties when needed. 226 * 227 * <p> 228 * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred 229 * through reflection. 230 * 231 * <p> 232 * When present, this value overrides the {@link org.apache.juneau.serializer.Serializer.Builder#addBeanTypes()} setting and is 233 * provided to customize the behavior of specific serializers in a {@link SerializerSet}. 234 * 235 * @return This object. 236 */ 237 public Builder addBeanTypesXml() { 238 return addBeanTypesXml(true); 239 } 240 241 /** 242 * Same as {@link #addBeanTypesXml()} but allows you to explicitly specify the value. 243 * 244 * @param value The value for this setting. 245 * @return This object. 246 */ 247 public Builder addBeanTypesXml(boolean value) { 248 addBeanTypesXml = value; 249 return this; 250 } 251 252 /** 253 * Add namespace URLs to the root element. 254 * 255 * <p> 256 * Use this setting to add {@code xmlns:x} attributes to the root element for the default and all mapped namespaces. 257 * 258 * <p> 259 * This setting is ignored if {@link #enableNamespaces()} is not enabled. 260 * 261 * <h5 class='section'>See Also:</h5><ul> 262 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a> 263 * </ul> 264 * 265 * @return This object. 266 */ 267 public Builder addNamespaceUrisToRoot() { 268 return addNamespaceUrisToRoot(true); 269 } 270 271 /** 272 * Same as {@link #addNamespaceUrisToRoot()} but allows you to explicitly specify the value. 273 * 274 * @param value The value for this setting. 275 * @return This object. 276 */ 277 public Builder addNamespaceUrisToRoot(boolean value) { 278 addNamespaceUrisToRoot = value; 279 return this; 280 } 281 282 @Override /* Overridden from Builder */ 283 public Builder addRootType() { 284 super.addRootType(); 285 return this; 286 } 287 288 @Override /* Overridden from Builder */ 289 public Builder addRootType(boolean value) { 290 super.addRootType(value); 291 return this; 292 } 293 294 @Override /* Overridden from Builder */ 295 public Builder annotations(Annotation...values) { 296 super.annotations(values); 297 return this; 298 } 299 300 @Override /* Overridden from Builder */ 301 public Builder apply(AnnotationWorkList work) { 302 super.apply(work); 303 return this; 304 } 305 306 @Override /* Overridden from Builder */ 307 public Builder applyAnnotations(Class<?>...from) { 308 super.applyAnnotations(from); 309 return this; 310 } 311 312 @Override /* Overridden from Builder */ 313 public Builder applyAnnotations(Object...from) { 314 super.applyAnnotations(from); 315 return this; 316 } 317 318 @Override /* Overridden from Builder */ 319 public Builder beanClassVisibility(Visibility value) { 320 super.beanClassVisibility(value); 321 return this; 322 } 323 324 @Override /* Overridden from Builder */ 325 public Builder beanConstructorVisibility(Visibility value) { 326 super.beanConstructorVisibility(value); 327 return this; 328 } 329 330 @Override /* Overridden from Builder */ 331 public Builder beanContext(BeanContext value) { 332 super.beanContext(value); 333 return this; 334 } 335 336 @Override /* Overridden from Builder */ 337 public Builder beanContext(BeanContext.Builder value) { 338 super.beanContext(value); 339 return this; 340 } 341 342 @Override /* Overridden from Builder */ 343 public Builder beanDictionary(java.lang.Class<?>...values) { 344 super.beanDictionary(values); 345 return this; 346 } 347 348 @Override /* Overridden from Builder */ 349 public Builder beanFieldVisibility(Visibility value) { 350 super.beanFieldVisibility(value); 351 return this; 352 } 353 354 @Override /* Overridden from Builder */ 355 public Builder beanInterceptor(Class<?> on, Class<? extends org.apache.juneau.swap.BeanInterceptor<?>> value) { 356 super.beanInterceptor(on, value); 357 return this; 358 } 359 360 @Override /* Overridden from Builder */ 361 public Builder beanMapPutReturnsOldValue() { 362 super.beanMapPutReturnsOldValue(); 363 return this; 364 } 365 366 @Override /* Overridden from Builder */ 367 public Builder beanMethodVisibility(Visibility value) { 368 super.beanMethodVisibility(value); 369 return this; 370 } 371 372 @Override /* Overridden from Builder */ 373 public Builder beanProperties(Class<?> beanClass, String properties) { 374 super.beanProperties(beanClass, properties); 375 return this; 376 } 377 378 @Override /* Overridden from Builder */ 379 public Builder beanProperties(Map<String,Object> values) { 380 super.beanProperties(values); 381 return this; 382 } 383 384 @Override /* Overridden from Builder */ 385 public Builder beanProperties(String beanClassName, String properties) { 386 super.beanProperties(beanClassName, properties); 387 return this; 388 } 389 390 @Override /* Overridden from Builder */ 391 public Builder beanPropertiesExcludes(Class<?> beanClass, String properties) { 392 super.beanPropertiesExcludes(beanClass, properties); 393 return this; 394 } 395 396 @Override /* Overridden from Builder */ 397 public Builder beanPropertiesExcludes(Map<String,Object> values) { 398 super.beanPropertiesExcludes(values); 399 return this; 400 } 401 402 @Override /* Overridden from Builder */ 403 public Builder beanPropertiesExcludes(String beanClassName, String properties) { 404 super.beanPropertiesExcludes(beanClassName, properties); 405 return this; 406 } 407 408 @Override /* Overridden from Builder */ 409 public Builder beanPropertiesReadOnly(Class<?> beanClass, String properties) { 410 super.beanPropertiesReadOnly(beanClass, properties); 411 return this; 412 } 413 414 @Override /* Overridden from Builder */ 415 public Builder beanPropertiesReadOnly(Map<String,Object> values) { 416 super.beanPropertiesReadOnly(values); 417 return this; 418 } 419 420 @Override /* Overridden from Builder */ 421 public Builder beanPropertiesReadOnly(String beanClassName, String properties) { 422 super.beanPropertiesReadOnly(beanClassName, properties); 423 return this; 424 } 425 426 @Override /* Overridden from Builder */ 427 public Builder beanPropertiesWriteOnly(Class<?> beanClass, String properties) { 428 super.beanPropertiesWriteOnly(beanClass, properties); 429 return this; 430 } 431 432 @Override /* Overridden from Builder */ 433 public Builder beanPropertiesWriteOnly(Map<String,Object> values) { 434 super.beanPropertiesWriteOnly(values); 435 return this; 436 } 437 438 @Override /* Overridden from Builder */ 439 public Builder beanPropertiesWriteOnly(String beanClassName, String properties) { 440 super.beanPropertiesWriteOnly(beanClassName, properties); 441 return this; 442 } 443 444 @Override /* Overridden from Builder */ 445 public Builder beansRequireDefaultConstructor() { 446 super.beansRequireDefaultConstructor(); 447 return this; 448 } 449 450 @Override /* Overridden from Builder */ 451 public Builder beansRequireSerializable() { 452 super.beansRequireSerializable(); 453 return this; 454 } 455 456 @Override /* Overridden from Builder */ 457 public Builder beansRequireSettersForGetters() { 458 super.beansRequireSettersForGetters(); 459 return this; 460 } 461 462 @Override /* Overridden from Context.Builder */ 463 public XmlSerializer build() { 464 return cache(CACHE).build(XmlSerializer.class); 465 } 466 467 @Override /* Overridden from Builder */ 468 public Builder cache(Cache<HashKey,? extends org.apache.juneau.Context> value) { 469 super.cache(value); 470 return this; 471 } 472 473 @Override /* Overridden from Context.Builder */ 474 public Builder copy() { 475 return new Builder(this); 476 } 477 478 @Override /* Overridden from Builder */ 479 public Builder debug() { 480 super.debug(); 481 return this; 482 } 483 484 @Override /* Overridden from Builder */ 485 public Builder debug(boolean value) { 486 super.debug(value); 487 return this; 488 } 489 490 /** 491 * Default namespace. 492 * 493 * <p> 494 * Specifies the default namespace URI for this document. 495 * 496 * <h5 class='section'>See Also:</h5><ul> 497 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a> 498 * </ul> 499 * 500 * @param value 501 * The new value for this property. 502 * <br>The default is <js>"juneau: http://www.apache.org/2013/Juneau"</js>. 503 * <br>Can be <jk>null</jk> to specify no namespace. 504 * @return This object. 505 */ 506 public Builder defaultNamespace(Namespace value) { 507 defaultNamespace = value; 508 return this; 509 } 510 511 @Override /* Overridden from Builder */ 512 public Builder detectRecursions() { 513 super.detectRecursions(); 514 return this; 515 } 516 517 @Override /* Overridden from Builder */ 518 public Builder detectRecursions(boolean value) { 519 super.detectRecursions(value); 520 return this; 521 } 522 523 @Override /* Overridden from Builder */ 524 public Builder dictionaryOn(Class<?> on, java.lang.Class<?>...values) { 525 super.dictionaryOn(on, values); 526 return this; 527 } 528 529 /** 530 * Don't auto-detect namespace usage. 531 * 532 * <p> 533 * Don't detect namespace usage before serialization. 534 * 535 * <p> 536 * Used in conjunction with {@link Builder#addNamespaceUrisToRoot()} to reduce the list of namespace URLs appended to the 537 * root element to only those that will be used in the resulting document. 538 * 539 * <p> 540 * If disabled, then the data structure will first be crawled looking for namespaces that will be encountered before 541 * the root element is serialized. 542 * 543 * <p> 544 * This setting is ignored if {@link Builder#enableNamespaces()} is not enabled. 545 * 546 * <h5 class='section'>Notes:</h5><ul> 547 * <li class='note'> 548 * Auto-detection of namespaces can be costly performance-wise. 549 * <br>In high-performance environments, it's recommended that namespace detection be 550 * disabled, and that namespaces be manually defined through the {@link Builder#namespaces(Namespace...)} property. 551 * </ul> 552 * 553 * <h5 class='section'>See Also:</h5><ul> 554 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a> 555 * </ul> 556 * 557 * @return This object. 558 */ 559 public Builder disableAutoDetectNamespaces() { 560 return disableAutoDetectNamespaces(true); 561 } 562 563 /** 564 * Same as {@link #disableAutoDetectNamespaces()} but allows you to explicitly specify the value. 565 * 566 * @param value The value for this setting. 567 * @return This object. 568 */ 569 public Builder disableAutoDetectNamespaces(boolean value) { 570 disableAutoDetectNamespaces = value; 571 return this; 572 } 573 574 @Override /* Overridden from Builder */ 575 public Builder disableBeansRequireSomeProperties() { 576 super.disableBeansRequireSomeProperties(); 577 return this; 578 } 579 580 @Override /* Overridden from Builder */ 581 public Builder disableIgnoreMissingSetters() { 582 super.disableIgnoreMissingSetters(); 583 return this; 584 } 585 586 @Override /* Overridden from Builder */ 587 public Builder disableIgnoreTransientFields() { 588 super.disableIgnoreTransientFields(); 589 return this; 590 } 591 592 @Override /* Overridden from Builder */ 593 public Builder disableIgnoreUnknownNullBeanProperties() { 594 super.disableIgnoreUnknownNullBeanProperties(); 595 return this; 596 } 597 598 @Override /* Overridden from Builder */ 599 public Builder disableInterfaceProxies() { 600 super.disableInterfaceProxies(); 601 return this; 602 } 603 604 /** 605 * <i><l>XmlSerializer</l> configuration property: </i> Disable use of JSON type identifier tags. 606 * 607 * <p> 608 * When enabled, JSON type tags (e.g. <js>"<string>"</js>) tags and attributes will not be added to the output. 609 * Note that JSON type tags are used to ensure parsers are able to recreate the original data types passed 610 * into the serializer. Disabling JSON tags can cause different data to be parsed (e.g. strings instead of numbers). 611 * 612 * @return This object. 613 */ 614 public Builder disableJsonTags() { 615 return disableJsonTags(true); 616 } 617 618 /** 619 * Same as {@link #disableJsonTags()} but allows you to explicitly specify the value. 620 * 621 * @param value The value for this setting. 622 * @return This object. 623 */ 624 public Builder disableJsonTags(boolean value) { 625 disableJsonTags = value; 626 return this; 627 } 628 629 /** 630 * Enable support for XML namespaces. 631 * 632 * <p> 633 * If not enabled, XML output will not contain any namespaces regardless of any other settings. 634 * 635 * <h5 class='section'>See Also:</h5><ul> 636 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlNamespaces">Namespaces</a> 637 * </ul> 638 * 639 * @return This object. 640 */ 641 public Builder enableNamespaces() { 642 return enableNamespaces(true); 643 } 644 645 /** 646 * Same as {@link #enableNamespaces()} but allows you to explicitly specify the value. 647 * 648 * @param value The value for this setting. 649 * @return This object. 650 */ 651 public Builder enableNamespaces(boolean value) { 652 enableNamespaces = value; 653 return this; 654 } 655 656 @Override /* Overridden from Builder */ 657 public <T> Builder example(Class<T> pojoClass, String json) { 658 super.example(pojoClass, json); 659 return this; 660 } 661 662 @Override /* Overridden from Builder */ 663 public <T> Builder example(Class<T> pojoClass, T o) { 664 super.example(pojoClass, o); 665 return this; 666 } 667 668 @Override /* Overridden from Builder */ 669 public Builder fileCharset(Charset value) { 670 super.fileCharset(value); 671 return this; 672 } 673 674 @Override /* Overridden from Builder */ 675 public Builder findFluentSetters() { 676 super.findFluentSetters(); 677 return this; 678 } 679 680 @Override /* Overridden from Builder */ 681 public Builder findFluentSetters(Class<?> on) { 682 super.findFluentSetters(on); 683 return this; 684 } 685 686 @Override /* Overridden from Context.Builder */ 687 public HashKey hashKey() { 688 // @formatter:off 689 return HashKey.of( 690 super.hashKey(), 691 addBeanTypesXml, 692 addNamespaceUrisToRoot, 693 disableAutoDetectNamespaces, 694 disableJsonTags, 695 enableNamespaces, 696 defaultNamespace, 697 namespaces, 698 textNodeDelimiter 699 ); 700 // @formatter:on 701 } 702 703 @Override /* Overridden from Builder */ 704 public Builder ignoreInvocationExceptionsOnGetters() { 705 super.ignoreInvocationExceptionsOnGetters(); 706 return this; 707 } 708 709 @Override /* Overridden from Builder */ 710 public Builder ignoreInvocationExceptionsOnSetters() { 711 super.ignoreInvocationExceptionsOnSetters(); 712 return this; 713 } 714 715 @Override /* Overridden from Builder */ 716 public Builder ignoreRecursions() { 717 super.ignoreRecursions(); 718 return this; 719 } 720 721 @Override /* Overridden from Builder */ 722 public Builder ignoreRecursions(boolean value) { 723 super.ignoreRecursions(value); 724 return this; 725 } 726 727 @Override /* Overridden from Builder */ 728 public Builder ignoreUnknownBeanProperties() { 729 super.ignoreUnknownBeanProperties(); 730 return this; 731 } 732 733 @Override /* Overridden from Builder */ 734 public Builder ignoreUnknownEnumValues() { 735 super.ignoreUnknownEnumValues(); 736 return this; 737 } 738 739 @Override /* Overridden from Builder */ 740 public Builder impl(Context value) { 741 super.impl(value); 742 return this; 743 } 744 745 @Override /* Overridden from Builder */ 746 public Builder implClass(Class<?> interfaceClass, Class<?> implClass) { 747 super.implClass(interfaceClass, implClass); 748 return this; 749 } 750 751 @Override /* Overridden from Builder */ 752 public Builder implClasses(Map<Class<?>,Class<?>> values) { 753 super.implClasses(values); 754 return this; 755 } 756 757 @Override /* Overridden from Builder */ 758 public Builder initialDepth(int value) { 759 super.initialDepth(value); 760 return this; 761 } 762 763 @Override /* Overridden from Builder */ 764 public Builder interfaceClass(Class<?> on, Class<?> value) { 765 super.interfaceClass(on, value); 766 return this; 767 } 768 769 @Override /* Overridden from Builder */ 770 public Builder interfaces(java.lang.Class<?>...value) { 771 super.interfaces(value); 772 return this; 773 } 774 775 @Override /* Overridden from Builder */ 776 public Builder keepNullProperties() { 777 super.keepNullProperties(); 778 return this; 779 } 780 781 @Override /* Overridden from Builder */ 782 public Builder keepNullProperties(boolean value) { 783 super.keepNullProperties(value); 784 return this; 785 } 786 787 @Override /* Overridden from Builder */ 788 public Builder listener(Class<? extends org.apache.juneau.serializer.SerializerListener> value) { 789 super.listener(value); 790 return this; 791 } 792 793 @Override /* Overridden from Builder */ 794 public Builder locale(Locale value) { 795 super.locale(value); 796 return this; 797 } 798 799 @Override /* Overridden from Builder */ 800 public Builder maxDepth(int value) { 801 super.maxDepth(value); 802 return this; 803 } 804 805 @Override /* Overridden from Builder */ 806 public Builder maxIndent(int value) { 807 super.maxIndent(value); 808 return this; 809 } 810 811 @Override /* Overridden from Builder */ 812 public Builder mediaType(MediaType value) { 813 super.mediaType(value); 814 return this; 815 } 816 817 /** 818 * Default namespaces. 819 * 820 * <p> 821 * The default list of namespaces associated with this serializer. 822 * 823 * @param values The new value for this property. 824 * <br>Cannot contain <jk>null</jk> values. 825 * @return This object. 826 */ 827 public Builder namespaces(Namespace...values) { 828 assertArgNoNulls("values", values); 829 namespaces = addAll(namespaces, values); 830 return this; 831 } 832 833 @Override /* Overridden from Builder */ 834 public Builder notBeanClasses(java.lang.Class<?>...values) { 835 super.notBeanClasses(values); 836 return this; 837 } 838 839 @Override /* Overridden from Builder */ 840 public Builder notBeanPackages(String...values) { 841 super.notBeanPackages(values); 842 return this; 843 } 844 845 /** 846 * Enable support for XML namespaces. 847 * 848 * <p> 849 * Shortcut for calling <code>enableNamespaces(<jk>true</jk>)</code>. 850 * 851 * @return This object. 852 */ 853 public Builder ns() { 854 return enableNamespaces(); 855 } 856 857 @Override /* Overridden from Builder */ 858 public Builder produces(String value) { 859 super.produces(value); 860 return this; 861 } 862 863 @Override /* Overridden from Builder */ 864 public Builder propertyNamer(Class<?> on, Class<? extends org.apache.juneau.PropertyNamer> value) { 865 super.propertyNamer(on, value); 866 return this; 867 } 868 869 @Override /* Overridden from Builder */ 870 public Builder propertyNamer(Class<? extends org.apache.juneau.PropertyNamer> value) { 871 super.propertyNamer(value); 872 return this; 873 } 874 875 @Override /* Overridden from Builder */ 876 public Builder quoteChar(char value) { 877 super.quoteChar(value); 878 return this; 879 } 880 881 @Override /* Overridden from Builder */ 882 public Builder quoteCharOverride(char value) { 883 super.quoteCharOverride(value); 884 return this; 885 } 886 887 @Override /* Overridden from Builder */ 888 public Builder sortCollections() { 889 super.sortCollections(); 890 return this; 891 } 892 893 @Override /* Overridden from Builder */ 894 public Builder sortCollections(boolean value) { 895 super.sortCollections(value); 896 return this; 897 } 898 899 @Override /* Overridden from Builder */ 900 public Builder sortMaps() { 901 super.sortMaps(); 902 return this; 903 } 904 905 @Override /* Overridden from Builder */ 906 public Builder sortMaps(boolean value) { 907 super.sortMaps(value); 908 return this; 909 } 910 911 @Override /* Overridden from Builder */ 912 public Builder sortProperties() { 913 super.sortProperties(); 914 return this; 915 } 916 917 @Override /* Overridden from Builder */ 918 public Builder sortProperties(java.lang.Class<?>...on) { 919 super.sortProperties(on); 920 return this; 921 } 922 923 @Override /* Overridden from Builder */ 924 public Builder sq() { 925 super.sq(); 926 return this; 927 } 928 929 @Override /* Overridden from Builder */ 930 public Builder stopClass(Class<?> on, Class<?> value) { 931 super.stopClass(on, value); 932 return this; 933 } 934 935 @Override /* Overridden from Builder */ 936 public Builder streamCharset(Charset value) { 937 super.streamCharset(value); 938 return this; 939 } 940 941 @Override /* Overridden from Builder */ 942 public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction) { 943 super.swap(normalClass, swappedClass, swapFunction); 944 return this; 945 } 946 947 @Override /* Overridden from Builder */ 948 public <T,S> Builder swap(Class<T> normalClass, Class<S> swappedClass, ThrowingFunction<T,S> swapFunction, ThrowingFunction<S,T> unswapFunction) { 949 super.swap(normalClass, swappedClass, swapFunction, unswapFunction); 950 return this; 951 } 952 953 @Override /* Overridden from Builder */ 954 public Builder swaps(Class<?>...values) { 955 super.swaps(values); 956 return this; 957 } 958 959 @Override /* Overridden from Builder */ 960 public Builder swaps(Object...values) { 961 super.swaps(values); 962 return this; 963 } 964 965 /** 966 * Text node delimiter. 967 * 968 * <p> 969 * Specifies the delimiter string to insert between consecutive text nodes. 970 * This is useful for adding spacing between text elements to improve readability. 971 * 972 * <p> 973 * The default value is an empty string (no delimiter). 974 * 975 * <h5 class='section'>Example:</h5> 976 * <p class='bjava'> 977 * XmlSerializer.<jsm>create</jsm>() 978 * .textNodeDelimiter(<js>" "</js>) 979 * .build(); 980 * </p> 981 * 982 * <p> 983 * With this setting, serializing: 984 * <p class='bjava'> 985 * <jk>new</jk> Audio().children(<js>"a"</js>, <js>"b"</js>, <jk>new</jk> Strong(<js>"c"</js>)); 986 * </p> 987 * 988 * <p> 989 * Will produce: <code><audio>a b<strong>c</strong></audio></code> 990 * <br>Instead of: <code><audio>ab<strong>c</strong></audio></code> 991 * 992 * @param value 993 * The delimiter string. 994 * <br>Can be <jk>null</jk> (interpreted as empty string). 995 * @return This object. 996 */ 997 public Builder textNodeDelimiter(String value) { 998 textNodeDelimiter = value == null ? "" : value; 999 return this; 1000 } 1001 1002 @Override /* Overridden from Builder */ 1003 public Builder timeZone(TimeZone value) { 1004 super.timeZone(value); 1005 return this; 1006 } 1007 1008 @Override /* Overridden from Builder */ 1009 public Builder trimEmptyCollections() { 1010 super.trimEmptyCollections(); 1011 return this; 1012 } 1013 1014 @Override /* Overridden from Builder */ 1015 public Builder trimEmptyCollections(boolean value) { 1016 super.trimEmptyCollections(value); 1017 return this; 1018 } 1019 1020 @Override /* Overridden from Builder */ 1021 public Builder trimEmptyMaps() { 1022 super.trimEmptyMaps(); 1023 return this; 1024 } 1025 1026 @Override /* Overridden from Builder */ 1027 public Builder trimEmptyMaps(boolean value) { 1028 super.trimEmptyMaps(value); 1029 return this; 1030 } 1031 1032 @Override /* Overridden from Builder */ 1033 public Builder trimStrings() { 1034 super.trimStrings(); 1035 return this; 1036 } 1037 1038 @Override /* Overridden from Builder */ 1039 public Builder trimStrings(boolean value) { 1040 super.trimStrings(value); 1041 return this; 1042 } 1043 1044 @Override /* Overridden from Builder */ 1045 public Builder type(Class<? extends org.apache.juneau.Context> value) { 1046 super.type(value); 1047 return this; 1048 } 1049 1050 @Override /* Overridden from Builder */ 1051 public Builder typeName(Class<?> on, String value) { 1052 super.typeName(on, value); 1053 return this; 1054 } 1055 1056 @Override /* Overridden from Builder */ 1057 public Builder typePropertyName(Class<?> on, String value) { 1058 super.typePropertyName(on, value); 1059 return this; 1060 } 1061 1062 @Override /* Overridden from Builder */ 1063 public Builder typePropertyName(String value) { 1064 super.typePropertyName(value); 1065 return this; 1066 } 1067 1068 @Override /* Overridden from Builder */ 1069 public Builder uriContext(UriContext value) { 1070 super.uriContext(value); 1071 return this; 1072 } 1073 1074 @Override /* Overridden from Builder */ 1075 public Builder uriRelativity(UriRelativity value) { 1076 super.uriRelativity(value); 1077 return this; 1078 } 1079 1080 @Override /* Overridden from Builder */ 1081 public Builder uriResolution(UriResolution value) { 1082 super.uriResolution(value); 1083 return this; 1084 } 1085 1086 @Override /* Overridden from Builder */ 1087 public Builder useEnumNames() { 1088 super.useEnumNames(); 1089 return this; 1090 } 1091 1092 @Override /* Overridden from Builder */ 1093 public Builder useJavaBeanIntrospector() { 1094 super.useJavaBeanIntrospector(); 1095 return this; 1096 } 1097 1098 @Override /* Overridden from Builder */ 1099 public Builder useWhitespace() { 1100 super.useWhitespace(); 1101 return this; 1102 } 1103 1104 @Override /* Overridden from Builder */ 1105 public Builder useWhitespace(boolean value) { 1106 super.useWhitespace(value); 1107 return this; 1108 } 1109 1110 @Override /* Overridden from Builder */ 1111 public Builder ws() { 1112 super.ws(); 1113 return this; 1114 } 1115 } 1116 1117 /** Default serializer without namespaces. */ 1118 public static class Ns extends XmlSerializer { 1119 1120 /** 1121 * Constructor. 1122 * 1123 * @param builder The builder for this object. 1124 * <br>Cannot be <jk>null</jk>. 1125 */ 1126 public Ns(Builder builder) { 1127 super(assertArgNotNull("builder", builder).enableNamespaces()); 1128 } 1129 } 1130 1131 /** Default serializer without namespaces, single quotes. */ 1132 public static class NsSq extends XmlSerializer { 1133 1134 /** 1135 * Constructor. 1136 * 1137 * @param builder The builder for this object. 1138 * <br>Cannot be <jk>null</jk>. 1139 */ 1140 public NsSq(Builder builder) { 1141 super(assertArgNotNull("builder", builder).enableNamespaces().quoteChar('\'')); 1142 } 1143 } 1144 1145 /** Default serializer without namespaces, single quotes, with whitespace. */ 1146 public static class NsSqReadable extends XmlSerializer { 1147 1148 /** 1149 * Constructor. 1150 * 1151 * @param builder The builder for this object. 1152 * <br>Cannot be <jk>null</jk>. 1153 */ 1154 public NsSqReadable(Builder builder) { 1155 super(assertArgNotNull("builder", builder).enableNamespaces().quoteChar('\'').useWhitespace()); 1156 } 1157 } 1158 1159 /** Default serializer, single quotes. */ 1160 public static class Sq extends XmlSerializer { 1161 1162 /** 1163 * Constructor. 1164 * 1165 * @param builder The builder for this object. 1166 * <br>Cannot be <jk>null</jk>. 1167 */ 1168 public Sq(Builder builder) { 1169 super(assertArgNotNull("builder", builder).quoteChar('\'')); 1170 } 1171 } 1172 1173 /** Default serializer, single quotes, whitespace added. */ 1174 public static class SqReadable extends XmlSerializer { 1175 1176 /** 1177 * Constructor. 1178 * 1179 * @param builder The builder for this object. 1180 * <br>Cannot be <jk>null</jk>. 1181 */ 1182 public SqReadable(Builder builder) { 1183 super(assertArgNotNull("builder", builder).quoteChar('\'').useWhitespace()); 1184 } 1185 } 1186 1187 1188 /** Default serializer without namespaces. */ 1189 public static final XmlSerializer DEFAULT = new XmlSerializer(create()); 1190 /** Default serializer without namespaces, with single quotes. */ 1191 public static final XmlSerializer DEFAULT_SQ = new Sq(create()); 1192 1193 /** Default serializer without namespaces, with single quotes, whitespace added. */ 1194 public static final XmlSerializer DEFAULT_SQ_READABLE = new SqReadable(create()); 1195 1196 /** Default serializer, all default settings. */ 1197 public static final XmlSerializer DEFAULT_NS = new Ns(create()); 1198 1199 /** Default serializer, single quotes. */ 1200 public static final XmlSerializer DEFAULT_NS_SQ = new NsSq(create()); 1201 1202 /** Default serializer, single quotes, whitespace added. */ 1203 public static final XmlSerializer DEFAULT_NS_SQ_READABLE = new NsSqReadable(create()); 1204 1205 protected static final Namespace DEFAULT_JUNEAU_NAMESPACE = Namespace.of("juneau", "http://www.apache.org/2013/Juneau"), 1206 DEFAULT_XS_NAMESPACE = Namespace.of("xs", "http://www.w3.org/2001/XMLSchema"); 1207 1208 /** 1209 * Creates a new builder for this object. 1210 * 1211 * @return A new builder. 1212 */ 1213 public static Builder create() { 1214 return new Builder(); 1215 } 1216 1217 protected final boolean addBeanTypesXml; 1218 protected final boolean addJsonTags; 1219 protected final boolean addNamespaceUrlsToRoot; 1220 protected final boolean autoDetectNamespaces; 1221 protected final boolean enableNamespaces; 1222 private final Namespace defaultNamespace; 1223 private final List<Namespace> namespaces; 1224 private final String textNodeDelimiter; 1225 1226 private final boolean addBeanTypes; 1227 private final Map<ClassMeta<?>,XmlClassMeta> xmlClassMetas = new ConcurrentHashMap<>(); 1228 private final Map<BeanMeta<?>,XmlBeanMeta> xmlBeanMetas = new ConcurrentHashMap<>(); 1229 private final Map<BeanPropertyMeta,XmlBeanPropertyMeta> xmlBeanPropertyMetas = new ConcurrentHashMap<>(); 1230 1231 /** 1232 * Constructor. 1233 * 1234 * @param builder 1235 * The builder for this object. 1236 */ 1237 public XmlSerializer(Builder builder) { 1238 super(builder); 1239 addBeanTypesXml = builder.addBeanTypesXml; 1240 addJsonTags = ! builder.disableJsonTags; 1241 addNamespaceUrlsToRoot = builder.addNamespaceUrisToRoot; 1242 autoDetectNamespaces = ! builder.disableAutoDetectNamespaces; 1243 defaultNamespace = nn(builder.defaultNamespace) ? builder.defaultNamespace : DEFAULT_JUNEAU_NAMESPACE; 1244 enableNamespaces = builder.enableNamespaces; 1245 namespaces = u(nn(builder.namespaces) ? new ArrayList<>(builder.namespaces) : new ArrayList<>()); 1246 textNodeDelimiter = builder.textNodeDelimiter; 1247 addBeanTypes = addBeanTypesXml || super.isAddBeanTypes(); 1248 } 1249 1250 @Override /* Overridden from Context */ 1251 public Builder copy() { 1252 return new Builder(this); 1253 } 1254 1255 @Override /* Overridden from Context */ 1256 public XmlSerializerSession.Builder createSession() { 1257 return XmlSerializerSession.create(this); 1258 } 1259 1260 @Override /* Overridden from Context */ 1261 public XmlSerializerSession getSession() { return createSession().build(); } 1262 1263 @Override /* Overridden from XmlMetaProvider */ 1264 public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) { 1265 XmlBeanMeta m = xmlBeanMetas.get(bm); 1266 if (m == null) { 1267 m = new XmlBeanMeta(bm, this); 1268 xmlBeanMetas.put(bm, m); 1269 } 1270 return m; 1271 } 1272 1273 @Override /* Overridden from XmlMetaProvider */ 1274 public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) { 1275 XmlBeanPropertyMeta m = xmlBeanPropertyMetas.get(bpm); 1276 if (m == null) { 1277 m = new XmlBeanPropertyMeta(bpm.getDelegateFor(), this); 1278 xmlBeanPropertyMetas.put(bpm, m); 1279 } 1280 return m; 1281 } 1282 1283 @Override /* Overridden from XmlMetaProvider */ 1284 public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) { 1285 XmlClassMeta m = xmlClassMetas.get(cm); 1286 if (m == null) { 1287 m = new XmlClassMeta(cm, this); 1288 xmlClassMetas.put(cm, m); 1289 } 1290 return m; 1291 } 1292 1293 /** 1294 * Default namespace. 1295 * 1296 * @see Builder#defaultNamespace(Namespace) 1297 * @return 1298 * The default namespace URI for this document. 1299 */ 1300 protected final Namespace getDefaultNamespace() { return defaultNamespace; } 1301 1302 /** 1303 * Default namespaces. 1304 * 1305 * @see Builder#namespaces(Namespace...) 1306 * @return 1307 * The default list of namespaces associated with this serializer. 1308 * <br>Never <jk>null</jk>. 1309 * <br>List is unmodifiable. 1310 */ 1311 protected final List<Namespace> getNamespaces() { return namespaces; } 1312 1313 /** 1314 * Text node delimiter. 1315 * 1316 * @see Builder#textNodeDelimiter(String) 1317 * @return 1318 * The delimiter string to insert between consecutive text nodes. 1319 * <br>Never <jk>null</jk>. 1320 */ 1321 protected final String getTextNodeDelimiter() { return textNodeDelimiter; } 1322 1323 /** 1324 * Add <js>"_type"</js> properties when needed. 1325 * 1326 * @see Builder#addBeanTypesXml() 1327 * @return 1328 * <jk>true</jk> if<js>"_type"</js> properties will be added to beans if their type cannot be inferred 1329 * through reflection. 1330 */ 1331 @Override 1332 protected boolean isAddBeanTypes() { return addBeanTypes; } 1333 1334 /** 1335 * Add namespace URLs to the root element. 1336 * 1337 * @see Builder#addNamespaceUrisToRoot() 1338 * @return 1339 * <jk>true</jk> if {@code xmlns:x} attributes are added to the root element for the default and all mapped namespaces. 1340 */ 1341 protected final boolean isAddNamespaceUrlsToRoot() { return addNamespaceUrlsToRoot; } 1342 1343 /** 1344 * Auto-detect namespace usage. 1345 * 1346 * @see Builder#disableAutoDetectNamespaces() 1347 * @return 1348 * <jk>true</jk> if namespace usage is detected before serialization. 1349 */ 1350 protected final boolean isAutoDetectNamespaces() { return autoDetectNamespaces; } 1351 1352 /** 1353 * Enable support for XML namespaces. 1354 * 1355 * @see Builder#enableNamespaces() 1356 * @return 1357 * <jk>false</jk> if XML output will not contain any namespaces regardless of any other settings. 1358 */ 1359 protected final boolean isEnableNamespaces() { return enableNamespaces; } 1360 1361 @Override /* Overridden from WriterSerializer */ 1362 protected FluentMap<String,Object> properties() { 1363 return super.properties() 1364 .a("addBeanTypes", addBeanTypes) 1365 .a("addNamespaceUrlsToRoot", addNamespaceUrlsToRoot) 1366 .a("autoDetectNamespaces", autoDetectNamespaces) 1367 .a("defaultNamespace", defaultNamespace) 1368 .a("enableNamespaces", enableNamespaces) 1369 .a("namespaces", namespaces); 1370 } 1371}