jaxb marshall Boolean as Integer (0,1) problems - jaxb

My problem seems pretty simple, yet I was unable to find anything exactly like it on stackoverflow.
I'm using jaxb to marshall/unmarshall this object:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "")
#XmlRootElement(name = "root")
public class MyJaxbObject implements Serializable
{
#XmlElement(name = "DELETE")
#XmlJavaTypeAdapter(BooleanIntAdapter.class)
private Boolean delete;
#XmlElement(name = "message")
private String message;
constructors.. getters... setters...
}
My BooleanAdapter is a simple XmlAdapter<Integer, Boolean> that turns true/false to 1/0 and back.
Unmarshalling works, but marshalling does not. it always yeilds this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<DELETE xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.or/2001/XMLSchema" xsi:type="xs:boolean">true</DELETE>
<message>***DONE***</message>
</root>
When I change the xml element config to #XmlElement(name = "DELETE",type = Boolean.class) unmarshalling of the boolean fails and marshalling yields this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<DELETE xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:int">1</DELETE>
<message>***DONE***</message>
</root>
I put simple print messages in the marshal/unmarshal methods of the xml adapter and tried to marshal/unmarshal this kind of object.
I saw that without type declaration unmarshal method is called but marshal is not.
With the type declaration only marshal is called.
Help??
How can I marshal/unmarshal my Boolean to/from {1,0} and possibly get rid of the xsi:type in the marshalled xml?
Edit - this is the code i used to test marshal/unmarshal:
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
JAXBContext context = javax.xml.bind.JAXBContext.newInstance("my.package.classes");
Unmarshaller unmarshal = context.createUnmarshaller();
Marshaller marshal = context.createMarshaller();
marshal.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
String str = "<root><DELETE>1</DELETE><message>***DONE***</message></root>";
DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
Document d = null;
d = builder.parse(new InputSource(new StringReader(str)));
MyJaxbObject myJaxUnmarsh = unmarshal.unmarshal(d.getFirstChild(), MyJaxbObject.class).getValue();
System.out.println(myJaxUnmarsh.getMessage() + " , " + myJaxUnmarsh.getDelete());
MyJaxbObject myJax = new MyJaxbObject();
myJax.setDelete(true);
myJax.setMessage("***DONE***");
marshal.marshal(myJax, System.out);

