August 11, 2010

JAXB & Namespaces

The majority of XML documents are namespace qualified, yet most XML binding examples do not demonstrate namespaces (sometimes because the XML binding solution does not support them). In this post I'll demonstrate how easy namespace handling is using JAXB.


We'll start with a Customer class that has no namespace information specified:

package example;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    private long id;
    private String name;

    @XmlAttribute
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

The following code will be used to run this example:

package example;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = 
           JAXBContext.newInstance(Customer.class);

        Customer customer = new Customer();
        customer.setId(123);
        customer.setName("Jane Doe");

        Marshaller m = jc.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        m.marshal(customer, System.out);
    }
}

Running the example at this point will produce the following XML:

<customer id="123">
    <name>Jane Doe</name>
</customer>

Package Level Metadata

We can easily qualify all the elements through the use of the package level annotation @XmlSchema.  To specify a package level annotation, create a class called package-info in the desired package (see complete source below for an example).  Below we will set a default namespace and specify that by default all elements are namespace qualified.

@XmlSchema( 
    namespace = "http://www.example.org/package", 
    elementFormDefault = XmlNsForm.QUALIFIED) 
package example;

import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;

This will produce the following XML:

<customer 
    xmlns="http://www.example.org/package" 
    id="123">
    <name>Jane Doe</name>
</customer>

Type Level Metadata

You can override the package level namespace information at the type level using the @XmlType annotation.  As per the @XmlSchema annotation elements are still namespace qualified by default, only now the elements inside the Customer type will be qualified with the namespace http://www.example.org/type.

package example;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement
@XmlType(namespace="http://www.example.org/type")
public class Customer {

    private long id;
    private String name;

    @XmlAttribute
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

This will produce the following output:

<ns2:customer 
    xmlns="http://www.example.org/type" 
    xmlns:ns2="http://www.example.org/package" 
    id="123">
    <name>Jane Doe</name>
</ns2:customer>

Field/Property Level Metadata

You can also control namespaces at the property/field level.  All of the annotations related to attributes and elements have a namespace parameter.

package example;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement
@XmlType(namespace="http://www.example.org/type")
public class Customer {

    private long id;
    private String name;

    @XmlAttribute
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    @XmlElement(namespace="http://www.example.org/property")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

This will produce the following output:

<ns2:customer 
    xmlns="http://www.example.org/property" 
    xmlns:ns2="http://www.example.org/package"
    id="123">
    <name>Jane Doe</name>
</ns2:customer>

Namespace handling in JAXB is easy.  For most documents the package level annotation is all you need.  Then you can use the other levels to customize your document.

38 comments:

  1. Thank you, very good explanation.

    ReplyDelete
  2. This information is very thorough and helpful. However, it seems to be ineffective when preparing a Marshaller object on a Jersey (JSR-311) ContextResolver instance.

    Can you offer any thoughts?

    ReplyDelete
  3. You need to be sure that you have configured your JAXBContext to use the MOXy implementation. Refer to:
    http://bdoughan.blogspot.com/2010/08/creating-restful-web-service-part-35.html

    ReplyDelete
  4. Excellent documentation, very clear and thorough. However, in JAXWS, no matter if I put it in package level, or type level, or field level, it only generates a namespace with prefix, not a default namespace like xmlns="..." above. Without a default namespace, I have to specify a namespace for each every element, which is not acceptable. Any idea why the annotation doesn't generate default namespace like above?

    ReplyDelete
  5. Hi Fee,

    If at the package level you use @XmlSchema annotation that specifies a namespace and elementFormDefault=qualified, then JAXB should namespace qualify all elements. JAXB can do this with a default namespace or prefix qualified elements. This choice will not impact the metadata you need to supply.

    -Blaise

    ReplyDelete
  6. Hi, i have been looking @ org.eclipse.persistence.oxm.NamespaceResolver and trying to see if there is a way to supply something similar to a com.sun.xml.bind.marshaller.NamespacePrefixMapper so as to specify prefixes for the namespaces other than ns0, ns1 etc.

    Not strictly needed by many tools, but some of our integration tooling gets rather confused when we keep changing the namespace prefixes (not sure why...)

    thanks
    David

    ReplyDelete
  7. Hi David,

    EclipseLink MOXy will use the namespace prefixes as specified on the @XmlSchema annotation.

    -Blaise

    ReplyDelete
  8. Hello Blaise,
    That appears to work, but it appears to be not intelligent enough to figure out that the namespace prefix are the same, if I have model in two different packages but with the same prefix. Any other ideas on how to "coalsce" the preifx mapping, by default JAXB2 impl appears to be smart enought to do this.

    ReplyDelete
  9. Thanks for the very clear guide!!

    ReplyDelete
  10. Hi i want to use TypeLevel(class) Meta Data without prefix like

    Jane Doe

    becuase some java files in my package uses different namespaces...(example for Request message we are using some namespace and Response message we using different namespace)

    ReplyDelete
  11. Hi Vels,

    I believe your comment may have included an XML fragment that has been lost. Could you send more details through my "Contact Me" page?

