001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.xml; 014 015import static org.apache.juneau.internal.ArrayUtils.*; 016import static org.apache.juneau.xml.XmlSerializer.*; 017import static org.apache.juneau.xml.XmlSerializerSession.ContentResult.*; 018import static org.apache.juneau.xml.XmlSerializerSession.JsonType.*; 019import static org.apache.juneau.xml.annotation.XmlFormat.*; 020 021import java.lang.reflect.*; 022import java.util.*; 023 024import org.apache.juneau.*; 025import org.apache.juneau.internal.*; 026import org.apache.juneau.serializer.*; 027import org.apache.juneau.transform.*; 028import org.apache.juneau.xml.annotation.*; 029import org.apache.juneau.xmlschema.*; 030 031/** 032 * Session object that lives for the duration of a single use of {@link XmlSerializer}. 033 * 034 * <p> 035 * This class is NOT thread safe. 036 * It is typically discarded after one-time use although it can be reused within the same thread. 037 */ 038@SuppressWarnings({"unchecked","rawtypes"}) 039public class XmlSerializerSession extends WriterSerializerSession { 040 041 private final XmlSerializer ctx; 042 private Namespace 043 defaultNamespace; 044 private Namespace[] namespaces = new Namespace[0]; 045 046 /** 047 * Create a new session using properties specified in the context. 048 * 049 * @param ctx 050 * The context creating this session object. 051 * The context contains all the configuration settings for this object. 052 * @param args 053 * Runtime arguments. 054 * These specify session-level information such as locale and URI context. 055 * It also include session-level properties that override the properties defined on the bean and 056 * serializer contexts. 057 */ 058 protected XmlSerializerSession(XmlSerializer ctx, SerializerSessionArgs args) { 059 super(ctx, args); 060 this.ctx = ctx; 061 namespaces = getInstanceArrayProperty(XML_namespaces, Namespace.class, ctx.getNamespaces()); 062 defaultNamespace = findDefaultNamespace(getInstanceProperty(XML_defaultNamespace, Namespace.class, ctx.getDefaultNamespace())); 063 } 064 065 private Namespace findDefaultNamespace(Namespace n) { 066 if (n == null) 067 return null; 068 if (n.name != null && n.uri != null) 069 return n; 070 if (n.uri == null) { 071 for (Namespace n2 : getNamespaces()) 072 if (n2.name.equals(n.name)) 073 return n2; 074 } 075 if (n.name == null) { 076 for (Namespace n2 : getNamespaces()) 077 if (n2.uri.equals(n.uri)) 078 return n2; 079 } 080 return n; 081 } 082 083 @Override /* Session */ 084 public ObjectMap asMap() { 085 return super.asMap() 086 .append("XmlSerializerSession", new ObjectMap() 087 ); 088 } 089 090 /* 091 * Add a namespace to this session. 092 * 093 * @param ns The namespace being added. 094 */ 095 private void addNamespace(Namespace ns) { 096 if (ns == defaultNamespace) 097 return; 098 099 for (Namespace n : namespaces) 100 if (n == ns) 101 return; 102 103 if (defaultNamespace != null && (ns.uri.equals(defaultNamespace.uri) || ns.name.equals(defaultNamespace.name))) 104 defaultNamespace = ns; 105 else 106 namespaces = append(namespaces, ns); 107 } 108 109 /** 110 * Returns <jk>true</jk> if we're serializing HTML. 111 * 112 * <p> 113 * The difference in behavior is how empty non-void elements are handled. 114 * The XML serializer will produce a collapsed tag, whereas the HTML serializer will produce a start and end tag. 115 * 116 * @return <jk>true</jk> if we're generating HTML. 117 */ 118 protected boolean isHtmlMode() { 119 return false; 120 } 121 122 /** 123 * Converts the specified output target object to an {@link XmlWriter}. 124 * 125 * @param out The output target object. 126 * @return The output target object wrapped in an {@link XmlWriter}. 127 * @throws Exception 128 */ 129 public final XmlWriter getXmlWriter(SerializerPipe out) throws Exception { 130 Object output = out.getRawOutput(); 131 if (output instanceof XmlWriter) 132 return (XmlWriter)output; 133 XmlWriter w = new XmlWriter(out.getWriter(), isUseWhitespace(), getMaxIndent(), isTrimStrings(), getQuoteChar(), getUriResolver(), isEnableNamespaces(), defaultNamespace); 134 out.setWriter(w); 135 return w; 136 } 137 138 @Override /* Serializer */ 139 protected void doSerialize(SerializerPipe out, Object o) throws Exception { 140 if (isEnableNamespaces() && isAutoDetectNamespaces()) 141 findNsfMappings(o); 142 serializeAnything(getXmlWriter(out), o, getExpectedRootType(o), null, null, isEnableNamespaces() && isAddNamespaceUrlsToRoot(), XmlFormat.DEFAULT, false, false, null); 143 } 144 145 /** 146 * Recursively searches for the XML namespaces on the specified POJO and adds them to the serializer context object. 147 * 148 * @param o The POJO to check. 149 * @throws SerializeException 150 */ 151 protected final void findNsfMappings(Object o) throws SerializeException { 152 ClassMeta<?> aType = null; // The actual type 153 154 try { 155 aType = push(null, o, null); 156 } catch (BeanRecursionException e) { 157 throw new SerializeException(e); 158 } 159 160 if (aType != null) { 161 Namespace ns = cXml(aType).getNamespace(); 162 if (ns != null) { 163 if (ns.uri != null) 164 addNamespace(ns); 165 else 166 ns = null; 167 } 168 } 169 170 // Handle recursion 171 if (aType != null && ! aType.isPrimitive()) { 172 173 BeanMap<?> bm = null; 174 if (aType.isBeanMap()) { 175 bm = (BeanMap<?>)o; 176 } else if (aType.isBean()) { 177 bm = toBeanMap(o); 178 } else if (aType.isDelegate()) { 179 ClassMeta<?> innerType = ((Delegate<?>)o).getClassMeta(); 180 Namespace ns = cXml(innerType).getNamespace(); 181 if (ns != null) { 182 if (ns.uri != null) 183 addNamespace(ns); 184 else 185 ns = null; 186 } 187 188 if (innerType.isBean()) { 189 for (BeanPropertyMeta bpm : innerType.getBeanMeta().getPropertyMetas()) { 190 if (bpm.canRead()) { 191 ns = bpXml(bpm).getNamespace(); 192 if (ns != null && ns.uri != null) 193 addNamespace(ns); 194 } 195 } 196 197 } else if (innerType.isMap()) { 198 for (Object o2 : ((Map<?,?>)o).values()) 199 findNsfMappings(o2); 200 } else if (innerType.isCollection()) { 201 for (Object o2 : ((Collection<?>)o)) 202 findNsfMappings(o2); 203 } 204 205 } else if (aType.isMap()) { 206 for (Object o2 : ((Map<?,?>)o).values()) 207 findNsfMappings(o2); 208 } else if (aType.isCollection()) { 209 for (Object o2 : ((Collection<?>)o)) 210 findNsfMappings(o2); 211 } else if (aType.isArray() && ! aType.getElementType().isPrimitive()) { 212 for (Object o2 : ((Object[])o)) 213 findNsfMappings(o2); 214 } 215 if (bm != null) { 216 for (BeanPropertyValue p : bm.getValues(isTrimNullProperties())) { 217 218 Namespace ns = bpXml(p.getMeta()).getNamespace(); 219 if (ns != null && ns.uri != null) 220 addNamespace(ns); 221 222 try { 223 findNsfMappings(p.getValue()); 224 } catch (Throwable x) { 225 // Ignore 226 } 227 } 228 } 229 } 230 231 pop(); 232 } 233 234 /** 235 * Workhorse method. 236 * 237 * @param out The writer to send the output to. 238 * @param o The object to serialize. 239 * @param eType The expected type if this is a bean property value being serialized. 240 * @param elementName The root element name. 241 * @param elementNamespace The namespace of the element. 242 * @param addNamespaceUris Flag indicating that namespace URIs need to be added. 243 * @param format The format to serialize the output to. 244 * @param isMixed We're serializing mixed content, so don't use whitespace. 245 * @param preserveWhitespace 246 * <jk>true</jk> if we're serializing {@link XmlFormat#MIXED_PWS} or {@link XmlFormat#TEXT_PWS}. 247 * @param pMeta The bean property metadata if this is a bean property being serialized. 248 * @return The same writer passed in so that calls to the writer can be chained. 249 * @throws Exception If a problem occurred trying to convert the output. 250 */ 251 protected ContentResult serializeAnything( 252 XmlWriter out, 253 Object o, 254 ClassMeta<?> eType, 255 String elementName, 256 Namespace elementNamespace, 257 boolean addNamespaceUris, 258 XmlFormat format, 259 boolean isMixed, 260 boolean preserveWhitespace, 261 BeanPropertyMeta pMeta) throws Exception { 262 263 JsonType type = null; // The type string (e.g. <type> or <x x='type'> 264 int i = isMixed ? 0 : indent; // Current indentation 265 ClassMeta<?> aType = null; // The actual type 266 ClassMeta<?> wType = null; // The wrapped type (delegate) 267 ClassMeta<?> sType = object(); // The serialized type 268 269 aType = push(elementName, o, eType); 270 271 if (eType == null) 272 eType = object(); 273 274 // Handle recursion 275 if (aType == null) { 276 o = null; 277 aType = object(); 278 } 279 280 if (o != null) { 281 282 if (aType.isDelegate()) { 283 wType = aType; 284 eType = aType = ((Delegate<?>)o).getClassMeta(); 285 } 286 287 sType = aType; 288 289 // Swap if necessary 290 PojoSwap swap = aType.getPojoSwap(this); 291 if (swap != null) { 292 o = swap.swap(this, o); 293 sType = swap.getSwapClassMeta(this); 294 295 // If the getSwapClass() method returns Object, we need to figure out 296 // the actual type now. 297 if (sType.isObject()) 298 sType = getClassMetaForObject(o); 299 } 300 } else { 301 sType = eType.getSerializedClassMeta(this); 302 } 303 304 // Does the actual type match the expected type? 305 boolean isExpectedType = true; 306 if (o == null || ! eType.same(aType)) { 307 if (eType.isNumber()) 308 isExpectedType = aType.isNumber(); 309 else if (eType.isMap()) 310 isExpectedType = aType.isMap(); 311 else if (eType.isCollectionOrArray()) 312 isExpectedType = aType.isCollectionOrArray(); 313 else 314 isExpectedType = false; 315 } 316 317 String resolvedDictionaryName = isExpectedType ? null : aType.getDictionaryName(); 318 319 // Note that the dictionary name may be specified on the actual type or the serialized type. 320 // HTML templates will have them defined on the serialized type. 321 String dictionaryName = aType.getDictionaryName(); 322 if (dictionaryName == null) 323 dictionaryName = sType.getDictionaryName(); 324 325 // char '\0' is interpreted as null. 326 if (o != null && sType.isChar() && ((Character)o).charValue() == 0) 327 o = null; 328 329 boolean isCollapsed = false; // If 'true', this is a collection and we're not rendering the outer element. 330 boolean isRaw = (sType.isReader() || sType.isInputStream()) && o != null; 331 332 // Get the JSON type string. 333 if (o == null) { 334 type = NULL; 335 } else if (sType.isCharSequence() || sType.isChar()) { 336 type = STRING; 337 } else if (sType.isNumber()) { 338 type = NUMBER; 339 } else if (sType.isBoolean()) { 340 type = BOOLEAN; 341 } else if (sType.isMapOrBean()) { 342 isCollapsed = cXml(sType).getFormat() == COLLAPSED; 343 type = OBJECT; 344 } else if (sType.isCollectionOrArray()) { 345 isCollapsed = (format == COLLAPSED && ! addNamespaceUris); 346 type = ARRAY; 347 } else { 348 type = STRING; 349 } 350 351 if (format.isOneOf(MIXED,MIXED_PWS,TEXT,TEXT_PWS,XMLTEXT) && type.isOneOf(NULL,STRING,NUMBER,BOOLEAN)) 352 isCollapsed = true; 353 354 // Is there a name associated with this bean? 355 if (elementName == null && dictionaryName != null) { 356 elementName = dictionaryName; 357 isExpectedType = true; 358 } 359 360 if (isEnableNamespaces()) { 361 if (elementNamespace == null) 362 elementNamespace = cXml(sType).getNamespace(); 363 if (elementNamespace == null) 364 elementNamespace = cXml(aType).getNamespace(); 365 if (elementNamespace != null && elementNamespace.uri == null) 366 elementNamespace = null; 367 if (elementNamespace == null) 368 elementNamespace = defaultNamespace; 369 } else { 370 elementNamespace = null; 371 } 372 373 // Do we need a carriage return after the start tag? 374 boolean cr = o != null && (sType.isMapOrBean() || sType.isCollectionOrArray()) && ! isMixed; 375 376 String en = elementName; 377 if (en == null && ! isRaw) { 378 en = type.toString(); 379 type = null; 380 } 381 boolean encodeEn = elementName != null; 382 String ns = (elementNamespace == null ? null : elementNamespace.name); 383 String dns = null, elementNs = null; 384 if (isEnableNamespaces()) { 385 dns = elementName == null && defaultNamespace != null ? defaultNamespace.name : null; 386 elementNs = elementName == null ? dns : ns; 387 if (elementName == null) 388 elementNamespace = null; 389 } 390 391 // Render the start tag. 392 if (! isCollapsed) { 393 if (en != null) { 394 out.oTag(i, elementNs, en, encodeEn); 395 if (addNamespaceUris) { 396 out.attr((String)null, "xmlns", defaultNamespace.getUri()); 397 398 for (Namespace n : namespaces) 399 out.attr("xmlns", n.getName(), n.getUri()); 400 } 401 if (! isExpectedType) { 402 if (resolvedDictionaryName != null) 403 out.attr(dns, getBeanTypePropertyName(eType), resolvedDictionaryName); 404 else if (type != null && type != STRING) 405 out.attr(dns, getBeanTypePropertyName(eType), type); 406 } 407 } else { 408 out.i(i); 409 } 410 if (o == null) { 411 if ((sType.isBoolean() || sType.isNumber()) && ! sType.isNullable()) 412 o = sType.getPrimitiveDefault(); 413 } 414 415 if (o != null && ! (sType.isMapOrBean() || en == null)) 416 out.append('>'); 417 418 if (cr && ! (sType.isMapOrBean())) 419 out.nl(i+1); 420 } 421 422 ContentResult rc = CR_ELEMENTS; 423 424 // Render the tag contents. 425 if (o != null) { 426 if (sType.isUri() || (pMeta != null && pMeta.isUri())) { 427 out.textUri(o); 428 } else if (sType.isCharSequence() || sType.isChar()) { 429 if (isXmlText(format, sType)) 430 out.append(o); 431 else 432 out.text(o, preserveWhitespace); 433 } else if (sType.isNumber() || sType.isBoolean()) { 434 out.append(o); 435 } else if (sType.isMap() || (wType != null && wType.isMap())) { 436 if (o instanceof BeanMap) 437 rc = serializeBeanMap(out, (BeanMap)o, elementNamespace, isCollapsed, isMixed); 438 else 439 rc = serializeMap(out, (Map)o, sType, eType.getKeyType(), eType.getValueType(), isMixed); 440 } else if (sType.isBean()) { 441 rc = serializeBeanMap(out, toBeanMap(o), elementNamespace, isCollapsed, isMixed); 442 } else if (sType.isCollection() || (wType != null && wType.isCollection())) { 443 if (isCollapsed) 444 this.indent--; 445 serializeCollection(out, o, sType, eType, pMeta, isMixed); 446 if (isCollapsed) 447 this.indent++; 448 } else if (sType.isArray()) { 449 if (isCollapsed) 450 this.indent--; 451 serializeCollection(out, o, sType, eType, pMeta, isMixed); 452 if (isCollapsed) 453 this.indent++; 454 } else if (sType.isReader() || sType.isInputStream()) { 455 IOUtils.pipe(o, out); 456 } else { 457 if (isXmlText(format, sType)) 458 out.append(toString(o)); 459 else 460 out.text(toString(o)); 461 } 462 } 463 464 pop(); 465 466 // Render the end tag. 467 if (! isCollapsed) { 468 if (en != null) { 469 if (rc == CR_EMPTY) { 470 if (isHtmlMode()) 471 out.append('>').eTag(elementNs, en, encodeEn); 472 else 473 out.append('/').append('>'); 474 } else if (rc == CR_VOID || o == null) { 475 out.append('/').append('>'); 476 } 477 else 478 out.ie(cr && rc != CR_MIXED ? i : 0).eTag(elementNs, en, encodeEn); 479 } 480 if (! isMixed) 481 out.nl(i); 482 } 483 484 return rc; 485 } 486 487 private boolean isXmlText(XmlFormat format, ClassMeta<?> sType) { 488 if (format == XMLTEXT) 489 return true; 490 XmlClassMeta xcm = sType.getExtendedMeta(XmlClassMeta.class); 491 if (xcm == null) 492 return false; 493 return xcm.getFormat() == XMLTEXT; 494 } 495 496 private ContentResult serializeMap(XmlWriter out, Map m, ClassMeta<?> sType, 497 ClassMeta<?> eKeyType, ClassMeta<?> eValueType, boolean isMixed) throws Exception { 498 499 m = sort(m); 500 501 ClassMeta<?> keyType = eKeyType == null ? sType.getKeyType() : eKeyType; 502 ClassMeta<?> valueType = eValueType == null ? sType.getValueType() : eValueType; 503 504 boolean hasChildren = false; 505 for (Iterator i = m.entrySet().iterator(); i.hasNext();) { 506 Map.Entry e = (Map.Entry)i.next(); 507 508 Object k = e.getKey(); 509 if (k == null) { 510 k = "\u0000"; 511 } else { 512 k = generalize(k, keyType); 513 if (isTrimStrings() && k instanceof String) 514 k = k.toString().trim(); 515 } 516 517 Object value = e.getValue(); 518 519 if (! hasChildren) { 520 hasChildren = true; 521 out.append('>').nlIf(! isMixed, indent); 522 } 523 serializeAnything(out, value, valueType, toString(k), null, false, XmlFormat.DEFAULT, isMixed, false, null); 524 } 525 return hasChildren ? CR_ELEMENTS : CR_EMPTY; 526 } 527 528 private ContentResult serializeBeanMap(XmlWriter out, BeanMap<?> m, 529 Namespace elementNs, boolean isCollapsed, boolean isMixed) throws Exception { 530 boolean hasChildren = false; 531 BeanMeta<?> bm = m.getMeta(); 532 533 List<BeanPropertyValue> lp = m.getValues(isTrimNullProperties()); 534 535 XmlBeanMeta xbm = bXml(bm); 536 537 Set<String> 538 attrs = xbm.getAttrPropertyNames(), 539 elements = xbm.getElementPropertyNames(), 540 collapsedElements = xbm.getCollapsedPropertyNames(); 541 String 542 attrsProperty = xbm.getAttrsPropertyName(), 543 contentProperty = xbm.getContentPropertyName(); 544 545 XmlFormat cf = null; 546 547 Object content = null; 548 ClassMeta<?> contentType = null; 549 for (BeanPropertyValue p : lp) { 550 String n = p.getName(); 551 if (attrs.contains(n) || attrs.contains("*") || n.equals(attrsProperty)) { 552 BeanPropertyMeta pMeta = p.getMeta(); 553 if (pMeta.canRead()) { 554 ClassMeta<?> cMeta = p.getClassMeta(); 555 556 String key = p.getName(); 557 Object value = p.getValue(); 558 Throwable t = p.getThrown(); 559 if (t != null) 560 onBeanGetterException(pMeta, t); 561 562 if (canIgnoreValue(cMeta, key, value)) 563 continue; 564 565 XmlBeanPropertyMeta bpXml = bpXml(pMeta); 566 Namespace ns = (isEnableNamespaces() && bpXml.getNamespace() != elementNs ? bpXml.getNamespace() : null); 567 568 if (pMeta.isUri() ) { 569 out.attrUri(ns, key, value); 570 } else if (n.equals(attrsProperty)) { 571 if (value instanceof BeanMap) { 572 BeanMap<?> bm2 = (BeanMap)value; 573 for (BeanPropertyValue p2 : bm2.getValues(true)) { 574 String key2 = p2.getName(); 575 Object value2 = p2.getValue(); 576 Throwable t2 = p2.getThrown(); 577 if (t2 != null) 578 onBeanGetterException(pMeta, t); 579 out.attr(ns, key2, value2); 580 } 581 } else /* Map */ { 582 Map m2 = (Map)value; 583 for (Map.Entry e : (Set<Map.Entry>)(m2.entrySet())) { 584 out.attr(ns, toString(e.getKey()), e.getValue()); 585 } 586 } 587 } else { 588 out.attr(ns, key, value); 589 } 590 } 591 } 592 } 593 594 boolean 595 hasContent = false, 596 preserveWhitespace = false, 597 isVoidElement = xbm.getContentFormat() == VOID; 598 599 for (BeanPropertyValue p : lp) { 600 BeanPropertyMeta pMeta = p.getMeta(); 601 if (pMeta.canRead()) { 602 ClassMeta<?> cMeta = p.getClassMeta(); 603 604 String n = p.getName(); 605 if (n.equals(contentProperty)) { 606 content = p.getValue(); 607 contentType = p.getClassMeta(); 608 hasContent = true; 609 cf = xbm.getContentFormat(); 610 if (cf.isOneOf(MIXED,MIXED_PWS,TEXT,TEXT_PWS,XMLTEXT)) 611 isMixed = true; 612 if (cf.isOneOf(MIXED_PWS, TEXT_PWS)) 613 preserveWhitespace = true; 614 if (contentType.isCollection() && ((Collection)content).isEmpty()) 615 hasContent = false; 616 else if (contentType.isArray() && Array.getLength(content) == 0) 617 hasContent = false; 618 } else if (elements.contains(n) || collapsedElements.contains(n) || elements.contains("*") || collapsedElements.contains("*") ) { 619 String key = p.getName(); 620 Object value = p.getValue(); 621 Throwable t = p.getThrown(); 622 if (t != null) 623 onBeanGetterException(pMeta, t); 624 625 if (canIgnoreValue(cMeta, key, value)) 626 continue; 627 628 if (! hasChildren) { 629 hasChildren = true; 630 out.appendIf(! isCollapsed, '>').nlIf(! isMixed, indent); 631 } 632 633 XmlBeanPropertyMeta bpXml = bpXml(pMeta); 634 serializeAnything(out, value, cMeta, key, bpXml.getNamespace(), false, bpXml.getXmlFormat(), isMixed, false, pMeta); 635 } 636 } 637 } 638 if (! hasContent) 639 return (hasChildren ? CR_ELEMENTS : isVoidElement ? CR_VOID : CR_EMPTY); 640 out.append('>').nlIf(! isMixed, indent); 641 642 // Serialize XML content. 643 if (content != null) { 644 if (contentType == null) { 645 } else if (contentType.isCollection()) { 646 Collection c = (Collection)content; 647 for (Iterator i = c.iterator(); i.hasNext();) { 648 Object value = i.next(); 649 serializeAnything(out, value, contentType.getElementType(), null, null, false, cf, isMixed, preserveWhitespace, null); 650 } 651 } else if (contentType.isArray()) { 652 Collection c = toList(Object[].class, content); 653 for (Iterator i = c.iterator(); i.hasNext();) { 654 Object value = i.next(); 655 serializeAnything(out, value, contentType.getElementType(), null, null, false, cf, isMixed, preserveWhitespace, null); 656 } 657 } else { 658 serializeAnything(out, content, contentType, null, null, false, cf, isMixed, preserveWhitespace, null); 659 } 660 } else { 661 if (! isTrimNullProperties()) { 662 if (! isMixed) 663 out.i(indent); 664 out.text(content); 665 if (! isMixed) 666 out.nl(indent); 667 } 668 } 669 return isMixed ? CR_MIXED : CR_ELEMENTS; 670 } 671 672 private XmlWriter serializeCollection(XmlWriter out, Object in, ClassMeta<?> sType, 673 ClassMeta<?> eType, BeanPropertyMeta ppMeta, boolean isMixed) throws Exception { 674 675 ClassMeta<?> eeType = eType.getElementType(); 676 677 Collection c = (sType.isCollection() ? (Collection)in : toList(sType.getInnerClass(), in)); 678 679 c = sort(c); 680 681 String type2 = null; 682 683 String eName = type2; 684 Namespace eNs = null; 685 686 if (ppMeta != null) { 687 XmlBeanPropertyMeta bpXml = bpXml(ppMeta); 688 eName = bpXml.getChildName(); 689 eNs = bpXml.getNamespace(); 690 } 691 692 for (Iterator i = c.iterator(); i.hasNext();) { 693 Object value = i.next(); 694 serializeAnything(out, value, eeType, eName, eNs, false, XmlFormat.DEFAULT, isMixed, false, null); 695 } 696 return out; 697 } 698 699 private static XmlClassMeta cXml(ClassMeta<?> cm) { 700 return cm.getExtendedMeta(XmlClassMeta.class); 701 } 702 703 private static XmlBeanPropertyMeta bpXml(BeanPropertyMeta pMeta) { 704 return pMeta == null ? XmlBeanPropertyMeta.DEFAULT : pMeta.getExtendedMeta(XmlBeanPropertyMeta.class); 705 } 706 707 private static XmlBeanMeta bXml(BeanMeta bm) { 708 return (XmlBeanMeta)bm.getExtendedMeta(XmlBeanMeta.class); 709 } 710 711 static enum JsonType { 712 STRING("string"),BOOLEAN("boolean"),NUMBER("number"),ARRAY("array"),OBJECT("object"),NULL("null"); 713 714 private final String value; 715 private JsonType(String value) { 716 this.value = value; 717 } 718 719 @Override 720 public String toString() { 721 return value; 722 } 723 724 boolean isOneOf(JsonType...types) { 725 for (JsonType type : types) 726 if (type == this) 727 return true; 728 return false; 729 } 730 } 731 732 /** 733 * Identifies what the contents were of a serialized bean. 734 */ 735 @SuppressWarnings("javadoc") 736 public static enum ContentResult { 737 CR_VOID, // No content...append "/>" to the start tag. 738 CR_EMPTY, // No content...append "/>" to the start tag if XML, "/></end>" if HTML. 739 CR_MIXED, // Mixed content...don't add whitespace. 740 CR_ELEMENTS // Elements...use normal whitespace rules. 741 } 742 743 //----------------------------------------------------------------------------------------------------------------- 744 // Properties 745 //----------------------------------------------------------------------------------------------------------------- 746 747 /** 748 * Configuration property: Auto-detect namespace usage. 749 * 750 * @see XmlSerializer#XML_autoDetectNamespaces 751 * @return 752 * <jk>true</jk> if namespace usage is detected before serialization. 753 */ 754 protected final boolean isAutoDetectNamespaces() { 755 return ctx.isAutoDetectNamespaces(); 756 } 757 758 /** 759 * Configuration property: Enable support for XML namespaces. 760 * 761 * @see XmlSerializer#XML_enableNamespaces 762 * @return 763 * <jk>false</jk> if XML output will not contain any namespaces regardless of any other settings. 764 */ 765 protected final boolean isEnableNamespaces() { 766 return ctx.isEnableNamespaces(); 767 } 768 769 /** 770 * Configuration property: Add namespace URLs to the root element. 771 * 772 * @see XmlSerializer#XML_addNamespaceUrisToRoot 773 * @return 774 * <jk>true</jk> if {@code xmlns:x} attributes are added to the root element for the default and all mapped namespaces. 775 */ 776 protected final boolean isAddNamespaceUrlsToRoot() { 777 return ctx.isAddNamespaceUrlsToRoot(); 778 } 779 780 /** 781 * Configuration property: Add <js>"_type"</js> properties when needed. 782 * 783 * @see XmlSerializer#XML_addBeanTypes 784 * @return 785 * <jk>true</jk> if<js>"_type"</js> properties will be added to beans if their type cannot be inferred 786 * through reflection. 787 */ 788 @Override 789 protected boolean isAddBeanTypes() { 790 return ctx.isAddBeanTypes(); 791 } 792 793 /** 794 * Configuration property: Default namespace. 795 * 796 * @see XmlSerializer#XML_defaultNamespace 797 * @return 798 * The default namespace URI for this document. 799 */ 800 protected final Namespace getDefaultNamespace() { 801 return defaultNamespace; 802 } 803 804 /** 805 * Configuration property: XMLSchema namespace. 806 * 807 * @see XmlSerializer#XML_xsNamespace 808 * @return 809 * The namespace for the <code>XMLSchema</code> namespace, used by the schema generated by the 810 * {@link XmlSchemaSerializer} class. 811 */ 812 protected final Namespace getXsNamespace() { 813 return ctx.getXsNamespace(); 814 } 815 816 /** 817 * Configuration property: Default namespaces. 818 * 819 * @see XmlSerializer#XML_namespaces 820 * @return 821 * The default list of namespaces associated with this serializer. 822 */ 823 protected final Namespace[] getNamespaces() { 824 return namespaces; 825 } 826}