Jaxb: Unmarshalling xml with multiple namespaces in same package

annotations, java, jaxb, namespaces, xml

Solution

Below is an explanation of how namespaces work in JAXB (JSR-222) based on your model.

JAVA MODEL

package-info

Below is a modified version of your `@XmlSchema` annotation. It contains some key information:

- `namespace` - The default namespace that will be used to qualify global elements (those corresponding to `@XmlRootElement` and `@XmlElementDecl` annotations (and local elements based on the `elementFormDefault` value) that don't have another namespace specified.

- `elementFormDefault` by default only global elements are namespace qualified but by setting the value to be `XmlNsForm.QUALIFIED` all elements without an explicit namespace specified will be qualified with the `namespace` value.

- `xmlns` is the preferred set of prefixes that a JAXB impl should use for those namespaces (although they may use other prefixes).

@XmlSchema(
    namespace="http://mycompany/train",
    elementFormDefault = XmlNsForm.QUALIFIED,
    xmlns={
       @XmlNs(prefix="train", namespaceURI="http://mycompany/train"), 
       @XmlNs(prefix="passenger", namespaceURI="http://mycompany/passenger")
   }
)
package forum15772478;

import javax.xml.bind.annotation.*;

Train

Since all the elements corresponding to the `Train` class correspond to the `namespace` specified on the `@XmlSchema` annotation, we don't need to specify any namespace info.

- Global Elements - The `@XmlRootElement` annotation corresponds to a global element.

- Local Elements - The `@XmlElementWrapper` and `@XmlElement` annotations correspond to local elements.

package forum15772478;

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlRootElement(name="Train")
public class Train {

    private List<Passenger> passengers;

    @XmlElementWrapper(name="Passengers")
    @XmlElement(name="Passenger")
    public List<Passenger> getPassengers() {
        return passengers;
    }

    public void setPassengers(List<Passenger> passengers) {
        this.passengers = passengers;
    }

}

Passenger

If all the elements corresponding to properties on the `Passenger` class will be in the `http://mycompany/passenger` namespace, then you can use the `@XmlType` annotation to override the `namespace` from the `@XmlSchema` annotation.

package forum15772478;

import javax.xml.bind.annotation.*;

@XmlType(namespace="http://mycompany/passenger")
public class Passenger {

    private String ticketNumber;

    @XmlElement(name="TicketNumber")
    public String getTicketNumber() {
        return ticketNumber;
    }

    public void setTicketNumber(String ticketNumber) {
        this.ticketNumber = ticketNumber;
    }

}

Alternatively you can override the namespace at the property level.

package forum15772478;

import javax.xml.bind.annotation.*;

public class Passenger {

    private String ticketNumber;

    @XmlElement(
        namespace="http://mycompany/passenger",
        name="TicketNumber")
    public String getTicketNumber() {
        return ticketNumber;
    }

    public void setTicketNumber(String ticketNumber) {
        this.ticketNumber = ticketNumber;
    }

}

DEMO CODE

The following demo code can be run to prove that everything works:

Demo

package forum15772478;

import java.io.File;
import javax.xml.bind.*;

public class Demo {

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

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum15772478/input.xml");
        Train train = (Train) unmarshaller.unmarshal(xml);

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

}

input.xml/Output

In the XML below I have added the necessary namespace declarations that were missing from the XML document in your question.

<train:Train 
   xmlns:train="http://mycompany/train" 
   xmlns:passenger="http://mycompany/passenger">
   <train:Color>Red</train:Color>
   <train:Passengers>
       <train:Passenger>
           <passenger:TicketNumber>T101</passenger:TicketNumber>
       </train:Passenger>
   </train:Passengers>
</train:Train>

FOR MORE INFORMATION

- http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

Problem

I'm new to using namespaces in xml so I am kind of confused and would like some clarification. I have a java service where I am receiving xml documents with many different namespaces and while i got it working, I feel like I must have done something wrong so I want to check. In my package-info.java I have my schema annotation such as: ``` @javax.xml.bind.annotation.XmlSchema( xmlns={ @javax.xml.bind.annotation.XmHS(prefix="train", namespaceURI="http://mycompany/train"), @javax.xml.bind.annotation.XmHS(prefix="passenger", namespaceURI="http://mycompany/passenger") }, elementFormDefault = javax.xml.bind.annotation.XmlNsForm=QUALIFIED ) ``` I have a Train.java annotated on the class level with: ``` @XmlRootElement(name="Train", namespace="http://mycompany/train") ``` and each field in the class annotated with: ``` @XmlElement(name="Color") for example ``` Train contains a List of Passenger(s) so there's a property ``` private Set<Passenger> passengers; ``` and this collection is annotated with: ``` @XmlElementWrapper(name="Passengers") @XmlElements(@XmlElement(name="Passenger", namespace="http://mycompany/passenger")) ``` Then within Passenger.java the class itself is annotated with: ``` @XmlElement(name="Passenger", namespace="http://mycompany/passenger") ``` Finally for individual fields within Passenger.java, they are annotated like this: ``` @XmlElement(name="TicketNumber", namespace="http://mycompany/passenger") ``` So when I have an xml that looks like: ``` <train:Train> <train:Color>Red</train:Color> <train:Passengers> <train:Passenger> <passenger:TicketNumber>T101</passenger:TicketNumber> </train:Passenger> </train:Passengers> </train:Train> ``` Now I unmarshal this xml I received and Train's Color property is set and Passenger's TicketNumber property is set. But I don't know why I need to add the namespace url on the XmlElement annotation on TicketNumber for that to work but I didn't need to do so for the Color property on Train. If I remove the namespace attribute from the XmlElement annotation on TicketNumber, the value from the xml wont get mapped to the object unless I also remove the namespace prefix from the xml request. I feel like since I've got the namespace attribute defined on the XmlRootElement for Passenger, I shouldn't need to do that for every single field in the class as well just like I didn't have to for Train so I am assuming I must have setup something wrong. Can someone point me in the right direction? Thanks!

Original source