    -Blaise

    ReplyDelete
  12. Hello Blaise,

    I am having trouble getting EclipseLink MOXy to work at the moment. I have configured namespace prefixes in package-info.java using @XmlSchema annotation. I have added eclipselink.jar to the classpath and both package-info.java and jaxb.properties files are in the same package as the model classes. However, when I run the program, I get the following error message:

    javax.xml.bind.JAXBException: Provider org.eclipse.persistence.jaxb.JAXBContextFactory not found

    Any idea what could be the problem?

    Many thanks

    Khutsi

    ReplyDelete
  13. The issue Khutsi was seeing has been resolved. Below is the link to a Stack Overflow thread with the resolution:

    - http://stackoverflow.com/questions/6817906/cannot-get-eclipselink-moxy-to-work

    -Blaise

    ReplyDelete
  14. hi~

    I've some question.

    Itpred class in package a,
    Header class in package a,
    MessageSender class in package cc,
    Identifier class in package a.

    only cc package has namespace.
    I apply package level annotation.


    @XmlSchema(
    xmlns = {@XmlNs(prefix = "cc", namespaceURI = "http://www.somesite.com/schema/CC")},
    elementFormDefault = XmlNsForm.QUALIFIED
    ) package cc;

    import javax.xml.bind.annotation.XmlNs;
    import javax.xml.bind.annotation.XmlNsForm;
    import javax.xml.bind.annotation.XmlSchema;


    but result print not include namespace prefix and namespace.





    moonumi





    I will add class use cc namespace class to cc package.

    Why MessageSender does not print ?


    but




    @XmlSchema(
    namespace = "http://www.somesite.com/schema/CC",
    xmlns = {@XmlNs(prefix = "cc", namespaceURI = "http://www.somesite.com/schema/CC")},
    elementFormDefault = XmlNsForm.QUALIFIED
    ) package cc;

    import javax.xml.bind.annotation.XmlNs;
    import javax.xml.bind.annotation.XmlNsForm;
    import javax.xml.bind.annotation.XmlSchema;


    result





    moonumi




    how to specify namespace prefix cc package.

    ReplyDelete
  15. Hello Moonumi,

    Would you mind providing me more details about your use case through my contact page:
    - http://blog.bdoughan.com/p/contact_01.html

    I believe some of the details did not survive the formatting of the comments area.

    -Blaise

    ReplyDelete
  16. @XmlSchema(
    namespace="http://www.example.org/package",
    elementFormDefault = XmlNsForm.QUALIFIED)
    package cdac.medinfo.serialize.poc;

    import java.util.Collection;
    import java.util.HashSet;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBException;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.annotation.*;

    import java.io.StringReader;
    import java.io.StringWriter;

    //@XmlSeeAlso({Employee.class})

    @XmlRootElement(name = "ths")
    public class TestHashSet {


    Collection collection = new HashSet();

    public Collection getCollection() {
    return collection;
    }

    public void setCollection(Collection collection) {
    this.collection = collection;
    }

    public static void main(String args[]) throws JAXBException{

    TestHashSet ths = new TestHashSet();

    /*Employee e = new Employee();
    e.setFirstName("Ajay");
    e.setId(10);
    e.setLastName("Bhojak");
    e.setMiddleName("Kumar");*/

    //String e = "Ajay Bhojak";
    subClass sub=new subClass();
    sub.setX(32323);
    sub.setZ(323233);

    //Collection newCollection = new HashSet();

    Collection c=new HashSet();
    c.add(sub);

    subClass sub1=new subClass();
    sub1.setX(787);
    sub1.setZ(5512);
    c.add(sub1);

    ths.setCollection(c);
    Class arr[]=new Class[]{TestHashSet.class,subClass.class};
    final JAXBContext jaxbContext = JAXBContext.newInstance(arr);
    StringWriter writer = new StringWriter();
    Marshaller ms = jaxbContext.createMarshaller();
    ms.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "xyz.xsd");
    ms.marshal(ths, writer);

    System.out.println(writer.toString());

    final TestHashSet ths1 =
    (TestHashSet) jaxbContext.createUnmarshaller().unmarshal(
    new StringReader(writer.toString()));

    System.out.println("Hello");
    }
    }


    Blaise I have a problem while applying package level annotation, as when I apply it give me a compile time error saying
    "Package annotations must be in file package-info.java".
    please tell me how do i resolve this problem and I am using eclipse galileo 3.5.

    Thank you.
    Ajay Bhojak.

    ReplyDelete
  17. Hey I figured out the solution for my problem.

    Thank you.
    Ajay Bhojak

    ReplyDelete
  18. Hi Ajay,

    I'm happy to hear that things are working now. I've updated my post in an attempt to make it more clear for others.

    -Blaise

