Jaxb ignore the namespace on unmarshalling - java

I use Jaxb2 and Spring. I am trying to unmarshal some XML that are sent by 2 of my customers.
Up to now, I only had to handle one customer which sent some xml like this :
<foo xmlns="com.acme">
<bar>[...]</bar>
<foo>
that is bound to a POJO like this :
#XmlType(name = "", propOrder = {"bar"})
#XmlRootElement(name = "Foo")
public class Foo {
#XmlElement(name = "Bar")
private String bar;
[...]
}
I discovered that the previous developer hardcoded the namespace in the unmarshaller in order to make it work.
Now, the second customer sends the same XML but changes the namespace!
<foo xmlns="com.xyz">
<bar>[...]</bar>
<foo>
Obviously, the unmarshaller fails to unmarshall because it expects some {com.acme}foo instead of {com.xyz}foo. Unforunately, asking the customer to change the XML is not an option.
What I tried :
1) In application-context.xml, I searched for a configuration which would allow me to ignore the namespace but could not find one :
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="packagesToScan">
<list>
<value>com.mycompany.mypkg</value>
</list>
</property>
<property name="marshallerProperties">
<map>
<entry key="???"><value type="java.lang.Boolean">false</value></entry>
</map>
</property>
</bean>
it seems that the only available options are the ones listed in the Jaxb2Marshaller's Javadoc :
/**
* Set the JAXB {#code Marshaller} properties. These properties will be set on the
* underlying JAXB {#code Marshaller}, and allow for features such as indentation.
* #param properties the properties
* #see javax.xml.bind.Marshaller#setProperty(String, Object)
* #see javax.xml.bind.Marshaller#JAXB_ENCODING
* #see javax.xml.bind.Marshaller#JAXB_FORMATTED_OUTPUT
* #see javax.xml.bind.Marshaller#JAXB_NO_NAMESPACE_SCHEMA_LOCATION
* #see javax.xml.bind.Marshaller#JAXB_SCHEMA_LOCATION
*/
public void setMarshallerProperties(Map<String, ?> properties) {
this.marshallerProperties = properties;
}
2) I also tried to configure the unmarshaller in the code :
try {
jc = JAXBContext.newInstance("com.mycompany.mypkg");
Unmarshaller u = jc.createUnmarshaller();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(false);//Tried this option.
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(xmlFile.toFile());
u.unmarshal(new DOMSource(doc));
return (Foo)u.unmarshal(new StreamSource(xmlFile.toFile()));
} catch (ParserConfigurationException | SAXException | IOException | JAXBException e) {
LOGGER.error("Erreur Unmarshalling CPL");
}
3) Different form with a SAXParser :
try {
jc = JAXBContext.newInstance("com.mycompany.mypkg");
Unmarshaller um = jc.createUnmarshaller();
final SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);
final XMLReader reader = sax.newSAXParser().getXMLReader();
final Source er = new SAXSource(reader, new InputSource(new FileReader(xmlFile.toFile())));
return (Foo)um.unmarshal(er);
}catch(...) {[...]}
This one works! But still, I would prefer to be able to autowire the Unmarshaller without needing this ugly conf everytime.

Namesapce awareness is feature of the document reader/builder/parser not marshallers. XML elements from different namespaces represents different entities == objects, so marshallers cannot ignore them.
You correctly switched off the namespaces in your SAX reader and as you said it worked. I don't understand your problem with it, your marshaller still can be injected, the difference is in obtaining the input data.
The same trick with document builder should also work (I will test it later on), I suspect that you were still using the marshaller with "hardcoded" namespace but your document was namespace free.
In my project I use XSLT to solve similar issue. Setting namespace awarness is definitely easier solution. But, with XSLT I could selectviely remove only some namespaces and also my my input xml are not always identical (ignoring namespaces) and sometimes I have to rename few elements so XSLT gives me this extra flexibility.
To remove namespaces you can use such xslt template:
<xsl:stylesheet version="1.0" xmlns:e="http://timet.dom.robust.ed" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="text() | processing-instruction() | comment()">
<xsl:copy />
</xsl:template>
</xsl:stylesheet>
Then in Java before unmarshalling I transform the input data:
Transformer transformer = TransformerFactory.newInstance().newTransformer(stylesource);
Source source = new DOMSource(xml);
DOMResult result = new DOMResult();
transformer.transform(source, result);

