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