Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jaxb ignore the namespace on unmarshalling

Tags:

java

spring

jaxb

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.

like image 651
Arnaud Denoyelle Avatar asked Apr 14 '15 08:04

Arnaud Denoyelle


1 Answers

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);

`

like image 113
Binal Parekh Avatar answered Oct 21 '22 12:10

Binal Parekh