JAXB: How can I unmarshal XML without namespaces
An easier way might be to use unmarshalByDeclaredType, since you already know the type you want to unmarshal.
By using
Unmarshaller.unmarshal(rootNode, MyType.class);
you don't need to have a namespace declaration in the XML, since you pass in the JAXBElement that has the namespace already set.
This also perfectly legal, since you are not required to reference a namespace in an XML instance, see http://www.w3.org/TR/xmlschema-0/#PO - and many clients produce XML in that fashion.
Finally got it to work. Note that you have to remove any custom namespace in the schema; here's working sample code:
The schema:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="customer"> <xsd:complexType> <xsd:sequence minOccurs="1" maxOccurs="1"> <xsd:element name="name" type="xsd:string" minOccurs="1" maxOccurs="1" /> <xsd:element name="phone" type="xsd:string" minOccurs="1" maxOccurs="1" /> </xsd:sequence> </xsd:complexType></xsd:element>
XML:
<?xml version="1.0" encoding="UTF-8"?><customer> <name>Jane Doe</name> <phone>08154712</phone></customer>
JAXB code:
JAXBContext jc = JAXBContext.newInstance(Customer.class);Unmarshaller u = jc.createUnmarshaller();u.setSchema(schemaInputStream); // load your schema from File or any streamsourceCustomer = u.unmarshal(new StreamSource(inputStream), clazz); // pass in your XML as inputStream
UPDATE
You can get this to work by introducing an intermediate layer to translate between type
and xsi:type
. Below is example of using the StAX StreamReaderDelegate
to do this for the JAXB unmarshal operation:
package forum7184526;import java.io.FileInputStream;import javax.xml.bind.JAXBContext;import javax.xml.bind.Unmarshaller;import javax.xml.stream.XMLInputFactory;import javax.xml.stream.XMLStreamReader;import javax.xml.stream.util.StreamReaderDelegate;import org.eclipse.persistence.oxm.XMLConstants;public class Demo { public static void main(String[] args) throws Exception { XMLInputFactory xif = XMLInputFactory.newFactory(); XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("input.xml")); xsr = new XsiTypeReader(xsr); JAXBContext jc = JAXBContext.newInstance(Spec.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); Spec spec = (Spec) unmarshaller.unmarshal(xsr); } private static class XsiTypeReader extends StreamReaderDelegate { public XsiTypeReader(XMLStreamReader reader) { super(reader); } @Override public String getAttributeNamespace(int arg0) { if("type".equals(getAttributeLocalName(arg0))) { return XMLConstants.SCHEMA_INSTANCE_URL; } return super.getAttributeNamespace(arg0); } }}
xsi:type
is a schema mechanism for specifying the real type of an element (similar to a cast in Java). If you remove the namespace, you are changing the semantics of the document.
In EclipseLink JAXB (MOXy) we allow you to specify your own inheritance indicator for domain objects using @XmlDescriminatorNode
and @XmlDescrimatorValue
. We currently do not offer this type of customization for data type properties:
Based on Blaise's comments (thanks Blaise!) and my research.Here is the solution to my problem. Do you agree with that Blaise, or you have a better way?
package forum7184526;import java.io.FileInputStream;import javax.xml.bind.JAXBContext;import javax.xml.bind.Unmarshaller;import javax.xml.stream.XMLInputFactory;import javax.xml.stream.XMLStreamReader;import javax.xml.stream.util.StreamReaderDelegate;import org.eclipse.persistence.oxm.XMLConstants;public class Demo {public static void main(String[] args) throws Exception { XMLInputFactory xif = XMLInputFactory.newFactory(); XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("input.xml")); xsr = new XsiTypeReader(xsr); JAXBContext jc = JAXBContext.newInstance(Spec.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); Spec spec = (Spec) unmarshaller.unmarshal(xsr);}private static class XsiTypeReader extends StreamReaderDelegate { public XsiTypeReader(XMLStreamReader reader) { super(reader); } @Override public String getAttributeNamespace(int arg0) { if("type".equals(getAttributeLocalName(arg0))) { return "http://www.w3.org/2001/XMLSchema-instance"; } return super.getAttributeNamespace(arg0); } @Override public String getAttributeValue(int arg0) { String n = getAttributeLocalName(arg0); if("type".equals(n)) { String v = super.getAttributeValue(arg0); return "xs:"+ v; } return super.getAttributeValue(arg0); } @Override public NamespaceContext getNamespaceContext() { return new MyNamespaceContext(super.getNamespaceContext()); }}private static class MyNamespaceContext implements NamespaceContext { public NamespaceContext _context; public MyNamespaceContext(NamespaceContext c){ _context = c; } @Override public Iterator<?> getPrefixes(String namespaceURI) { return _context.getPrefixes(namespaceURI); } @Override public String getPrefix(String namespaceURI) { return _context.getPrefix(namespaceURI); } @Override public String getNamespaceURI(String prefix) { if("xs".equals(prefix)) { return "http://www.w3.org/2001/XMLSchema"; } return _context.getNamespaceURI(prefix); }}}