    ReplyDelete
  19. Thanks Blaise.
    I would surely come to you for any issues if I would not get them solved :-).
    Thanks again.

    I have some issues regarding unmarshalling on web service side when I do not apply package-info annotation on some specific packages and gives unmarshalling exceptions of local elemen is "some element"
    and expected element is "some element".

    why does this happen ?

    and also when applied NAMESPACE QUALIFIED in package-info it gives the same exception while it works if NAMESPACE UNQUALIFIED annotation is applied.

    Thanks.
    Ajay Bhojak

    ReplyDelete
  20. Hi Ajay,

    If you specify elementFormDefault = XmlNsForm.QUALIFIED then your fields/properties will inherit the namespace qualification from the @XmlSchema or @XmlType annotations. For your use case it appears as though your XML input does not expect all the elements to be namespace qualified.

    -Blaise

    ReplyDelete
  21. Hi Blaise,

    The post is really helpful. Thanks for sharing with us.

    We have a typical problem if you could help us. Here is the link http://stackoverflow.com/q/7445996/517483

    ReplyDelete
  22. If you use EclipseLink JAXB (MOXy) you won't see the extra namespace declaration on the XML corresponding to the DOM Element. I'm not sure why the JAXB reference implementation adds that declaration, but it shouldn't cause you any issues.

    -Blaise

    ReplyDelete
  23. Really useful tutorial, I was going crazy trying to use namespaces with JAXB. Thank you!

    ReplyDelete
  24. Hi Blaise, nice post ;)
    One question, though: I've got a project where JAXB auto generated 2 separate packages for me, those 2 being foo and foo.bar for example. These 2 packages have their own package-info class and each declares a separate namespace using @XmlSchema. In my project a class B from package foo.bar is associated with class A from package foo which is annotated with @XmlRootElement. This is causing the ns2 prefix from foo.bar namespace to be shown in my XML when marshalling class A. The question is: how can I get XML by marshalling the root element, but without the ns2 prefix?

    Thanks!

    ReplyDelete
  25. Hi Jonathan,

    JAXB implementations do there best guess at declaring the namespaces that will be required. Sometimes implementations err on the side of declaring one that might not be needed to avoid the situation of declaring it when it is encountered which could cause it to be declared many times on sibling nodes.

    That being said, you can use JAXB with StAX to control the namespaces. Below is a link to an answer I gave on Stack Overflow that used JAXB with StAX to declare namespaces as they were encountered as the default namespace. A similar strategy may help you:
    - http://stackoverflow.com/questions/5720501/jaxb-marshalling-xmpp-stanzas/5722013#5722013

    -Blaise

    ReplyDelete
  26. Hi Blaise,

    Not sure this is still active or not. I am having an issue while unmarshalling in jaxb. I am getting an xml in which no name space is present. So when I am unmarshalling it again the XSD that I have, its failing. Is there a way to add a default xmlns before unmarshalling the XML?

    Regards
    Arnab

    ReplyDelete
    Replies
    1. Hi Arnab,

      Instead of trying to answer your question in the comment section, I have written a complete example that you can find at the following link:
      - Applying a Namespace During JAXB Unmarshal

      -Blaise

      Delete
  27. Hi Blaise,

    How can I get namespace prefixed at the attribute level also ?
    e.g. ns:id

    Thanks
    Sudhir

    ReplyDelete
    Replies
    1. Hi Sudhir,

      I apologize for the delay in responding. The process for XML attributes is very similar to that for elements. There is an attributeFormDefault setting on the @XmlSchema annotation and you can set a namespace on the @XmlAttribute annotation.

      -Blaise

      Delete
  28. I'm having headaches...
    Cannot get JAXB to produce the prefix for the namespace.
    I have specified @XmlSchema with @XmlNs mapping and set elementFormDefault to QUALIFIED.
    Running on Mac, JDK 1.6.0_37

    ReplyDelete
  29. Hi Blaise,

    In JAXB, during un-marshalling I am facing issue, not able to un-mashall properly (response in xml is coming as expected).
    I tried to give namespace at type level which were not present in response classes.

    But still it is failing.
    So what could be issue??

    ReplyDelete
    Replies
    1. Hi Sachin,

      If you specify the namespace at the type level, then you still need the @XmlSchema annotation at the package level specifying elementFormDefault = XmlNsForm.QUALIFIED. Otherwise you need to specify the namespaces on each of the @XmlElement annotations.

      -Blaise

      Delete
  30. Fantastic one on namespace...too good..

    ReplyDelete
  31. Hi Blaise,

    If i post an xml to rest easy webservice with attributes i get the below error .Same thing works without the attribute
    How to reolve this? Thanks in advance 1!!


    Exception

    [org.xml.sax.SAXParseException: The end-tag for element type "vcon" must end with a '>' delimiter

    here vcon refers to a node with an attribute

    [org.xml.sax.SAXParseException: The end-tag for element type "option " must end with a '>' delimiter.]type Status reportmessage javax.xml.bind.UnmarshalException
    – with linked exception:

    ReplyDelete
  32. Thank for the article. I wanted to get up and running with JAXB and this exactly answered my questions about namespaces.

    ReplyDelete
  33. Written 2010, stays to be a good article in 2013. Thanx.

    ReplyDelete

Note: Only a member of this blog may post a comment.