How to make JAXB unmarshaller ignore prefixes?
I have the following XML:
<ns2:Person name="John" age="20" />
And I want to untie it with a JAXB Person object that was generated from XSD.
this is the code I'm running:
JAXBContext context = JAXBContext.newInstance(PersoEntity.class); Unmarshaller um = context.createUnmarshaller(); StringReader sr = new StringReader(xml); Person p = (Person)um.unmarshal(sr);
Surprisingly, I get the following exception:
javax.xml.bind.UnmarshalException - with linked exception: [org.xml.sax.SAXParseException: The prefix "ns2" for element "ns2:Person" is not bound.]
How do I solve it? Thanks
OBTAINING FRAGMENTS
The way you are currently retrieving an XML snippet causes namespace declarations to get lost. In your snippet, ns2
no longer a prefix, you just have the element name with a colon in it ( ns2:Person
). This will cause problems with parsers that support the namespace. The following article can help you get an XML snippet:
HANDLING CASE OF USE
Using the XML fragment that you have, you can create an XMLFilter
that removes the prefix from the XML element and then uses the JAXB UnmarshallerHandler
to do unmarshalling.
Demo
package forum11968399; import java.io.StringReader; import javax.xml.bind.*; import javax.xml.parsers.*; import org.xml.sax.*; import org.xml.sax.helpers.XMLFilterImpl; public class Demo { private static final String xml = "<ns2:Person name='John' age='20' />"; public static void main(String[] args) throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); XMLReader xmlReader = sp.getXMLReader(); XMLFilter xmlFilter = new MyXMLFilter(xmlReader); JAXBContext context = JAXBContext.newInstance(PersonEntity.class); Unmarshaller um = context.createUnmarshaller(); UnmarshallerHandler unmarshallerHandler = um.getUnmarshallerHandler(); xmlFilter.setContentHandler(unmarshallerHandler); StringReader sr = new StringReader(xml); xmlFilter.parse(new InputSource(sr)); PersonEntity p = (PersonEntity) unmarshallerHandler.getResult(); } private static class MyXMLFilter extends XMLFilterImpl { public MyXMLFilter(XMLReader xmlReader) { super(xmlReader); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { int colonIndex = qName.indexOf(':'); if(colonIndex >= 0) { qName = qName.substring(colonIndex + 1); } uri = XML_NAMESPACE; //to prevent unknown XML element exception, we have to specify the namespace here super.startElement(uri, localName, qName, attributes); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { int colonIndex = qName.indexOf(':'); if(colonIndex >= 0) { qName = qName.substring(colonIndex + 1); } super.endElement(uri, localName, qName); } } }
Personentity
package forum11968399; import javax.xml.bind.annotation.*; @XmlRootElement(name="Person") @XmlAccessorType(XmlAccessType.FIELD) public class PersonEntity { @XmlAttribute private String name; @XmlAttribute private int age; }
It is best, perhaps, to nest the element you want in another element that binds the namespace. It doesn't really matter what you are binding to, just make it a valid XML document that will parse. Then you can unmarshal declared type
JAXBContext context = JAXBContext.newInstance(Person.class); Unmarshaller um = context.createUnmarshaller(); String xml = "<ns2:Person name=\"John\" age=\"20\" />"; String xmlWithPrefixMapped = "<ns2:FakeElement xmlns:ns2=\"someuri\">" + xml + "</ns2:FakeElement>"; StringReader sr = new StringReader(xmlWithPrefixMapped); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(new InputSource(sr)); Node n = (Node) doc.getDocumentElement().getFirstChild(); JAXBElement<Person> personElement = um.unmarshal(n, Person.class); Person p = personElement.getValue();