Skip to content

Commit

Permalink
Fixed issue#2, addition of @JacksonXmlRootElement.
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Feb 5, 2011
1 parent 86296bf commit 06b9b24
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ public QName findWrapperElement(Annotated ann)
}
return null;
}

@Override
public QName findRootElement(Annotated ann)
{
JacksonXmlRootElement root = ann.getAnnotation(JacksonXmlRootElement.class);
if (root != null) {
return new QName(root.namespace(), root.localName());
}
return null;
}

/*
/**********************************************************************
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,19 @@ public interface XmlAnnotationIntrospector
public Boolean isOutputAsAttribute(Annotated ann);

/**
* Method to check if specified property has annotation that indicates
* Method used to check if specified property has annotation that indicates
* that it should be wrapped in an element; and if so, name to use.
* Note: local name of "" is used to indicate that name should default
* to using name (local name and namespace) of property itself.
*/
public QName findWrapperElement(Annotated ann);

/**
* Method used to find out name to use for the outermost (root) XML element
* name when serializing (since there is no property that would define it);
* this overrides default name based on type of object.
*/
public QName findRootElement(Annotated ann);

/*
/**********************************************************************
Expand Down Expand Up @@ -87,6 +94,16 @@ public QName findWrapperElement(Annotated ann)
return value;
}

@Override
public QName findRootElement(Annotated ann)
{
QName value = (_xmlPrimary == null) ? null : _xmlPrimary.findRootElement(ann);
if (value == null && _xmlSecondary != null) {
value = _xmlSecondary.findRootElement(ann);
}
return value;
}

@Override
public Boolean isOutputAsAttribute(Annotated ann)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
import org.codehaus.jackson.map.jsontype.impl.ClassNameIdResolver;
import org.codehaus.jackson.map.jsontype.impl.MinimalClassNameIdResolver;
import org.codehaus.jackson.map.jsontype.impl.StdTypeResolverBuilder;
import org.codehaus.jackson.map.jsontype.impl.TypeNameIdResolver;
import org.codehaus.jackson.type.JavaType;

/**
* Custom specialization of {@link StdTypeResolverBuilder}; needed so that
* type id property name can be modified as necessary to make it legal
* xml element or attribute name.
* XML element or attribute name.
*/
public class XmlTypeResolverBuilder extends StdTypeResolverBuilder
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
* for properties, above and beyond what
* {@link org.codehaus.jackson.annotate.JsonProperty} contains.
* It is an alternative to using JAXB annotations.
*
* @since 1.7
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.fasterxml.jackson.xml.annotate;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.codehaus.jackson.annotate.JacksonAnnotation;

