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!
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