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!