in juneau-core/juneau-marshall/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java [405:662]
protected ContentResult serializeAnything(
XmlWriter out,
Object o,
ClassMeta<?> eType,
String keyName,
String elementName,
Namespace elementNamespace,
boolean addNamespaceUris,
XmlFormat format,
boolean isMixedOrText,
boolean preserveWhitespace,
BeanPropertyMeta pMeta) throws SerializeException {
JsonType type = null; // The type string (e.g. <type> or <x x='type'>
int i = isMixedOrText ? 0 : indent; // Current indentation
ClassMeta<?> aType = null; // The actual type
ClassMeta<?> wType = null; // The wrapped type (delegate)
ClassMeta<?> sType = object(); // The serialized type
aType = push2(keyName, o, eType);
if (eType == null)
eType = object();
// Handle recursion
if (aType == null) {
o = null;
aType = object();
}
// Handle Optional<X>
if (isOptional(aType)) {
o = getOptionalValue(o);
eType = getOptionalType(eType);
aType = getClassMetaForObject(o, object());
}
if (o != null) {
if (aType.isDelegate()) {
wType = aType;
eType = aType = ((Delegate<?>)o).getClassMeta();
}
sType = aType;
// Swap if necessary
ObjectSwap swap = aType.getSwap(this);
if (swap != null) {
o = swap(swap, o);
sType = swap.getSwapClassMeta(this);
// If the getSwapClass() method returns Object, we need to figure out
// the actual type now.
if (sType.isObject())
sType = getClassMetaForObject(o);
}
} else {
sType = eType.getSerializedClassMeta(this);
}
// Does the actual type match the expected type?
boolean isExpectedType = true;
if (o == null || ! eType.same(aType)) {
if (eType.isNumber())
isExpectedType = aType.isNumber();
else if (eType.isMap())
isExpectedType = aType.isMap();
else if (eType.isCollectionOrArray())
isExpectedType = aType.isCollectionOrArray();
else
isExpectedType = false;
}
String resolvedDictionaryName = isExpectedType ? null : aType.getDictionaryName();
// Note that the dictionary name may be specified on the actual type or the serialized type.
// HTML templates will have them defined on the serialized type.
String dictionaryName = aType.getDictionaryName();
if (dictionaryName == null)
dictionaryName = sType.getDictionaryName();
// char '\0' is interpreted as null.
if (o != null && sType.isChar() && ((Character)o).charValue() == 0)
o = null;
boolean isCollapsed = false; // If 'true', this is a collection and we're not rendering the outer element.
boolean isRaw = (sType.isReader() || sType.isInputStream()) && o != null;
// Get the JSON type string.
if (o == null) {
type = NULL;
} else if (sType.isCharSequence() || sType.isChar()) {
type = STRING;
} else if (sType.isNumber()) {
type = NUMBER;
} else if (sType.isBoolean()) {
type = BOOLEAN;
} else if (sType.isMapOrBean()) {
isCollapsed = getXmlClassMeta(sType).getFormat() == COLLAPSED;
type = OBJECT;
} else if (sType.isCollectionOrArray()) {
isCollapsed = (format == COLLAPSED && ! addNamespaceUris);
type = ARRAY;
} else {
type = STRING;
}
if (format.isOneOf(MIXED,MIXED_PWS,TEXT,TEXT_PWS,XMLTEXT) && type.isOneOf(NULL,STRING,NUMBER,BOOLEAN))
isCollapsed = true;
// Is there a name associated with this bean?
String name = keyName;
if (elementName == null && dictionaryName != null) {
elementName = dictionaryName;
isExpectedType = o != null; // preserve type='null' when it's null.
}
if (elementName == null) {
elementName = name;
name = null;
}
if (eq(name, elementName))
name = null;
if (isEnableNamespaces()) {
if (elementNamespace == null)
elementNamespace = getXmlClassMeta(sType).getNamespace();
if (elementNamespace == null)
elementNamespace = getXmlClassMeta(aType).getNamespace();
if (elementNamespace != null && elementNamespace.uri == null)
elementNamespace = null;
if (elementNamespace == null)
elementNamespace = defaultNamespace;
} else {
elementNamespace = null;
}
// Do we need a carriage return after the start tag?
boolean cr = o != null && (sType.isMapOrBean() || sType.isCollectionOrArray()) && ! isMixedOrText;
String en = elementName;
if (en == null && ! isRaw) {
en = type.toString();
type = null;
}
boolean encodeEn = elementName != null;
String ns = (elementNamespace == null ? null : elementNamespace.name);
String dns = null, elementNs = null;
if (isEnableNamespaces()) {
dns = elementName == null && defaultNamespace != null ? defaultNamespace.name : null;
elementNs = elementName == null ? dns : ns;
if (elementName == null)
elementNamespace = null;
}
// Render the start tag.
if (! isCollapsed) {
if (en != null) {
out.oTag(i, elementNs, en, encodeEn);
if (addNamespaceUris) {
out.attr((String)null, "xmlns", defaultNamespace.getUri());
for (Namespace n : namespaces)
out.attr("xmlns", n.getName(), n.getUri());
}
if (! isExpectedType) {
if (resolvedDictionaryName != null)
out.attr(dns, getBeanTypePropertyName(eType), resolvedDictionaryName);
else if (type != null && type != STRING)
out.attr(dns, getBeanTypePropertyName(eType), type);
}
if (name != null)
out.attr(getNamePropertyName(), name);
} else {
out.i(i);
}
if (o == null) {
if ((sType.isBoolean() || sType.isNumber()) && ! sType.isNullable())
o = sType.getPrimitiveDefault();
}
if (o != null && ! (sType.isMapOrBean() || en == null))
out.w('>');
if (cr && ! (sType.isMapOrBean()))
out.nl(i+1);
}
ContentResult rc = CR_ELEMENTS;
// Render the tag contents.
if (o != null) {
if (sType.isUri() || (pMeta != null && pMeta.isUri())) {
out.textUri(o);
} else if (sType.isCharSequence() || sType.isChar()) {
if (isXmlText(format, sType))
out.append(o);
else
out.text(o, preserveWhitespace);
} else if (sType.isNumber() || sType.isBoolean()) {
out.append(o);
} else if (sType.isMap() || (wType != null && wType.isMap())) {
if (o instanceof BeanMap)
rc = serializeBeanMap(out, (BeanMap)o, elementNamespace, isCollapsed, isMixedOrText);
else
rc = serializeMap(out, (Map)o, sType, eType.getKeyType(), eType.getValueType(), isMixedOrText);
} else if (sType.isBean()) {
rc = serializeBeanMap(out, toBeanMap(o), elementNamespace, isCollapsed, isMixedOrText);
} else if (sType.isCollection() || (wType != null && wType.isCollection())) {
if (isCollapsed)
this.indent--;
serializeCollection(out, o, sType, eType, pMeta, isMixedOrText);
if (isCollapsed)
this.indent++;
} else if (sType.isArray()) {
if (isCollapsed)
this.indent--;
serializeCollection(out, o, sType, eType, pMeta, isMixedOrText);
if (isCollapsed)
this.indent++;
} else if (sType.isReader()) {
pipe((Reader)o, out, SerializerSession::handleThrown);
} else if (sType.isInputStream()) {
pipe((InputStream)o, out, SerializerSession::handleThrown);
} else {
if (isXmlText(format, sType))
out.append(toString(o));
else
out.text(toString(o));
}
}
pop();
// Render the end tag.
if (! isCollapsed) {
if (en != null) {
if (rc == CR_EMPTY) {
if (isHtmlMode())
out.w('>').eTag(elementNs, en, encodeEn);
else
out.w('/').w('>');
} else if (rc == CR_VOID || o == null) {
out.w('/').w('>');
}
else
out.ie(cr && rc != CR_MIXED ? i : 0).eTag(elementNs, en, encodeEn);
}
if (! isMixedOrText)
out.nl(i);
}
return rc;
}