http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java index 6836fbc..cf5f007 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java @@ -15,6 +15,8 @@ package org.apache.juneau.xml; import static org.apache.juneau.serializer.SerializerContext.*; import static org.apache.juneau.xml.XmlSerializerContext.*; import static org.apache.juneau.xml.annotation.XmlFormat.*; +import static org.apache.juneau.xml.XmlSerializer.ContentResult.*; +import static org.apache.juneau.xml.XmlSerializer.JsonType.*; import java.lang.reflect.*; import java.util.*; @@ -81,7 +83,6 @@ import org.apache.juneau.xml.annotation.*; * <p> * An additional "add-json-properties" mode is also provided to prevent loss of JSON data types... * <p class='bcode'> - * <xt><object></xt> * <xt><name</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt>John Smith<xt></name></xt> * <xt><address</xt> <xa>_type</xa>=<xs>'object'</xs><xt>></xt> * <xt><streetAddress</xt> <xa>_type</xa>=<xs>'string'</xs><xt>></xt>21 2nd Street<xt></streetAddress></xt> @@ -118,38 +119,29 @@ import org.apache.juneau.xml.annotation.*; * <ul class='spaced-list'> * <li>{@link Sq} - Default serializer, single quotes. * <li>{@link SqReadable} - Default serializer, single quotes, whitespace added. - * <li>{@link XmlJson} - Default serializer with JSON attribute tags. - * <li>{@link XmlJsonSq} - Default serializer with JSON attribute tags, single quotes. * </ul> */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Produces("text/xml") public class XmlSerializer extends WriterSerializer { - /** Default serializer, all default settings. */ + /** Default serializer without namespaces. */ public static final XmlSerializer DEFAULT = new XmlSerializer().lock(); - /** Default serializer, single quotes. */ + /** Default serializer without namespaces, with single quotes. */ public static final XmlSerializer DEFAULT_SQ = new XmlSerializer.Sq().lock(); - /** Default serializer, single quotes, whitespace added. */ + /** Default serializer without namespaces, with single quotes, whitespace added. */ public static final XmlSerializer DEFAULT_SQ_READABLE = new XmlSerializer.SqReadable().lock(); - /** Default serializer with JSON attribute tags. */ - public static final XmlSerializer DEFAULT_XMLJSON = new XmlSerializer.XmlJson().lock(); - - /** Default serializer with JSON attribute tags, single quotes. */ - public static final XmlSerializer DEFAULT_XMLJSON_SQ = new XmlSerializer.XmlJsonSq().lock(); - - /** Default serializer without namespaces. */ - public static final XmlSerializer DEFAULT_SIMPLE = new XmlSerializer.Simple().lock(); - - /** Default serializer without namespaces, with single quotes. */ - public static final XmlSerializer DEFAULT_SIMPLE_SQ = new XmlSerializer.SimpleSq().lock(); + /** Default serializer, all default settings. */ + public static final XmlSerializer DEFAULT_NS = new XmlSerializer.Ns().lock(); - /** Default serializer without namespaces, with JSON attribute tags and single quotes. */ - public static final XmlSerializer DEFAULT_SIMPLE_XMLJSON_SQ = new XmlSerializer.SimpleXmlJsonSq().lock(); + /** Default serializer, single quotes. */ + public static final XmlSerializer DEFAULT_NS_SQ = new XmlSerializer.NsSq().lock(); + /** Default serializer, single quotes, whitespace added. */ + public static final XmlSerializer DEFAULT_NS_SQ_READABLE = new XmlSerializer.NsSqReadable().lock(); /** Default serializer, single quotes. */ public static class Sq extends XmlSerializer { @@ -167,45 +159,28 @@ public class XmlSerializer extends WriterSerializer { } } - /** Default serializer with JSON attribute tags. */ - @Produces(value="text/xml+json",contentType="text/xml") - public static class XmlJson extends XmlSerializer { - /** Constructor */ - public XmlJson() { - setProperty(XML_addJsonTypeAttrs, true); - } - } - - /** Default serializer with JSON attribute tags, single quotes. */ - public static class XmlJsonSq extends XmlJson { - /** Constructor */ - public XmlJsonSq() { - setProperty(SERIALIZER_quoteChar, '\''); - } - } - /** Default serializer without namespaces. */ @Produces(value="text/xml+simple",contentType="text/xml") - public static class Simple extends XmlSerializer { + public static class Ns extends XmlSerializer { /** Constructor */ - public Simple() { - setProperty(XML_enableNamespaces, false); + public Ns() { + setProperty(XML_enableNamespaces, true); } } /** Default serializer without namespaces, single quotes. */ - public static class SimpleSq extends Simple { + public static class NsSq extends Ns { /** Constructor */ - public SimpleSq() { + public NsSq() { setProperty(SERIALIZER_quoteChar, '\''); } } - /** Default serializer with JSON attribute tags, single quotes. */ - public static class SimpleXmlJsonSq extends SimpleSq { + /** Default serializer without namespaces, single quotes, with whitespace. */ + public static class NsSqReadable extends NsSq { /** Constructor */ - public SimpleXmlJsonSq() { - setProperty(XML_addJsonTypeAttrs, true); + public NsSqReadable() { + setProperty(SERIALIZER_useIndentation, true); } } @@ -303,18 +278,27 @@ public class XmlSerializer extends WriterSerializer { * @param elementNamespace The namespace of the element. * @param addNamespaceUris Flag indicating that namespace URIs need to be added. * @param format The format to serialize the output to. + * @param isMixed We're serializing mixed content, so don't use whitespace. * @param pMeta The bean property metadata if this is a bean property being serialized. * @return The same writer passed in so that calls to the writer can be chained. * @throws Exception If a problem occurred trying to convert the output. */ - protected XmlWriter serializeAnything(XmlSerializerSession session, XmlWriter out, Object o, - ClassMeta eType, String elementName, Namespace elementNamespace, boolean addNamespaceUris, - XmlFormat format, BeanPropertyMeta pMeta) throws Exception { - - String ts = null; // The type string (e.g. <type> or <x x='type'> - int indent = session.indent; // Current indentation + protected XmlWriter serializeAnything( + XmlSerializerSession session, + XmlWriter out, + Object o, + ClassMeta eType, + String elementName, + Namespace elementNamespace, + boolean addNamespaceUris, + XmlFormat format, + boolean isMixed, + BeanPropertyMeta pMeta) throws Exception { + + JsonType type = null; // The type string (e.g. <type> or <x x='type'> + int indent = isMixed ? 0 : session.indent; // Current indentation ClassMeta<?> aType = null; // The actual type - ClassMeta<?> wType = null; // The wrapped type + ClassMeta<?> wType = null; // The wrapped type (delegate) ClassMeta<?> sType = object(); // The serialized type aType = session.push(elementName, o, eType); @@ -332,7 +316,7 @@ public class XmlSerializer extends WriterSerializer { if (aType.isDelegate()) { wType = aType; - aType = ((Delegate)o).getClassMeta(); + eType = aType = ((Delegate)o).getClassMeta(); } sType = aType.getSerializedClassMeta(); @@ -351,12 +335,22 @@ public class XmlSerializer extends WriterSerializer { sType = eType.getSerializedClassMeta(); } - String typeName = null; - if (session.isAddBeanTypeProperties()) { - if (o != null && ! eType.equals(aType)) - typeName = aType.getDictionaryName(); + // 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.isCollection()) + isExpectedType = aType.isCollection(); + else + isExpectedType = false; } + String resolvedDictionaryName = isExpectedType ? null : aType.getResolvedDictionaryName(); + String dictionaryName = aType.getDictionaryName(); + // char '\0' is interpreted as null. if (o != null && sType.isChar() && ((Character)o).charValue() == 0) o = null; @@ -364,33 +358,32 @@ public class XmlSerializer extends WriterSerializer { boolean isCollapsed = false; // If 'true', this is a collection and we're not rendering the outer element. // Get the JSON type string. - if (sType.isCharSequence() || sType.isChar()) - ts = "string"; - else if (sType.isNumber()) - ts = "number"; - else if (sType.isBoolean()) - ts = "boolean"; - else if (sType.isMap() || sType.isBean() || sType.hasToObjectMapMethod()) { - isCollapsed = sType.getExtendedMeta(XmlClassMeta.class).getFormat() == XmlFormat.COLLAPSED; - ts = "object"; - } - else if (sType.isCollection() || sType.isArray()) { + 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() || sType.hasToObjectMapMethod()) { + isCollapsed = sType.getExtendedMeta(XmlClassMeta.class).getFormat() == COLLAPSED; + type = OBJECT; + } else if (sType.isCollectionOrArray()) { isCollapsed = (format == COLLAPSED && ! addNamespaceUris); - ts = "array"; + type = ARRAY; + } else { + type = STRING; } - else - ts = "string"; - // Is there a name associated with this bean? - if (elementName == null) - elementName = sType.getDictionaryName(); + if (format.isOneOf(MIXED,TEXT,XMLTEXT) && type.isOneOf(NULL,STRING,NUMBER,BOOLEAN)) + isCollapsed = true; - // If the value is null then it's either going to be <null/> or <XmlSerializer nil='true'/> - // depending on whether the element has a name. - boolean isNullTag = (elementName == null && o == null); - - if (isNullTag) - ts = "null"; + // Is there a name associated with this bean? + if (elementName == null && dictionaryName != null) { + elementName = dictionaryName; + isExpectedType = true; + } if (session.isEnableNamespaces()) { if (elementNamespace == null) @@ -406,14 +399,17 @@ public class XmlSerializer extends WriterSerializer { } // Do we need a carriage return after the start tag? - boolean cr = o != null && (sType.isMap() || sType.isCollection() || sType.isArray() || sType.isBean() || sType.hasToObjectMapMethod()); + boolean cr = o != null && (sType.isMapOrBean() || sType.isCollectionOrArray() || sType.hasToObjectMapMethod()) && ! isMixed; - String en = (elementName == null ? ts : elementName); + String en = elementName; + if (en == null) { + en = type.toString(); + type = null; + } boolean encodeEn = elementName != null; String ns = (elementNamespace == null ? null : elementNamespace.name); - String xsi = null, dns = null, elementNs = null; + String dns = null, elementNs = null; if (session.isEnableNamespaces()) { - xsi = session.getXsiNamespace().name; dns = elementName == null && session.getDefaultNamespace() != null ? session.getDefaultNamespace().name : null; elementNs = elementName == null ? dns : ns; if (elementName == null) @@ -428,85 +424,90 @@ public class XmlSerializer extends WriterSerializer { for (Namespace n : session.getNamespaces()) out.attr("xmlns", n.getName(), n.getUri()); - - Namespace xsiNs = session.getXsiNamespace(); - if (xsiNs != null) - out.attr("xmlns", xsiNs.name, xsiNs.uri); } - if (typeName == null && elementName != null && session.isAddJsonTypeAttrs() && (session.isAddJsonStringTypeAttrs() || ! ts.equals("string"))) - typeName = ts; - if (typeName != null && ! typeName.equals(elementName)) - out.attr(dns, session.getBeanTypePropertyName(), typeName); + if (! isExpectedType) { + if (resolvedDictionaryName != null) + out.attr(dns, session.getBeanTypePropertyName(), resolvedDictionaryName); + else if (type != null && type != STRING) + out.attr(dns, session.getBeanTypePropertyName(), type); + } if (o == null) { - if (! isNullTag) - out.attr(xsi, "nil", "true"); if ((sType.isBoolean() || sType.isNumber()) && ! sType.isNullable()) o = sType.getPrimitiveDefault(); } - if (o != null && !(sType.isMap() || sType.isBean() || sType.hasToObjectMapMethod())) + if (o != null && ! (sType.isMapOrBean() || sType.hasToObjectMapMethod())) out.append('>'); - if (cr && !(sType.isMap() || sType.isBean() || sType.hasToObjectMapMethod())) + if (cr && ! (sType.isMapOrBean() || sType.hasToObjectMapMethod())) out.nl(); } - boolean hasChildren = true; + ContentResult rc = CR_ELEMENTS; // Render the tag contents. if (o != null) { - if (sType.isUri() || (pMeta != null && pMeta.isUri())) + if (sType.isUri() || (pMeta != null && pMeta.isUri())) { out.appendUri(o); - else if (sType.isCharSequence() || sType.isChar()) - out.encodeText(session.trim(o)); - else if (sType.isNumber() || sType.isBoolean()) + } else if (sType.isCharSequence() || sType.isChar()) { + if (format == XMLTEXT) + out.append(o); + else + out.encodeText(session.trim(o)); + } else if (sType.isNumber() || sType.isBoolean()) { out.append(o); - else if (sType.isMap() || (wType != null && wType.isMap())) { + } else if (sType.isMap() || (wType != null && wType.isMap())) { if (o instanceof BeanMap) - hasChildren = serializeBeanMap(session, out, (BeanMap)o, elementNamespace, isCollapsed); + rc = serializeBeanMap(session, out, (BeanMap)o, elementNamespace, isCollapsed, isMixed); else - hasChildren = serializeMap(session, out, (Map)o, sType); - } - else if (sType.hasToObjectMapMethod()) - hasChildren = serializeMap(session, out, sType.toObjectMap(o), sType); - else if (sType.isBean()) - hasChildren = serializeBeanMap(session, out, session.toBeanMap(o), elementNamespace, isCollapsed); - else if (sType.isCollection() || (wType != null && wType.isCollection())) { + rc = serializeMap(session, out, (Map)o, sType, eType.getKeyType(), eType.getValueType(), isMixed); + } else if (sType.hasToObjectMapMethod()) { + rc = serializeMap(session, out, sType.toObjectMap(o), sType, null, null, isMixed); + } else if (sType.isBean()) { + rc = serializeBeanMap(session, out, session.toBeanMap(o), elementNamespace, isCollapsed, isMixed); + } else if (sType.isCollection() || (wType != null && wType.isCollection())) { if (isCollapsed) session.indent--; - serializeCollection(session, out, (Collection)o, sType, pMeta); + serializeCollection(session, out, o, sType, eType, pMeta, isMixed); if (isCollapsed) session.indent++; - } - else if (sType.isArray()) { + } else if (sType.isArray()) { if (isCollapsed) session.indent--; - serializeCollection(session, out, toList(sType.getInnerClass(), o), sType, pMeta); + if (resolvedDictionaryName != null) + eType = aType; + serializeCollection(session, out, o, sType, eType, pMeta, isMixed); if (isCollapsed) session.indent++; + } else { + if (format == XMLTEXT) + out.append(session.toString(o)); + else + out.encodeText(session.toString(o)); } - else - out.encodeText(session.toString(o)); } session.pop(); // Render the end tag. if (! isCollapsed) { - if (o == null || ! hasChildren) - out.append('/').append('>').nl(); + if (o == null || rc == CR_EMPTY) + out.append('/').append('>'); else - out.i(cr ? indent : 0).eTag(elementNs, en, encodeEn).nl(); + out.i(cr && rc != CR_MIXED ? indent : 0).eTag(elementNs, en, encodeEn); + if (! isMixed) + out.nl(); } return out; } - private boolean serializeMap(XmlSerializerSession session, XmlWriter out, Map m, ClassMeta<?> type) throws Exception { + private ContentResult serializeMap(XmlSerializerSession session, XmlWriter out, Map m, ClassMeta<?> sType, ClassMeta<?> eKeyType, ClassMeta<?> eValueType, boolean isMixed) throws Exception { m = session.sort(m); - ClassMeta<?> keyType = type.getKeyType(), valueType = type.getValueType(); + ClassMeta<?> keyType = eKeyType == null ? sType.getKeyType() : eKeyType; + ClassMeta<?> valueType = eValueType == null ? sType.getValueType() : eValueType; boolean hasChildren = false; for (Iterator i = m.entrySet().iterator(); i.hasNext();) { @@ -525,23 +526,36 @@ public class XmlSerializer extends WriterSerializer { if (! hasChildren) { hasChildren = true; - out.append('>').nl(); + out.append('>').nlIf(! isMixed); } - serializeAnything(session, out, value, valueType, session.toString(k), null, false, NORMAL, null); + serializeAnything(session, out, value, valueType, session.toString(k), null, false, XmlFormat.DEFAULT, isMixed, null); } - return hasChildren; + return hasChildren ? CR_ELEMENTS : CR_EMPTY; } - private boolean serializeBeanMap(XmlSerializerSession session, XmlWriter out, BeanMap<?> m, Namespace elementNs, boolean isCollapsed) throws Exception { + private ContentResult serializeBeanMap(XmlSerializerSession session, XmlWriter out, BeanMap<?> m, Namespace elementNs, boolean isCollapsed, boolean isMixed) throws Exception { boolean hasChildren = false; BeanMeta<?> bm = m.getMeta(); List<BeanPropertyValue> lp = m.getValues(session.isTrimNulls()); - Map<String,BeanPropertyMeta> xmlAttrs = bm.getExtendedMeta(XmlBeanMeta.class).getXmlAttrProperties(); + XmlBeanMeta xbm = bm.getExtendedMeta(XmlBeanMeta.class); + + Set<String> + attrs = xbm.getAttrPropertyNames(), + elements = xbm.getElementPropertyNames(), + collapsedElements = xbm.getCollapsedPropertyNames(); + String + attrsProperty = xbm.getAttrsPropertyName(), + contentProperty = xbm.getContentPropertyName(); + + XmlFormat cf = null; + Object content = null; + ClassMeta<?> contentType = null; for (BeanPropertyValue p : lp) { - if (xmlAttrs.containsKey(p.getName())) { + String n = p.getName(); + if (attrs.contains(n) || n.equals(attrsProperty)) { BeanPropertyMeta pMeta = p.getMeta(); ClassMeta<?> cMeta = p.getClassMeta(); @@ -556,10 +570,28 @@ public class XmlSerializer extends WriterSerializer { Namespace ns = (session.isEnableNamespaces() && pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() != elementNs ? pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() : null); - if (pMeta.isUri()) + if (pMeta.isUri()) { out.attrUri(ns, key, value); - else + } else if (n.equals(attrsProperty)) { + if (value instanceof BeanMap) { + BeanMap<?> bm2 = (BeanMap)value; + for (BeanPropertyValue p2 : bm2.getValues(true)) { + String key2 = p2.getName(); + Object value2 = p2.getValue(); + Throwable t2 = p2.getThrown(); + if (t2 != null) + session.addBeanGetterWarning(pMeta, t); + out.attr(ns, key2, value2); + } + } else /* Map */ { + Map m2 = (Map)value; + for (Map.Entry e : (Set<Map.Entry>)(m2.entrySet())) { + out.attr(ns, session.toString(e.getKey()), e.getValue()); + } + } + } else { out.attr(ns, key, value); + } } } @@ -569,14 +601,15 @@ public class XmlSerializer extends WriterSerializer { BeanPropertyMeta pMeta = p.getMeta(); ClassMeta<?> cMeta = p.getClassMeta(); - XmlFormat xf = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat(); - - if (xf == CONTENT) { + String n = p.getName(); + if (n.equals(contentProperty)) { content = p.getValue(); + contentType = p.getClassMeta(); hasContent = true; - } else if (xf == ATTR) { - // Do nothing - } else { + cf = xbm.getContentFormat(); + if (cf.isOneOf(MIXED,TEXT,XMLTEXT)) + isMixed = true; + } else if (elements.contains(n) || collapsedElements.contains(n)) { String key = p.getName(); Object value = p.getValue(); Throwable t = p.getThrown(); @@ -588,52 +621,74 @@ public class XmlSerializer extends WriterSerializer { if (! hasChildren) { hasChildren = true; - out.appendIf(! isCollapsed, '>').nl(); + out.appendIf(! isCollapsed, '>').nlIf(! isMixed); } - serializeAnything(session, out, value, cMeta, key, pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace(), false, pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat(), pMeta); + + XmlBeanPropertyMeta xbpm = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class); + serializeAnything(session, out, value, cMeta, key, xbpm.getNamespace(), false, xbpm.getXmlFormat(), isMixed, pMeta); } } if ((! hasContent) || session.canIgnoreValue(string(), null, content)) - return hasChildren; - out.append('>').cr(session.indent); + return (hasChildren ? CR_ELEMENTS : CR_EMPTY); + out.append('>').nlIf(! isMixed); // Serialize XML content. - XmlContentHandler h = bm.getExtendedMeta(XmlBeanMeta.class).getXmlContentHandler(); - if (h != null) - h.serialize(out, m.getBean()); - else - out.encodeText(content); - out.nl(); - return true; + if (content != null) { + if (contentType == null) { + } else if (contentType.isCollection()) { + Collection c = (Collection)content; + for (Iterator i = c.iterator(); i.hasNext();) { + Object value = i.next(); + serializeAnything(session, out, value, contentType.getElementType(), null, null, false, cf, isMixed, null); + } + } else if (contentType.isArray()) { + Collection c = toList(Object[].class, content); + for (Iterator i = c.iterator(); i.hasNext();) { + Object value = i.next(); + serializeAnything(session, out, value, contentType.getElementType(), null, null, false, cf, isMixed, null); + } + } else { + serializeAnything(session, out, content, contentType, null, null, false, cf, isMixed, null); + } + } else { + if (! session.isTrimNulls()) { + if (! isMixed) + out.i(session.indent); + out.encodeText(content); + if (! isMixed) + out.nl(); + } + } + return isMixed ? CR_MIXED : CR_ELEMENTS; } - private XmlWriter serializeCollection(XmlSerializerSession session, XmlWriter out, Collection c, ClassMeta<?> type, BeanPropertyMeta ppMeta) throws Exception { + private XmlWriter serializeCollection(XmlSerializerSession session, XmlWriter out, Object in, ClassMeta<?> sType, ClassMeta<?> eType, BeanPropertyMeta ppMeta, boolean isMixed) throws Exception { + + ClassMeta<?> seType = sType.getElementType(); + if (seType == null) + seType = session.object(); + ClassMeta<?> eeType = eType.getElementType(); + + Collection c = (sType.isCollection() ? (Collection)in : toList(sType.getInnerClass(), in)); c = session.sort(c); - ClassMeta<?> elementType = type.getElementType(); + String type2 = null; + if (sType != eType) + type2 = sType.getDictionaryName(); - String eName = null; + String eName = type2; Namespace eNs = null; if (ppMeta != null) { - eName = ppMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getChildName(); - eNs = ppMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace(); - } - - if (eName == null) { - eName = type.getExtendedMeta(XmlClassMeta.class).getChildName(); - eNs = type.getExtendedMeta(XmlClassMeta.class).getNamespace(); - } - - if (eName == null && ! elementType.isObject()) { - eName = elementType.getDictionaryName(); - eNs = elementType.getExtendedMeta(XmlClassMeta.class).getNamespace(); + XmlBeanPropertyMeta xbpm = ppMeta.getExtendedMeta(XmlBeanPropertyMeta.class); + eName = xbpm.getChildName(); + eNs = xbpm.getNamespace(); } for (Iterator i = c.iterator(); i.hasNext();) { Object value = i.next(); - serializeAnything(session, out, value, elementType, eName, eNs, false, NORMAL, null); + serializeAnything(session, out, value, eeType, eName, eNs, false, XmlFormat.DEFAULT, isMixed, null); } return out; } @@ -647,6 +702,36 @@ public class XmlSerializer extends WriterSerializer { return s; } + static enum JsonType { + STRING("string"),BOOLEAN("boolean"),NUMBER("number"),ARRAY("array"),OBJECT("object"),NULL("null"); + + private final String value; + private JsonType(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + + boolean isOneOf(JsonType...types) { + for (JsonType type : types) + if (type == this) + return true; + return false; + } + } + + /** + * Identifies what the contents were of a serialized bean. + */ + static enum ContentResult { + CR_EMPTY, // No content...append "/>" to the start tag. + CR_MIXED, // Mixed content...don't add whitespace. + CR_ELEMENTS // Elements...use normal whitespace rules. + } + //-------------------------------------------------------------------------------- // Overridden methods @@ -657,7 +742,7 @@ public class XmlSerializer extends WriterSerializer { XmlSerializerSession s = (XmlSerializerSession)session; if (s.isEnableNamespaces() && s.isAutoDetectNamespaces()) findNsfMappings(s, o); - serializeAnything(s, s.getWriter(), o, null, null, null, s.isEnableNamespaces() && s.isAddNamespaceUrlsToRoot(), NORMAL, null); + serializeAnything(s, s.getWriter(), o, null, null, null, s.isEnableNamespaces() && s.isAddNamespaceUrlsToRoot(), XmlFormat.DEFAULT, false, null); } @Override /* Serializer */
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerContext.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerContext.java index 9b7b65c..d1d51df 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerContext.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerContext.java @@ -38,24 +38,10 @@ import org.apache.juneau.serializer.*; * <table class='styled' style='border-collapse: collapse;'> * <tr><th>Setting name</th><th>Description</th><th>Data type</th><th>Default value</th><th>Session overridable</th></tr> * <tr> - * <td>{@link #XML_addJsonTypeAttrs}</td> - * <td>Add JSON type attributes to output.</td> - * <td><code>Boolean</code></td> - * <td><jk>false</jk></td> - * <td><jk>true</jk></td> - * </tr> - * <tr> - * <td>{@link #XML_addJsonStringTypeAttrs}</td> - * <td>Add JSON type attributes for strings to output.</td> - * <td><code>Boolean</code></td> - * <td><jk>false</jk></td> - * <td><jk>true</jk></td> - * </tr> - * <tr> * <td>{@link #XML_enableNamespaces}</td> * <td>Enable support for XML namespaces.</td> * <td><code>Boolean</code></td> - * <td><jk>true</jk></td> + * <td><jk>false</jk></td> * <td><jk>true</jk></td> * </tr> * <tr> @@ -69,11 +55,11 @@ import org.apache.juneau.serializer.*; * <td>{@link #XML_addNamespaceUrisToRoot}</td> * <td>Add namespace URLs to the root element.</td> * <td><code>Boolean</code></td> - * <td><jk>true</jk></td> + * <td><jk>false</jk></td> * <td><jk>true</jk></td> * </tr> * <tr> - * <td>{@link #XML_defaultNamespaceUri}</td> + * <td>{@link #XML_defaultNamespace}</td> * <td>Default namespace URI.</td> * <td><code>String</code></td> * <td><jk>null</jk></td> @@ -87,13 +73,6 @@ import org.apache.juneau.serializer.*; * <td><jk>true</jk></td> * </tr> * <tr> - * <td>{@link #XML_xsiNamespace}</td> - * <td>XMLSchema-Instance namespace.</td> - * <td>{@link Namespace}</td> - * <td><code>{name:<js>'xsi'</js>,uri:<js>'http://www.w3.org/2001/XMLSchema-instance'</js>}</code></td> - * <td><jk>true</jk></td> - * </tr> - * <tr> * <td>{@link #XML_namespaces}</td> * <td>Default namespaces.</td> * <td><code>Set<{@link Namespace}></code></td> @@ -113,45 +92,12 @@ import org.apache.juneau.serializer.*; public class XmlSerializerContext extends SerializerContext { /** - * <b>Configuration property:</b> Add JSON type attributes to output. - * <p> - * <ul> - * <li><b>Name:</b> <js>"XmlSerializer.addJsonTypeAttrs"</js> - * <li><b>Data type:</b> <code>Boolean</code> - * <li><b>Default:</b> <jk>false</jk> - * <li><b>Session-overridable:</b> <jk>true</jk> - * </ul> - * <p> - * If <js>true</jk>, {@code type} attributes will be added to elements in the XML for number/boolean/null nodes. - */ - public static final String XML_addJsonTypeAttrs = "XmlSerializer.addJsonTypeAttrs"; - - /** - * <b>Configuration property:</b> Add JSON type attributes for strings to output. - * <p> - * <ul> - * <li><b>Name:</b> <js>"XmlSerializer.addJsonStringTypeAttrs"</js> - * <li><b>Data type:</b> <code>Boolean</code> - * <li><b>Default:</b> <jk>false</jk> - * <li><b>Session-overridable:</b> <jk>true</jk> - * </ul> - * <p> - * If <jk>true</jk>, {@code type} attributes will be added to elements in the XML for string nodes. - * <p> - * By default, these attributes are not added, and the parser will assume that the content type - * of the node is string by default. - * <p> - * This feature is disabled if {@link #XML_addJsonTypeAttrs} is disabled. - */ - public static final String XML_addJsonStringTypeAttrs = "XmlSerializer.addJsonStringTypeAttrs"; - - /** * <b>Configuration property:</b> Enable support for XML namespaces. * <p> * <ul> * <li><b>Name:</b> <js>"XmlSerializer.enableNamespaces"</js> * <li><b>Data type:</b> <code>Boolean</code> - * <li><b>Default:</b> <jk>true</jk> + * <li><b>Default:</b> <jk>false</jk> * <li><b>Session-overridable:</b> <jk>true</jk> * </ul> * <p> @@ -194,7 +140,7 @@ public class XmlSerializerContext extends SerializerContext { * <ul> * <li><b>Name:</b> <js>"XmlSerializer.addNamespaceUrisToRoot"</js> * <li><b>Data type:</b> <code>Boolean</code> - * <li><b>Default:</b> <jk>true</jk> + * <li><b>Default:</b> <jk>false</jk> * <li><b>Session-overridable:</b> <jk>true</jk> * </ul> * <p> @@ -206,18 +152,18 @@ public class XmlSerializerContext extends SerializerContext { public static final String XML_addNamespaceUrisToRoot = "XmlSerializer.addNamespaceUrisToRoot"; /** - * <b>Configuration property:</b> Default namespace URI. + * <b>Configuration property:</b> Default namespace. * <p> * <ul> - * <li><b>Name:</b> <js>"XmlSerializer.defaultNamespaceUri"</js> + * <li><b>Name:</b> <js>"XmlSerializer.defaultNamespace"</js> * <li><b>Data type:</b> <code>String</code> - * <li><b>Default:</b> <jk>null</jk> + * <li><b>Default:</b> <js>"{juneau:'http://www.apache.org/2013/Juneau'}"</js> * <li><b>Session-overridable:</b> <jk>true</jk> * </ul> * <p> * Specifies the default namespace URI for this document. */ - public static final String XML_defaultNamespaceUri = "XmlSerializer.defaultNamespaceUri"; + public static final String XML_defaultNamespace = "XmlSerializer.defaultNamespace"; /** * <b>Configuration property:</b> XMLSchema namespace. @@ -235,20 +181,6 @@ public class XmlSerializerContext extends SerializerContext { public static final String XML_xsNamespace = "XmlSerializer.xsNamespace"; /** - * <b>Configuration property:</b> XMLSchema-Instance namespace. - * <p> - * <ul> - * <li><b>Name:</b> <js>"XmlSerializer.xsiNamespace"</js> - * <li><b>Data type:</b> {@link Namespace} - * <li><b>Default:</b> <code>{name:<js>'xsi'</js>,uri:<js>'http://www.w3.org/2001/XMLSchema-instance'</js>}</code> - * <li><b>Session-overridable:</b> <jk>true</jk> - * </ul> - * <p> - * Specifies the namespace of the <code>XMLSchema-instance</code> namespace used for<code>nil=<jk>true</jk></code> attributes. - */ - public static final String XML_xsiNamespace = "XmlSerializer.xsiNamespace"; - - /** * <b>Configuration property:</b> Default namespaces. * <p> * <ul> @@ -260,12 +192,10 @@ public class XmlSerializerContext extends SerializerContext { * <p> * The default list of namespaces associated with this serializer. */ - public static final String XML_namespaces = "XmlSerializer.namespaces"; + public static final String XML_namespaces = "XmlSerializer.namespaces.list"; final boolean - addJsonTypeAttrs, - addJsonStringTypeAttrs, autoDetectNamespaces, enableNamespaces, addNamespaceUrlsToRoot; @@ -273,7 +203,6 @@ public class XmlSerializerContext extends SerializerContext { final String defaultNamespace; final Namespace - xsiNamespace, xsNamespace; final Namespace[] namespaces; @@ -287,14 +216,11 @@ public class XmlSerializerContext extends SerializerContext { */ public XmlSerializerContext(ContextFactory cf) { super(cf); - addJsonTypeAttrs = cf.getProperty(XML_addJsonTypeAttrs, boolean.class, false); - addJsonStringTypeAttrs = cf.getProperty(XML_addJsonStringTypeAttrs, boolean.class, false); autoDetectNamespaces = cf.getProperty(XML_autoDetectNamespaces, boolean.class, true); - enableNamespaces = cf.getProperty(XML_enableNamespaces, boolean.class, true); - addNamespaceUrlsToRoot = cf.getProperty(XML_addNamespaceUrisToRoot, boolean.class, true); - defaultNamespace = cf.getProperty(XML_defaultNamespaceUri, String.class, "{juneau:'http://www.apache.org/2013/Juneau'}"); + enableNamespaces = cf.getProperty(XML_enableNamespaces, boolean.class, false); + addNamespaceUrlsToRoot = cf.getProperty(XML_addNamespaceUrisToRoot, boolean.class, false); + defaultNamespace = cf.getProperty(XML_defaultNamespace, String.class, "{juneau:'http://www.apache.org/2013/Juneau'}"); xsNamespace = cf.getProperty(XML_xsNamespace, Namespace.class, new Namespace("xs", "http://www.w3.org/2001/XMLSchema")); - xsiNamespace = cf.getProperty(XML_xsiNamespace, Namespace.class, new Namespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")); namespaces = cf.getProperty(XML_namespaces, Namespace[].class, new Namespace[0]); } @@ -302,14 +228,11 @@ public class XmlSerializerContext extends SerializerContext { public ObjectMap asMap() { return super.asMap() .append("XmlSerializerContext", new ObjectMap() - .append("addJsonTypeAttrs", addJsonTypeAttrs) - .append("addJsonStringTypeAttrs", addJsonStringTypeAttrs) .append("autoDetectNamespaces", autoDetectNamespaces) .append("enableNamespaces", enableNamespaces) .append("addNamespaceUrlsToRoot", addNamespaceUrlsToRoot) .append("defaultNamespace", defaultNamespace) .append("xsNamespace", xsNamespace) - .append("xsiNamespace", xsiNamespace) .append("namespaces", namespaces) ); } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java index dfdd942..6a13cb4 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java @@ -32,8 +32,6 @@ import org.apache.juneau.serializer.*; public class XmlSerializerSession extends SerializerSession { private final boolean - addJsonTypeAttrs, - addJsonStringTypeAttrs, autoDetectNamespaces, enableNamespaces, addNamespaceUrlsToRoot; @@ -41,7 +39,6 @@ public class XmlSerializerSession extends SerializerSession { private Namespace defaultNamespace; private final Namespace - xsiNamespace, xsNamespace; private Namespace[] namespaces = new Namespace[0]; @@ -63,24 +60,18 @@ public class XmlSerializerSession extends SerializerSession { public XmlSerializerSession(XmlSerializerContext ctx, ObjectMap op, Object output, Method javaMethod, Locale locale, TimeZone timeZone) { super(ctx, op, output, javaMethod, locale, timeZone); if (op == null || op.isEmpty()) { - addJsonTypeAttrs = ctx.addJsonTypeAttrs; - addJsonStringTypeAttrs = ctx.addJsonStringTypeAttrs; enableNamespaces = ctx.enableNamespaces; autoDetectNamespaces = ctx.autoDetectNamespaces; addNamespaceUrlsToRoot = ctx.addNamespaceUrlsToRoot; addNamespaces(ctx.namespaces); defaultNamespace = findDefaultNamespace(ctx.defaultNamespace); - xsiNamespace = ctx.xsiNamespace; xsNamespace = ctx.xsNamespace; } else { - addJsonTypeAttrs = op.getBoolean(XML_addJsonTypeAttrs, ctx.addJsonTypeAttrs); - addJsonStringTypeAttrs = op.getBoolean(XML_addJsonStringTypeAttrs, ctx.addJsonStringTypeAttrs); enableNamespaces = op.getBoolean(XML_enableNamespaces, ctx.enableNamespaces); autoDetectNamespaces = op.getBoolean(XML_autoDetectNamespaces, ctx.autoDetectNamespaces); addNamespaceUrlsToRoot = op.getBoolean(XML_addNamespaceUrisToRoot, ctx.addNamespaceUrlsToRoot); namespaces = (op.containsKey(XML_namespaces) ? parseNamespaces(op.get(XML_namespaces)) : ctx.namespaces); - defaultNamespace = findDefaultNamespace(op.containsKey(XML_defaultNamespaceUri) ? op.getString(XML_defaultNamespaceUri) : ctx.defaultNamespace); - xsiNamespace = (op.containsKey(XML_xsiNamespace) ? parseNamespace(op.get(XML_xsiNamespace)) : ctx.xsiNamespace); + defaultNamespace = findDefaultNamespace(op.containsKey(XML_defaultNamespace) ? op.getString(XML_defaultNamespace) : ctx.defaultNamespace); xsNamespace = (op.containsKey(XML_xsNamespace) ? parseNamespace(op.get(XML_xsNamespace)) : ctx.xsNamespace); } } @@ -129,24 +120,6 @@ public class XmlSerializerSession extends SerializerSession { } /** - * Returns the {@link XmlSerializerContext#XML_addJsonTypeAttrs} setting value in this context. - * - * @return The {@link XmlSerializerContext#XML_addJsonTypeAttrs} setting value in this context. - */ - public final boolean isAddJsonTypeAttrs() { - return addJsonTypeAttrs; - } - - /** - * Returns the {@link XmlSerializerContext#XML_addJsonStringTypeAttrs} setting value in this context. - * - * @return The {@link XmlSerializerContext#XML_addJsonStringTypeAttrs} setting value in this context. - */ - public final boolean isAddJsonStringTypeAttrs() { - return addJsonStringTypeAttrs; - } - - /** * Returns the {@link XmlSerializerContext#XML_autoDetectNamespaces} setting value in this context. * * @return The {@link XmlSerializerContext#XML_autoDetectNamespaces} setting value in this context. @@ -174,24 +147,15 @@ public class XmlSerializerSession extends SerializerSession { } /** - * Returns the {@link XmlSerializerContext#XML_defaultNamespaceUri} setting value in this context. + * Returns the {@link XmlSerializerContext#XML_defaultNamespace} setting value in this context. * - * @return The {@link XmlSerializerContext#XML_defaultNamespaceUri} setting value in this context. + * @return The {@link XmlSerializerContext#XML_defaultNamespace} setting value in this context. */ public final Namespace getDefaultNamespace() { return defaultNamespace; } /** - * Returns the {@link XmlSerializerContext#XML_xsiNamespace} setting value in this context. - * - * @return The {@link XmlSerializerContext#XML_xsiNamespace} setting value in this context. - */ - public final Namespace getXsiNamespace() { - return xsiNamespace; - } - - /** * Returns the {@link XmlSerializerContext#XML_xsNamespace} setting value in this context. * * @return The {@link XmlSerializerContext#XML_xsNamespace} setting value in this context. http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java index b512537..4ca4b20 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java @@ -234,16 +234,18 @@ public final class XmlUtils { * Translates any _x####_ sequences (introduced by the various encode methods) back into their original characters. * * @param s The string being decoded. + * @param sb The string builder to use as a scratch pad. * @return The decoded string. */ - public static final String decode(String s) { + public static final String decode(String s, StringBuilder sb) { if (s == null) return null; if (s.length() == 0) return s; if (s.indexOf('_') == -1) return s; - StringBuffer sb = new StringBuffer(s.length()); + if (sb == null) + sb = new StringBuilder(s.length()); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '_' && isEscapeSequence(s,i)) { @@ -570,4 +572,45 @@ public final class XmlUtils { return null; } + + /** + * Utility method that converts the current event on the XML stream to something human-readable for debug purposes. + * + * @param r The XML stream reader whose current event is to be converted to a readable string. + * @return The event in human-readable form. + */ + public static final String toReadableEvent(XMLStreamReader r) { + int t = r.getEventType(); + if (t == 1) + return "<"+r.getLocalName()+">"; + if (t == 2) + return "</"+r.getLocalName()+">"; + if (t == 3) + return "PROCESSING_INSTRUCTION"; + if (t == 4) + return "CHARACTERS=[" + r.getText() + "]"; + if (t == 5) + return "COMMENTS=[" + r.getText() + "]"; + if (t == 6) + return "SPACE=[" + r.getText() + "]"; + if (t == 7) + return "START_DOCUMENT"; + if (t == 8) + return "END_DOCUMENT"; + if (t == 9) + return "ENTITY_REFERENCE"; + if (t == 10) + return "ATTRIBUTE"; + if (t == 11) + return "DTD"; + if (t == 12) + return "CDATA=["+r.getText()+"]"; + if (t == 13) + return "NAMESPACE"; + if (t == 14) + return "NOTATION_DECLARATION"; + if (t == 15) + return "ENTITY_DECLARATION"; + return "UNKNOWN"; + } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/annotation/Xml.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/annotation/Xml.java b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/Xml.java index c9e7f50..e901f58 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/annotation/Xml.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/Xml.java @@ -17,8 +17,6 @@ import static java.lang.annotation.RetentionPolicy.*; import java.lang.annotation.*; -import org.apache.juneau.xml.*; - /** * Annotation for specifying various XML options for the XML and RDF/XML serializers. * <p> @@ -144,12 +142,5 @@ public @interface Xml { * <xt></object></xt> * </p> */ - XmlFormat format() default XmlFormat.NORMAL; - - /** - * Associates a content handler with a bean class or bean property. - * <p> - * Refer to {@link XmlContentHandler} for more information. - */ - Class<? extends XmlContentHandler<?>> contentHandler() default XmlContentHandler.NULL.class; + XmlFormat format() default XmlFormat.DEFAULT; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlFormat.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlFormat.java b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlFormat.java index e6fb499..98140b6 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlFormat.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlFormat.java @@ -18,21 +18,30 @@ package org.apache.juneau.xml.annotation; public enum XmlFormat { /** - * Normal formatting (default) + * Normal formatting (default). + * <p> + * On a bean class, implies {@link #ELEMENTS} meaning bean properties will be serialized as child elements by default. + * <p> + * On a bean property, implies {@link #ELEMENT} meaning the bean property will be serialized as a child element. */ - NORMAL, + DEFAULT, /** - * Render property as an attribute instead of an element. + * Render a bean property as an attribute instead of an element. * <p> - * Can only be applied to properties (methods/fields) of class types that can be convertible to <code>Strings</code>. + * Only applicable for bean properties, not bean classes. + * <p> + * Can only be applied to properties (methods/fields) of class types that can be convertible to <code>Strings</code>. */ ATTR, /** * Render property as attributes instead of an element. * <p> - * Can only be applied to properties (methods/fields) of class type <code>Map<Object,Object></code> where both + * On a bean class, implies bean properties will be serialized as attributes instead of child elements by default. + * <p> + * On bean properties, implies that the bean property value itself should be serialized as attributes on the bean element. + * The bean property data type must be of class type <code>Map<Object,Object></code> where both * objects are convertible to <code>Strings</code>. */ ATTRS, @@ -40,29 +49,75 @@ public enum XmlFormat { /** * Render property as an element instead of an attribute. * <p> - * Can be applied to URL and ID bean properties that would normally be rendered as attributes. + * Only applicable for bean properties, not bean classes. + * <p> + * Used to override the behavior of the {@link #ATTRS} format applied to the bean class. */ ELEMENT, /** - * Prevents collections and arrays from being enclosed in <xt><array></xt> elements. + * Render property value directly as the contents of the element. + * <p> + * On a bean class, implies that bean properties will be serialized as child elements. + * Note that this is equivalent to {@link #DEFAULT}. + * <p> + * Only applicable for objects of type array/Collection. + * <p> + * On a bean property, implies that the bean property value itself should be serialized as child elements of the bean element. + */ + ELEMENTS, + + /** + * Same as {@link #ELEMENTS} except primitive types (e.g. string/boolean/number/null) are not wrapped in elements. + * <p> + * Only applicable for bean properties, not bean classes. * <p> - * Can only be applied to properties (methods/fields) of type collection or array, or collection classes. + * Only applicable for objects of type array/Collection. + * <p> + * Use of this format may cause data type loss during parsing if the types cannot be inferred through reflection. + */ + MIXED, + + /** + * Render property value as the text content of the element. + * <p> + * Similar to {@link #MIXED} but value must be a single value, not a collection. + * <p> + * Only applicable for bean properties, not bean classes. + * <p> + * Use of this format may cause data type loss during parsing if the type cannot be inferred through reflection. */ - COLLAPSED, + TEXT, /** - * Render property value directly as content of element. + * Same as {@link #TEXT} except the content is expected to be fully-formed XML that will + * get serialized as-is. * <p> - * By default, content is converted to plain text. + * During parsing, this XML text will be reserialized and set on the property. + * <p> + * Only applicable for bean properties, not bean classes. + * <p> + * Use of this format may cause data type loss during parsing if the type cannot be inferred through reflection. + */ + XMLTEXT, + + /** + * Prevents collections and arrays from being enclosed in <xt><array></xt> elements. * <p> - * Can be used in combination with {@link Xml#contentHandler()} to produce something other - * than plain text, such as embedded XML. + * Can only be applied to properties (methods/fields) of type collection or array, or collection classes. */ - CONTENT, + COLLAPSED; /** - * Render a collection/array as mixed child content of the element. + * Returns <jk>true</jk> if this format is one of those specified. + * + * @param formats The formats to match against. + * @return <jk>true</jk> if this format is one of those specified. */ - MIXED + public boolean isOneOf(XmlFormat...formats) { + for (XmlFormat format : formats) + if (format == this) + return true; + return false; + } } \ No newline at end of file