With the following implementation of BooleanIntAdapter:
BooleanIntAdapter
package forum9380680;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class BooleanIntAdapter extends XmlAdapter<Integer, Boolean> {
#Override
public Boolean unmarshal(Integer v) throws Exception {
return v.equals(1);
}
#Override
public Integer marshal(Boolean v) throws Exception {
if(v) {
return 1;
} else {
return 0;
}
}
}
Output
I get the following output:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<DELETE>1</DELETE>
<message>***DONE***</message>
</root>
Below is the rest of the code I used:
Demo
package forum9380680;
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(MyJaxbObject.class);
MyJaxbObject object = new MyJaxbObject();
object.setDelete(true);
object.setMessage("***DONE***");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(object, System.out);
}
}
MyJaxbObject
package forum9380680;
import java.io.Serializable;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "")
#XmlRootElement(name = "root")
public class MyJaxbObject implements Serializable
{
#XmlElement(name = "DELETE")
#XmlJavaTypeAdapter(BooleanIntAdapter.class)
private Boolean delete;
#XmlElement(name = "message")
private String message;
public Boolean getDelete() {
return delete;
}
public void setDelete(Boolean delete) {
this.delete = delete;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

found the problem:
CXF pom makes the project use jaxb-impl version 2.1.0.
Without CXF the project uses the JDK default which was 2.1.10 in my case.
see:
http://jaxb.java.net/guide/Which_JAXB_RI_is_included_in_which_JDK_.html
I added the following under dependency management to fix the problem
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.5</version>
<type>jar</type>
<scope>compile</scope>
</dependency>

Related

If a class has #XmlElement property, it cannot have #XmlValue property error

I am getting below exception when I am trying to marshel below java classes to below expected xml.
Exception:
If a class has #XmlElement property, it cannot have #XmlValue property.
XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<parent>
I am the parent element of mixedtype.
<child>I am the child element</child>
</parent>
Parent.java
#XmlRootElement(name = "parent")
public class Parent{
protected List<Child> child= new ArrayList<Child>();
protected List<String> text= new ArrayList<String>();
#XmlElementRef(name="child",type=Child.class)
public List<Child> getChild() {
return child;
}
#XmlMixed
public List<String> getText() {
return text;
}
public void setChild(Child value) {
this.child.add(value);
}
public void setText(String value) {
this.text.add(value);
}
}
Child.java
public class Child {
#XmlValue
protected String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
For the above the XML I tried this and seems to work fine for me:
XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<parent>
I am the parent element of mixedtype.
<child>I am the child element</child>
</parent>
Parent.class:
#XmlRootElement(name = "parent")
#Data
#NoArgsConstructor
#XmlAccessorType(XmlAccessType.FIELD)
public class Parent {
#XmlMixed
#XmlAnyElement
private List<Object> textContent;
#XmlElement(name = "child")
private String child;
}
My Main class:
public class JaxbExampleMain {
public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("parent.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Parent.class).createUnmarshaller();
final Parent parent = unmarshaller.unmarshal(xmlStreamReader, Parent.class).getValue();
System.out.println(parent.toString());
Marshaller marshaller = JAXBContext.newInstance(Parent.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(parent, System.out);
}
}
Following is the output that I am getting:
Parent(textContent=[
I am the parent element of mixedtype.
,
], child=I am the child element)
<parent>
I am the parent element of mixedtype.
<child>I am the child element</child>
</parent>

JaxB representation for Object to support different types

Following java property can be of either String/Integer type. I tried adding annotation as below, but it would only take the last type(string in this case), even when an integer is passed. How can we allow multiple types for this property?
#XmlElements({
#XmlElement(type = Integer.class), #XmlElement(type = String.class)
})
private Object val1;
The problem is that there is no name to differentiate between the different type of classes when unmarshaling. I don't know what exactly are your requirements but this is how you could make it work:
#XmlRootElement(name = "root")
#XmlAccessorType(XmlAccessType.FIELD)
public class SomeRoot {
#XmlElements({
#XmlElement(name="string", type = String .class),
#XmlElement(name="int", type = Integer.class)
})
private Object val;
public SomeRoot(Object val) {
this.val = val;
}
public SomeRoot() { }
}
When you have a String value provided it would be marshaled into something like that:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<string>test</string>
</root>
And when given an Integer it would marshal into something like that:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<int>1</int>
</root>
Both of these would be unmarshaled to their proper type because of the difference in the element name.
Also if you would have a list of Objects you could marshal/unmarshal something like this with no problem:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<int>1</int>
<string>test1</string>
<string>test2</string>
<string>test3</string>
<int>3</int>
</root>
Update:
If you want both types under the <val> element, you could use a custom XmlAdapter to check on the type. This could look like this:
public class MyXmlAdapter extends XmlAdapter<Object, Object> {
private DocumentBuilder documentBuilder;
public MyXmlAdapter() {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
documentBuilder = dbf.newDocumentBuilder();
} catch(Exception e) {
// TODO - Handle Exception
}
}
#Override
public Object unmarshal(Object v) throws Exception {
Element element = (Element) v;
String elementValue = element.getTextContent();
if(StringUtils.isNumeric(elementValue)) {
return Integer.parseInt(elementValue);
} else {
return new String(elementValue);
}
}
#Override
public Object marshal(Object v) throws Exception {
return v;
}
}
And your root class would be something like this:
#XmlRootElement(name = "root")
#XmlAccessorType(XmlAccessType.FIELD)
public class SomeRoot {
#XmlJavaTypeAdapter(MyXmlAdapter.class)
private Object val;
public SomeRoot(Object val) {
this.val = val;
}
public SomeRoot() { }
public Object getVal() {
return val;
}
}
Trying this out in a simple main, resolves the provided xml to the right type for both String and Integer on val:
public static void main(String[] args) {
try {
File file = new File("file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(SomeRoot.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setAdapter(new MyXmlAdapter());
SomeRoot pojo = (SomeRoot) jaxbUnmarshaller.unmarshal(file);
System.out.println(pojo.getVal().getClass());
} catch (JAXBException e) {
e.printStackTrace();
}
}

xs:int unmarshalled to null for decimal values

I've got a problem related to the unmarshalling process in a JAX-WS based WebService. In WSDL file there is an element defined as
<element name="quantity" nillable="true" type="int" />
In the related JAVA class it is defined as:
#XmlElement(name = "Quantity", required = true, type = Integer.class, nillable = true)
protected Integer quantity;
When an XML value for this element is the representation of a decimal number (3.4), the element is unmarshalled as a null Integer. No SOAPFault is generated and it's impossible to distinguish decimal values from null values inside the WebService.
Could it be a defect in JAXB implementation or I'm doing something wrong?
Could it be a defect in JAXB implementation or I'm doing something
wrong?
This is not a defect in the JAXB (JSR-222) implementation. It is a result of how the JAX-WS is configured to use JAXB. I will demonstrate below with an example.
Root
Below is a domain object with a field that matches the one from your question. I have remove the type=Integer.class from the #XmlElement annotation since it is redundant.
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElement(name = "Quantity", required = true, nillable = true)
protected Integer quantity;
}
Demo
JAXB offers the ability to set a ValidationEventHandler on the Unmarshaller to give you some control over how unmarshal errors are handled.
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return true;
}
});
StringReader xml = new StringReader("<root><Quantity>3.4</Quantity></root>");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
In the expert group we decided that invalid element data is common and that JAXB should not fail out every time this is encountered, but you can see that a ValidationEvent is raised.
Not a number: 3.4
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<Quantity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</root>
Update Demo
If we update the ValidationEventHandler to indicate that we do not wish to continue the unmarshal when a ValidationEvent is raised we can make the following change.
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return false;
}
Updated Output
And now the following output occurs.
Not a number: 3.4
Exception in thread "main" javax.xml.bind.UnmarshalException: Not a number: 3.4
- with linked exception:
[java.lang.NumberFormatException: Not a number: 3.4]
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:647)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleError(UnmarshallingContext.java:676)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleError(UnmarshallingContext.java:672)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.handleParseConversionException(Loader.java:256)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyLoader.text(LeafPropertyLoader.java:54)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.text(UnmarshallingContext.java:499)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.processText(SAXConnector.java:166)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.endElement(SAXConnector.java:139)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:606)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1742)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2900)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:607)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:116)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:489)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:835)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:123)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1210)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:568)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:203)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:175)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:157)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:214)
at forum14741140.Demo.main(Demo.java:22)
Caused by: java.lang.NumberFormatException: Not a number: 3.4
at com.sun.xml.internal.bind.DatatypeConverterImpl._parseInt(DatatypeConverterImpl.java:101)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$17.parse(RuntimeBuiltinLeafInfoImpl.java:713)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$17.parse(RuntimeBuiltinLeafInfoImpl.java:711)
at com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.parse(TransducedAccessor.java:232)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyLoader.text(LeafPropertyLoader.java:50)
... 19 more
I answered this question in : https://stackoverflow.com/a/30617814/3632201
I have been struggling with this issue during the last week and finally i have managed a working solution. The trick is that JAXB looks for the methods beforeUnmarshal and afterUnmarshal in the object annotated with #XmlRootElement.
..
#XmlRootElement(name="MSEPObtenerPolizaFechaDTO")
#XmlAccessorType(XmlAccessType.FIELD)
public class MSEPObtenerPolizaFechaDTO implements Serializable {
..
public void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException, IOException, SAXException {
unmarshaller.setSchema(Utils.getSchemaFromContext(this.getClass()));
unmarshaller.setEventHandler(new CustomEventHandler());
}
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException {
unmarshaller.setSchema(null);
unmarshaller.setEventHandler(null);
}
Using this ValidationEventHandler:
public class CustomEventHandler implements ValidationEventHandler{
#Override
public boolean handleEvent(ValidationEvent event) {
if (event.getSeverity() == event.ERROR ||
event.getSeverity() == event.FATAL_ERROR)
{
ValidationEventLocator locator = event.getLocator();
throw new RuntimeException(event.getMessage(), event.getLinkedException());
}
return true;
}
}
}
And this is the metodh getSchemaFromContext created in your Utility class:
#SuppressWarnings("unchecked")
public static Schema getSchemaFromContext(Class clazz) throws JAXBException, IOException, SAXException{
JAXBContext jc = JAXBContext.newInstance(clazz);
final List<ByteArrayOutputStream> outs = new ArrayList<ByteArrayOutputStream>();
jc.generateSchema(new SchemaOutputResolver(){
#Override
public Result createOutput(String namespaceUri,
String suggestedFileName) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
outs.add(out);
StreamResult streamResult = new StreamResult(out);
streamResult.setSystemId("");
return streamResult;
}
});
StreamSource[] sources = new StreamSource[outs.size()];
for (int i = 0; i < outs.size(); i++) {
ByteArrayOutputStream out = outs.get(i);
sources[i] = new StreamSource(new ByteArrayInputStream(out.toByteArray()), "");
}
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
return sf.newSchema(sources);
}