/**
* Annotation that can be used to define name of root element used
* for the root-level object when serialized, which normally uses
* name of the type (class). It is similar to JAXB <code>XmlRootElement</code>.
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JacksonXmlRootElement
{
String namespace() default "";
String localName() default "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,17 +86,21 @@ public QName findWrapperElement(Annotated ann)
{
XmlElementWrapper w = findAnnotation(XmlElementWrapper.class, ann, false, false, false);
if (w != null) {
String ln = w.name();
String ns = w.namespace();
// if undefined, means "use property's name":
if (MARKER_FOR_DEFAULT.equals(ln)) {
ln = "";
}
return new QName(ns, ln);
return new QName(handleJaxbDefault(w.namespace()), handleJaxbDefault(w.name()));
}
return null;
}

@Override
public QName findRootElement(Annotated ann)
{
XmlRootElement root = findAnnotation(XmlRootElement.class, ann, false, false, false);
if (root != null) {
return new QName(handleJaxbDefault(root.namespace()), handleJaxbDefault(root.name()));
}
return null;
}

/*
/**********************************************************************
/* Helper methods
Expand All @@ -108,4 +112,10 @@ private XmlRootElement findRootElementAnnotation(AnnotatedClass ac)
// Yes, check package, no class (already included), yes superclasses
return findAnnotation(XmlRootElement.class, ac, true, false, true);
}

private String handleJaxbDefault(String value)
{
return MARKER_FOR_DEFAULT.equals(value) ? "" : value;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,33 @@ public QName findRootName(Class<?> rootType, MapperConfig<?> config)
BasicBeanDescription beanDesc = (BasicBeanDescription) config.introspectClassAnnotations(rootType);
AnnotationIntrospector intr = config.getAnnotationIntrospector();
AnnotatedClass ac = beanDesc.getClassInfo();
String localName = intr.findRootName(ac);
String localName = null;
String ns = null;

QName root = findRootElement(intr, ac);
if (root != null) {
localName = root.getLocalPart();
ns = root.getNamespaceURI();
}
if (localName == null || localName.length() == 0) {
localName = intr.findRootName(ac);
}

// No answer so far? Let's just default to using simple class name
if (localName == null) {
if (localName == null || localName.length() == 0) {
// Should we strip out enclosing class tho? For now, nope:
localName = rootType.getSimpleName();
name = new QName("", localName);
} else {
// Otherwise let's see if there's namespace, too
String ns = findNamespace(intr, ac);
if (ns == null) { // some QName impls barf on nulls...
ns = "";
// Otherwise let's see if there's namespace, too (if we are missing it)
if (ns == null || ns.length() == 0) {
ns = findNamespace(intr, ac);
}
name = new QName(ns, localName);
}
if (ns == null) { // some QName impls barf on nulls...
ns = "";
}
name = new QName(ns, localName);
_rootNames.put(key, name);
}
}
Expand All @@ -75,4 +88,17 @@ private String findNamespace(AnnotationIntrospector ai, AnnotatedClass ann)
}
return null;
}

private QName findRootElement(AnnotationIntrospector ai, AnnotatedClass ann)
{
for (AnnotationIntrospector intr : ai.allIntrospectors()) {
if (intr instanceof XmlAnnotationIntrospector) {
QName elem = ((XmlAnnotationIntrospector) intr).findRootElement(ann);
if (elem != null) {
return elem;
}
}
}
return null;
}
}
31 changes: 29 additions & 2 deletions src/test/java/com/fasterxml/jackson/xml/TestSerialization.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.fasterxml.jackson.xml.XmlMapper;
import com.fasterxml.jackson.xml.annotate.JacksonXmlElementWrapper;
import com.fasterxml.jackson.xml.annotate.JacksonXmlProperty;
import com.fasterxml.jackson.xml.annotate.JacksonXmlRootElement;

public class TestSerialization extends XmlTestBase
{
Expand Down Expand Up @@ -92,6 +93,18 @@ static class NsAttrBean
public String attr = "3";
}

@JacksonXmlRootElement(localName="root")
static class RootBean
{
public String value = "123";
}

@JacksonXmlRootElement(localName="nsRoot", namespace="http://foo")
static class NsRootBean
{
public String value = "abc";
}

/*
/**********************************************************
/* Set up
Expand All @@ -118,11 +131,25 @@ public void testRootName() throws IOException
{
String xml = _xmlMapper.writeValueAsString(new StringBean());

// Hmmh. Looks like JDK Stax adds bogus ns declaration. As such,
// Hmmh. Looks like JDK Stax may adds bogus ns declaration. As such,
// let's just check that name starts ok...
if (xml.indexOf("<StringBean") != 0) {
if (!xml.startsWith("<StringBean")) {
fail("Expected root name of 'StringBean'; but XML document is ["+xml+"]");
}

// and then see that basic non-namespace root is ok
xml = _xmlMapper.writeValueAsString(new RootBean());
assertEquals("<root><value>123</value></root>", xml);

// and namespace one too
xml = _xmlMapper.writeValueAsString(new NsRootBean());
if (xml.indexOf("nsRoot") < 0) { // verify localName
fail("Expected root name of 'nsRoot'; but XML document is ["+xml+"]");
}
// and NS declaration
if (xml.indexOf("http://foo") < 0) {
fail("Expected NS declaration for 'http://foo', not found, XML document is ["+xml+"]");
}
}

public void testSimpleAttribute() throws IOException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,4 @@ public void testSerializeAsAttr() throws Exception
assertEquals("<AttrBean><attr>3</attr></AttrBean>", _nonJaxbMapper.writeValueAsString(bean));
assertEquals("<AttrBean attr=\"3\"/>", _jaxbMapper.writeValueAsString(bean));
}

/*
/**********************************************************************
/* Helper methods
/**********************************************************************
*/
}

0 comments on commit 06b9b24

Please sign in to comment.