Thank you all, here shared my solution which works for my code , i try to make it generic every namespace contain ": " i write code if any tag have ":" it will remove from xml , This is used to skip namespace during unmarshalling using jaxb.
public class NamespaceFilter {
private NamespaceFilter() {
}
private static final String COLON = ":";
public static XMLReader nameSpaceFilter() throws SAXException {
XMLReader xr = new XMLFilterImpl(XMLReaderFactory.createXMLReader()) {
private boolean skipNamespace;
#Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
if (qName.indexOf(COLON) > -1) {
skipNamespace = true;
} else {
skipNamespace = false;
super.startElement("", localName, qName, atts);
}
}
#Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (qName.indexOf(COLON) > -1) {
skipNamespace = true;
} else {
skipNamespace = false;
super.endElement("", localName, qName);
}
}
#Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (!skipNamespace) {
super.characters(ch, start, length);
}
}
};
return xr;
}
}
for unmarshalling
XMLReader xr = NamespaceFilter.nameSpaceFilter();
Source src = new SAXSource(xr, new InputSource("C:\\Users\\binal\\Desktop\\response.xml"));
StringWriter sw = new StringWriter();
Result res = new StreamResult(sw);
TransformerFactory.newInstance().newTransformer().transform(src, res);
JAXBContext jc = JAXBContext.newInstance(Tab.class);
Unmarshaller u = jc.createUnmarshaller();
String done = sw.getBuffer().toString();
StringReader reader = new StringReader(done);
Tab tab = (Tab) u.unmarshal(reader);
System.out.println(tab);
`

I've followed Java docs to handle namespace in xml while unmarshalling, it did the trick.
JAXBContext jc = JAXBContext.newInstance( "com.acme.foo" );
Unmarshaller u = jc.createUnmarshaller();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(false);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File( "nosferatu.xml"));
//If just a string
//InputSource is = new InputSource(new StringReader(line));
//Document doc = db.parse(is)
Object o = u.unmarshal( doc );
Used Java API JAXB Context and Unmarshal features.
https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/Unmarshaller.html

Related

Java. XML. how to validate certain part of xml document against XSD Schema?

I have a SOAP response from a web-service in String and Document formats, and I have a method that validates it. The problem is that I have to validate Node <Result>.
I have already got this node, but do not know how to get child nodes with tags and etc.
Node result = (Node)xPath.compile("//Result").evaluate(xmlDocument, XPathConstants.NODE);
<Result>
<playerID>some id</playerID>
<partnerUID>some partner uid</partnerUID>
<registrationLevel>some registration level</registrationLevel>
<properties>
<property>
<key>some key</key>
<value>some value</value>
</property>
<property>...</property>
</Result>
Thanks for help
I validate XML file against XSD using this way.I think this will help you.
public String validateXMLSchema() throws SAXException, IOException
{
File folder = new File("xsdPath");
File[] listOfFiles = folder.listFiles();
String tempXsdFile;
for( int i=0; i < listOfFiles.length; i++ )
{
if(listOfFiles[i].isFile())
{
tempXsdFile = listOfFiles[i].getName();
try
{
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new File(xsdPath + tempXsdFile));
Validator validator = schema.newValidator();
validator.validate(new StreamSource(new File("C://Users//test.xml")));
return tempXsdFile;
}
catch (IOException | SAXException e)
{
System.out.println("ERROR: XML not well known"+ e.getMessage());
}
}
}
return null;
}

JAXB unmarshal with declared type does not populate the resulting object with data

I am trying to unmarshal a given XML:
<FHcomment>
<TX>rewriting of file</TX>
<tool_id>toolA</tool_id>
<tool_vendor>Company</tool_vendor>
<tool_version>1.7.36.0</tool_version>
</FHcomment>
The schema has already been compiled to JAXB classes, see here:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"tx",
"toolId",
"toolVendor",
"toolVersion",
"userName",
"commonProperties",
"extensions"
})
#XmlRootElement(name = "FHcomment")
public class FHcomment {
#XmlElement(name = "TX", required = true)
protected TX tx;
#XmlElement(name = "tool_id", required = true)
protected BaseName toolId;
#XmlElement(name = "tool_vendor", required = true)
protected BaseName toolVendor;
#XmlElement(name = "tool_version", required = true)
protected BaseVersion toolVersion;
#XmlElement(name = "user_name")
protected BaseName userName;
#XmlElement(name = "common_properties")
protected CommonPropertiesType commonProperties;
protected ExtensionsType extensions;
#XmlAnyAttribute
private Map<QName, String> otherAttributes = new HashMap<QName, String>();
.....
/*
* GETTERS and SETTERS for the fields have been removed here
*/
.....
}
My code to unmarshal the XML is as follows:
JAXBContext jc = JAXBContext.newInstance(FHcomment.class);
String s = "<FHcomment>....</Fhcomment>";
Unmarshaller unmarshaller = jc.createUnmarshaller();
XMLInputFactory fac = XMLInputFactory.newFactory();
XMLStreamReader xsr = fac.createXMLStreamReader(new StringReader(s));
JAXBElement<FHcomment> foo = unmarshaller.unmarshal(xsr, FHcomment.class);
FHcomment val = foo.getValue();
Problem: The resulting FHcomment object does not contain the children elements of FHcomment. All are null which is not the desired result.
How can I tell JAXB to completely unmarshal the given XML into an object?
EDIT: After adding a ValidationHandler to the Unmsarshaller, I got closer to the problem:
unexpected element (uri:"", local:"TX"). Expected elements are <{htp://www.example.com/mdf/v4}tool_id>,<{htp://www.example.com/mdf/v4}TX>,<{htp://www.www.example.com/mdf/v4}common_properties>,<{htp://www.example.com/mdf/v4}tool_version>,<{htp://www.example.com/mdf/v4}extensions>,<{htp://www.www.example.com/mdf/v4}tool_vendor>,<{htp://www.www.example.com/mdf/v4}user_name>
unexpected element (uri:"", local:"tool_id"). Expected elements are....
It turns out JAXB does not like the fact that the provided XML does not contain namespace information.. So how do I tell the unmarshaller to ignore the namespaces?
EDIT2:
After some more research I could not find a way to trick JAXB into working without namespace verification. I used the tutorial at http://cooljavablogs.blogspot.de/2008/08/how-to-instruct-jaxb-to-ignore.html to circumvent my problem. Not a nice solution but the best at hand...
Your XML document does not match the namespace qualification that was defined in your mappings (see: http://blog.bdoughan.com/2010/08/jaxb-namespaces.html). You could leverage an XMLFilter to apply a namespace to your XML document during the unmarshal operation.
import org.xml.sax.*;
import org.xml.sax.helpers.XMLFilterImpl;
public class NamespaceFilter extends XMLFilterImpl {
private static final String NAMESPACE = "htp://www.example.com/mdf/v4";
#Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
super.endElement(NAMESPACE, localName, qName);
}
#Override
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
super.startElement(NAMESPACE, localName, qName, atts);
}
}
Below is an example of how you would leverage the XMLFilter during an unmarshal.
// Create the XMLFilter
XMLFilter filter = new NamespaceFilter();
// Set the parent XMLReader on the XMLFilter
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
filter.setParent(xr);
// Set UnmarshallerHandler as ContentHandler on XMLFilter
Unmarshaller unmarshaller = jc.createUnmarshaller();
UnmarshallerHandler unmarshallerHandler = unmarshaller
.getUnmarshallerHandler();
filter.setContentHandler(unmarshallerHandler);
// Parse the XML
InputSource xml = new InputSource("input.xml");
filter.parse(xml);
Object result = unmarshallerHandler.getResult();
For More Information
http://blog.bdoughan.com/2012/11/applying-namespace-during-jaxb-unmarshal.html
Change BaseName and TX to String and it shall work. As it is the xml doesn't comply with your schema, which is represented by the class in this case.
Try to remove following code, and try again:
#XmlType(name = "", propOrder = {
"tx",
"toolId",
"toolVendor",
"toolVersion",
"userName",
"commonProperties",
"extensions"
})
How do you have annotated the classes BaseName , BaseVersion and TX?
If you have not annotated these classes, you will annotated the String inside of this classes as #XmlValue

How to skip well-formed for java DOM parser

I know this has been asked multiple times here, but I've a different issue dealing with it. In my case, the app receives a non well-formed dom structure passed as a string. Here's a sample :
<div class='video yt'><div class='yt_url'>http://www.youtube.com/watch?v=U_QLu_Twd0g&feature=abcde_gdata</div></div>
As you can see, the content is not well-formed. Now, if I try to parse using a normal SAX or DOM parse it'll throw an exception which is understood.
org.xml.sax.SAXParseException: The reference to entity "feature" must end with the ';' delimiter.
As per the requirement, I need to read this document,add few additional div tags and send the content back as a string. This works great by using a DOM parser as I can read through the input structure and add additional tags at their required position.
I tried using tools like JTidy to do a pre-processing and then parse, but that results in converting the document to a fully-blown html, which I don't want. Here's a sample code :
StringWriter writer = new StringWriter();
Tidy tidy = new Tidy(); // obtain a new Tidy instance
tidy.setXHTML(true);
tidy.parse(new ByteArrayInputStream(content.getBytes()), writer);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new ByteArrayInputStream(writer.toString().getBytes()));
// Traverse thru the content and add new tags
....
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
This completely converts the input to a well-formed html document. It then becomes hard to remove html tags manually. The other option I tried was to use SAX2DOM, which too creates a HTML doc. Here's a sample code .
ByteArrayInputStream is = new ByteArrayInputStream(content.getBytes());
Parser p = new Parser();
p.setFeature(IContentExtractionConstant.SAX_NAMESPACE,true);
SAX2DOM sax2dom = new SAX2DOM();
p.setContentHandler(sax2dom);
p.parse(new InputSource(is));
Document doc = (Document)sax2dom.getDOM();
I'll appreciate if someone can share their ideas.
Thanks
The simplest way is replacing xml reserved characters with the corresponding xml entities. You can do this manually:
content.replaceAll("&", "&");
If you don't want to modify your string before parsing it, I could propose you another way using SaxParser, but this solution is more complicated. Basically you have to:
write a LexicalHandler in
combination with ContentHandler
tell the parser to continue its
execution after fatal error (the
ErrorHandler isn't enough)
treat undeclared entities as simple
text
UPDATE
According to your comment, I'm going to add some details regarding the second solution. I've writed a class which extends DefaulHandler (default implementation of EntityResolver, DTDHandler, ContentHandler and ErrorHandler) and implements LexicalHandler. I've extended ErrorHandler's fatalError method (my implementations does nothing instead of throwing the exception) and ContentHandler's characters method which works in combination with startEntity method of LexicalHandler.
public class MyHandler extends DefaultHandler implements LexicalHandler {
private String currentEntity = null;
#Override
public void fatalError(SAXParseException e) throws SAXException {
}
#Override
public void characters(char[] ch, int start, int length)
throws SAXException {
String content = new String(ch, start, length);
if (currentEntity != null) {
content = "&" + currentEntity + content;
currentEntity = null;
}
System.out.print(content);
}
#Override
public void startEntity(String name) throws SAXException {
currentEntity = name;
}
#Override
public void endEntity(String name) throws SAXException {
}
#Override
public void startDTD(String name, String publicId, String systemId)
throws SAXException {
}
#Override
public void endDTD() throws SAXException {
}
#Override
public void startCDATA() throws SAXException {
}
#Override
public void endCDATA() throws SAXException {
}
#Override
public void comment(char[] ch, int start, int length) throws SAXException {
}
}
This is my main which parses your xml not well formed. It's very important the setFeature, because without it the parser throws the SaxParseException despite of the ErrorHandler empty implementation.
public static void main(String[] args) throws ParserConfigurationException,
SAXException, IOException {
String xml = "<div class='video yt'><div class='yt_url'>http://www.youtube.com/watch?v=U_QLu_Twd0g&feature=abcde_gdata</div></div>";
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
MyHandler myHandler = new MyHandler();
xmlReader.setContentHandler(myHandler);
xmlReader.setErrorHandler(myHandler);
xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler",
myHandler);
xmlReader.setFeature(
"http://apache.org/xml/features/continue-after-fatal-error",
true);
xmlReader.parse(new InputSource(new StringReader(xml)));
}
This main prints out the content of your div element which contains the error:
http://www.youtube.com/watch?v=U_QLu_Twd0g&feature=abcde_gdata
Keep in mind that this is an example which works with your input, maybe you'll have to complete it...for instance if you have some characters correctly escaped you should add some lines of code to handle this situation etc.
Hope this helps.

SAXException when parsing XML file with XSD schema

I have the following XSD file:
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
targetNamespace='http://www.wvf.com/schemas'
xmlns='http://www.wvf.com/schemas'
xmlns:acmewvf='http://www.wvf.com/schemas'>
<xs:element name='loft'>
</xs:element>
</xs:schema>
and the following XML file:
<?xml version="1.0"?>
<acmewvf:loft xmlns:acmewvf="http://www.wvf.com/schemas"
xmlns="http://www.wvf.com/schemas">
</acmewvf:loft>
When I execute the following Java code:
public void parse(InputStream constraints) {
final SchemaFactory schemaFactory = new XMLSchemaFactory();
final URL resource =
ClassLoader.getSystemClassLoader().getResource(SCHEMA_PATH);
final DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
Document doc = null;
factory.setSchema(schemaFactory.newSchema(resource));
final DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.parse(constraints);
I get the following SAXException (on the last line of the code):
cvc-elt.1: Cannot find the declaration
of element 'acmewvf:loft'.
(Note that SCHEMA_PATH points to the XSD file whose contents are given above and constraints is an input stream to the XML file whose contents are also given above.)
What's going wrong here?
See Using the Validating Parser. Probably, you should try to add the following to generate a namespace-aware, validating parser:
factory.setNamespaceAware(true);
factory.setValidating(true);
try {
factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
}
catch (IllegalArgumentException x) {
// Happens if the parser does not support JAXP 1.2
...
}
Don't forget to define:
static final String JAXP_SCHEMA_LANGUAGE =
"http://java.sun.com/xml/jaxp/properties/schemaLanguage";
static final String W3C_XML_SCHEMA =
"http://www.w3.org/2001/XMLSchema";

JAXB unmarshalling ignoring namespace turns element attributes into null

I'm trying to use JAXB to unmarshal an xml file into objects but have come across a few difficulties. The actual project has a few thousand lines in the xml file so i've reproduced the error on a smaller scale as follows:
The XML file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<catalogue title="some catalogue title"
publisher="some publishing house"
xmlns="x-schema:TamsDataSchema.xml"/>
The XSD file for producing JAXB classes
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="catalogue" type="catalogueType"/>
<xsd:complexType name="catalogueType">
<xsd:sequence>
<xsd:element ref="journal" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="title" type="xsd:string"/>
<xsd:attribute name="publisher" type="xsd:string"/>
</xsd:complexType>
</xsd:schema>
Code snippet 1:
final JAXBContext context = JAXBContext.newInstance(CatalogueType.class);
um = context.createUnmarshaller();
CatalogueType ct = (CatalogueType)um.unmarshal(new File("file output address"));
Which throws the error:
javax.xml.bind.UnmarshalException: unexpected element (uri:"x-schema:TamsDataSchema.xml", local:"catalogue"). Expected elements are <{}catalogue>
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:642)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:247)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:242)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:116)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1049)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:478)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:459)
at com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:148)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDispatcher.scanRootElementHook(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
...etc
So the namespace in the XML document is causing issues, unfortunately if it's removed it works fine, but as the file is supplied by the client we're stuck with it. I've attempted numerous ways of specifying it in the XSD but none of the permutations seem to work.
I also attempted to unmarshal ignoring namespace using the following code:
Unmarshaller um = context.createUnmarshaller();
final SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);
final XMLReader reader = sax.newSAXParser().getXMLReader();
final Source er = new SAXSource(reader, new InputSource(new FileReader("file location")));
CatalogueType ct = (CatalogueType)um.unmarshal(er);
System.out.println(ct.getPublisher());
System.out.println(ct.getTitle());
which works fine but fails to unmarshal element attributes and prints
null
null
Due to reasons beyond our control we're limited to using Java 1.5 and we're using JAXB 2.0 which is unfortunate because the second code block works as desired using Java 1.6.
any suggestions would be greatly appreciated, the alternative is cutting the namespace declaration out of the file before parsing it which seems inelegant.
Thank you for this post and your code snippet. It definitely put me on the right path as I was also going nuts trying to deal with some vendor-provided XML that had xmlns="http://vendor.com/foo" all over the place.
My first solution (before I read your post) was to take the XML in a String, then xmlString.replaceAll(" xmlns=", " ylmns="); (the horror, the horror). Besides offending my sensibility, in was a pain when processing XML from an InputStream.
My second solution, after looking at your code snippet: (I'm using Java7)
// given an InputStream inputStream:
String packageName = docClass.getPackage().getName();
JAXBContext jc = JAXBContext.newInstance(packageName);
Unmarshaller u = jc.createUnmarshaller();
InputSource is = new InputSource(inputStream);
final SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);
final XMLReader reader;
try {
reader = sax.newSAXParser().getXMLReader();
} catch (SAXException | ParserConfigurationException e) {
throw new RuntimeException(e);
}
SAXSource source = new SAXSource(reader, is);
#SuppressWarnings("unchecked")
JAXBElement<T> doc = (JAXBElement<T>)u.unmarshal(source);
return doc.getValue();
But now, I found a third solution which I like much better, and hopefully that might be useful to others: How to define properly the expected namespace in the schema:
<xsd:schema jxb:version="2.0"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns="http://vendor.com/foo"
targetNamespace="http://vendor.com/foo"
elementFormDefault="unqualified"
attributeFormDefault="unqualified">
With that, we can now remove the sax.setNamespaceAware(false); line (update: actually, if we keep the unmarshal(SAXSource) call, then we need to sax.setNamespaceAware(true). But the simpler way is to not bother with SAXSource and the code surrounding its creation and instead unmarshal(InputStream) which by default is namespace-aware. And the ouput of a marshal() also has the proper namespace too.
Yeh. Only about 4 hours down the drain.
How to ignore the namespaces
You can use an XMLStreamReader that is non-namespace aware, it will basically trim out all namespaces from the xml file that you're parsing:
// configure the stream reader factory
XMLInputFactory xif = XMLInputFactory.newFactory();
xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false); // this is the magic line
// create xml stream reader using our configured factory
StreamSource source = new StreamSource(someFile);
XMLStreamReader xsr = xif.createXMLStreamReader(source);
// unmarshall, note that it's better to reuse JAXBContext, as newInstance()
// calls are pretty expensive
JAXBContext jc = JAXBContext.newInstance(your.ObjectFactory.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Object unmarshal = unmarshaller.unmarshal(xsr);
Now the actual xml that gets fed into JAXB doesn't have any namespace info.
Important note (xjc)
If you generated java classes from an xsd schema using xjc and the schema had a namespace defined, then the generated annotations will have that namespace, so delete it manually! Otherwise JAXB won't recognize such data.
Places where the annotations should be changed:
ObjectFactory.java
// change this line
private final static QName _SomeType_QNAME = new QName("some-weird-namespace", "SomeType");
// to something like
private final static QName _SomeType_QNAME = new QName("", "SomeType", "");
// and this annotation
#XmlElementDecl(namespace = "some-weird-namespace", name = "SomeType")
// to this
#XmlElementDecl(namespace = "", name = "SomeType")
package-info.java
// change this annotation
#javax.xml.bind.annotation.XmlSchema(namespace = "some-weird-namespace", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
// to something like this
#javax.xml.bind.annotation.XmlSchema(namespace = "", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
Now your JAXB code will expect to see everything without any namespaces and the XMLStreamReader that we created supplies just that.
Here is my solution for this Namespace related issue. We can trick JAXB by implementing our own XMLFilter and Attribute.
class MyAttr extends AttributesImpl {
MyAttr(Attributes atts) {
super(atts);
}
#Override
public String getLocalName(int index) {
return super.getQName(index);
}
}
class MyFilter extends XMLFilterImpl {
#Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
super.startElement(uri, localName, qName, new VersAttr(atts));
}
}
public SomeObject testFromXML(InputStream input) {
try {
// Create the JAXBContext
JAXBContext jc = JAXBContext.newInstance(SomeObject.class);
// Create the XMLFilter
XMLFilter filter = new VersFilter();
// Set the parent XMLReader on the XMLFilter
SAXParserFactory spf = SAXParserFactory.newInstance();
//spf.setNamespaceAware(false);
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
filter.setParent(xr);
// Set UnmarshallerHandler as ContentHandler on XMLFilter
Unmarshaller unmarshaller = jc.createUnmarshaller();
UnmarshallerHandler unmarshallerHandler = unmarshaller
.getUnmarshallerHandler();
filter.setContentHandler(unmarshallerHandler);
// Parse the XML
InputSource is = new InputSource(input);
filter.parse(is);
return (SomeObject) unmarshallerHandler.getResult();
}catch (Exception e) {
logger.debug(ExceptionUtils.getFullStackTrace(e));
}
return null;
}
There is a workaround for this issue explained in this post: JAXB: How to ignore namespace during unmarshalling XML document?. It explains how to dynamically add/remove xmlns entries from XML using a SAX Filter. Handles marshalling and unmarshalling alike.

Categories