Use XmlIDRef for reference to abstract class

First of all a small example. The class ReferencingEntity holds a reference to the abstract class AbstractEntity. There are two implementations fo this class:
#XmlRootElement
public abstract class AbstractEntity {
#XmlID
private String id;
}
#XmlRootElement
public class EntityImpl1 extends AbstractEntity {
}
#XmlRootElement
public class EntityImpl2 extends AbstractEntity {
}
#XmlRootElement
public class ReferencingEntity {
#XmlIDREF
private AbstractEntity entity;
}
There is no problem marshalling an instance of ReferencingEntity (except that the concrete type is not present in xml), but when trying to unmarshal the xml representation, the descriptor is missing to determine the concrete implementation.
Currently I'm using an XmlAdapter to set all non-id fields null, but it would be better to use #XmlID if possible. Any ideas?
UPDATE:
I'm using RESTEasy in JBoss 6.1.0.Final and the provider creates the context as follows:
ContextResolver<JAXBContextFinder> resolver = providers.getContextResolver(JAXBContextFinder.class, mediaType);
JAXBContextFinder finder = resolver.getContext(type);
if (finder == null)
{
if (reader) throw new JAXBUnmarshalException("Could not find JAXBContextFinder for media type: " + mediaType);
else throw new JAXBMarshalException("Could not find JAXBContextFinder for media type: " + mediaType);
}
JAXBContext context = finder.findCachedContext(type, mediaType, annotations);
Below is my initial answer to your question. I imagine it will evolve as I better understand your use case.
ABOUT #XmlID/#XmlIDREF
Every instance referenced from a field/property annotated with #XmlIDREF also needs to be referenced via containment. I'll use the class below in this example.
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Root {
private AbstractEntity abstractEntity;
private ReferencingEntity referencingEntity;
public AbstractEntity getAbstractEntity() {
return abstractEntity;
}
public void setAbstractEntity(AbstractEntity abstractEntity) {
this.abstractEntity = abstractEntity;
}
public ReferencingEntity getReferencingEntity() {
return referencingEntity;
}
public void setReferencingEntity(ReferencingEntity referencingEntity) {
this.referencingEntity = referencingEntity;
}
}
REGARDING INHERITANCE
JAXB (JSR-222) implementations can't automatically discover subclasses, so you will need to be sure that the JAXBContext is aware of them. One way to accomplish this is to use the #XmlSeeAlso annotation on the parent class to point at the child classes.
import javax.xml.bind.annotation.*;
#XmlSeeAlso({EntityImpl1.class, EntityImpl2.class})
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class AbstractEntity {
#XmlID
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
DEMO CODE
Demo
package forum12111815;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum12111815/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
System.out.println(root.getAbstractEntity().getClass());
System.out.println(root.getAbstractEntity() == root.getReferencingEntity().getEntity());
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<abstractEntity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="entityImpl2">
<id>123</id>
</abstractEntity>
<referencingEntity>
<entity>123</entity>
</referencingEntity>
</root>
Output
class forum12111815.EntityImpl2
true
<?xml version="1.0" encoding="UTF-8"?>
<root>
<abstractEntity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="entityImpl2">
<id>123</id>
</abstractEntity>
<referencingEntity>
<entity>123</entity>
</referencingEntity>
</root>
FOR MORE INFORMATION
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html

Cannot unmarshall using #XmlAttribute

I can marshall my sitemap with JAXB, though I cannot unmarshall it! Any help would be much appreciated.
I get the following exception:
javax.xml.bind.UnmarshalException: unexpected element
(uri:"http://www.sitemaps.org/schemas/sitemap/0.9", local:"urlset").
Expected elements are <{}urls>,<{}urlset>
Sitemap urlset:
#XmlRootElement(name = "urlset")
public class XMLURLSet
{
List<XMLURL> urls;
final String xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
#XmlAttribute(name = "xmlns")
public String getXmlns() {
return xmlns;
}
public void setXmlns(String xmlns) {
// nop
}
#XmlElement(name = "url")
public List<XMLURL> getUrls(){
return urls;
}
public void setUrls(List<XMLURL> urls) {
this.urls = urls;
}
Sitemap urls:
#XmlRootElement(name = "urls")
public class XMLURL {
String loc;
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
Output XML (it's correct):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://www.example.com</loc>
</url>
</urlset>
JUnit marshalling code that works
JAXBContext context = JAXBContext.newInstance(XMLURLSet.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
StringWriter sw = new StringWriter();
marshaller.marshal(urlSet, sw);
String xml = sw.toString();
Unmarshalling that DOESNT WORK (cont' from above):
JAXBContext ctx = JAXBContext.newInstance(XMLURLSet.class);
Unmarshaller umsler = ctx.createUnmarshaller();
XMLURLSet xmlUrlSet = (XMLURLSet) umsler.unmarshal(new StreamSource(new StringReader(xml)));
The last line generates the exception:
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.sitemaps.org/schemas/sitemap/0.9", local:"urlset"). Expected elements are <{}urls>,<{}urlset>
The problem is with the way you are trying to namespace qualify your document. You will need to add a class called package-info in the same package as your domain classes with the following entry:
package-info
#XmlSchema(
namespace = "http://www.sitemaps.org/schemas/sitemap/0.9",
elementFormDefault = XmlNsForm.QUALIFIED)
package your.package.name;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html

Resources