I am using JAXB to parse xml elements from the SOAP response. I have defined POJO classes for the xml elements. I have tested pojo classes without namespace and prefix its working fine .Though when i am trying to parse with namespaces and prefix facing the following exception.Requirement is to parse the input from SOAPMessage Object
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Envelope"). Expected elements are <{}Envelope>
Tried to fix by creating #XMLSchema for the package in package-info.java and located this file in package folder.Can any one guide me to move forward?
Referred this posts but didn help me .
EDITED :XMLSchema
#javax.xml.bind.annotation.XmlSchema (
xmlns = { #javax.xml.bind.annotation.XmlNs(prefix = "env",
namespaceURI="http://schemas.xmlsoap.org/soap/envelope/"),
#javax.xml.bind.annotation.XmlNs(prefix="ns3", namespaceURI="http://www.xxxx.com/ncp/oomr/dto/")
}
)
package com.one.two;
Thanks in advance
This can be done without modifying the generated JAXB code using standard SOAPMessage class. I wrote about this here and here
It's a little fiddly but works correctly.
Marshalling
Farm farm = new Farm();
farm.getHorse().add(new Horse());
farm.getHorse().get(0).setName("glue factory");
farm.getHorse().get(0).setHeight(BigInteger.valueOf(123));
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Marshaller marshaller = JAXBContext.newInstance(Farm.class).createMarshaller();
marshaller.marshal(farm, document);
SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
soapMessage.getSOAPBody().addDocument(document);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
soapMessage.writeTo(outputStream);
String output = new String(outputStream.toByteArray());
Unmarshalling
String example =
"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Header /><soapenv:Body><ns2:farm xmlns:ns2=\"http://adamish.com/example/farm\"><horse height=\"123\" name=\"glue factory\"/></ns2:farm></soapenv:Body></soapenv:Envelope>";
SOAPMessage message = MessageFactory.newInstance().createMessage(null,
new ByteArrayInputStream(example.getBytes()));
Unmarshaller unmarshaller = JAXBContext.newInstance(Farm.class).createUnmarshaller();
Farm farm = (Farm)unmarshaller.unmarshal(message.getSOAPBody().extractContentAsDocument());
Here is how you can handle your use cae:
If You Need to Map the Envelope Element
package-info
Typically you would use the #XmlSchema as follows. Using the namespace and elementFormDefault properties like I've done means that all data mapped to XML elements unless otherwise mapped will belong to the http://www.xxxx.com/ncp/oomr/dto/ namespace. The information specified in xmlns is for XML schema generation altough some JAXB implementations use this to determine the preferred prefix for a namespace when marshalling (see: http://blog.bdoughan.com/2011/11/jaxb-and-namespace-prefixes.html).
#XmlSchema (
namespace="http://www.xxxx.com/ncp/oomr/dto/",
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns = {
#XmlNs(prefix = "env", namespaceURI="http://schemas.xmlsoap.org/soap/envelope/"),
#XmlNs(prefix="whatever", namespaceURI="http://www.xxxx.com/ncp/oomr/dto/")
}
)
package com.one.two;
import javax.xml.bind.annotation.*;
Envelope
If within the com.one.two you need to map to elements from a namespace other than http://www.xxxx.com/ncp/oomr/dto/ then you need to specify it in the #XmlRootElement and #XmlElement annotations.
package com.one.two;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Envelope", namespace="http://schemas.xmlsoap.org/soap/envelope/")
#XmlAccessorType(XmlAccessType.FIELD)
public class Envelope {
#XmlElement(name="Body", namespace="http://schemas.xmlsoap.org/soap/envelope/")
private Body body;
}
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
If You Just Want to Map the Body
You can use a StAX parser to parse the message and advance to the payload portion and unmarshal from there:
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class UnmarshalDemo {
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource xml = new StreamSource("src/blog/stax/middle/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
xsr.nextTag();
while(!xsr.getLocalName().equals("return")) {
xsr.nextTag();
}
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement<Customer> jb = unmarshaller.unmarshal(xsr, Customer.class);
xsr.close();
}
}
For More Information
http://blog.bdoughan.com/2012/08/handle-middle-of-xml-document-with-jaxb.html
Just wanted to add onto the existing answers -- while unmarshalling if the XML document is not namespace aware you might receive an error: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://some.url";, local:"someOperation")
If this is the case you can simply use a different method on the unmarshaller:
Unmarshaller unmarshaller = JAXBContext.newInstance(YourObject.class).createUnmarshaller();
JAXBElement<YourObject> element = unmarshaller.unmarshal(message.getSOAPBody().extractContentAsDocument(), YourObject.class);
YourObject yo = element.getValue();
Related
In my application user uploads several XMLs. Few XMLs that are uploaded do not contain a namespace tag and others contain it. I want to be able to support upload for both. JAXB is giving exception on former.
I want to able able to make namespace as optional ie support both files.
XML that is working
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ns2:transforms xmlns:ns2="http://www.mynamesapace.com/xmlbeans/connectorconfig">
XML that is failing
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<transforms>
Here is how I am unmarshalling the XML
JAXBContext jaxbContext = JAXBContext.newInstance(Transforms.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
transforms = (Transforms) jaxbUnmarshaller.unmarshal(file);
This is my pojo
#XmlRootElement(name = "transforms", namespace =
"http://www.mynamesapace.com/xmlbeans/connectorconfig")
public class Transforms implements ConfigDiffable<Transforms,
ChangedTransforms> {
.....
Update :
If I remove
namespace =
"http://www.mynamesapace.com/xmlbeans/connectorconfig"
XML without namespace start working
Create a class:
class XMLReaderWithoutNamespace extends StreamReaderDelegate {
public XMLReaderWithoutNamespace(XMLStreamReader reader) {
super(reader);
}
#Override
public String getAttributeNamespace(int arg0) {
return "";
}
#Override
public String getNamespaceURI() {
return "";
}
}
Change your unmarshalling to:
JAXBContext jaxbContext = JAXBContext.newInstance(Transforms.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
InputStream is = new FileInputStream(file);
XMLStreamReader xsr = XMLInputFactory.newFactory().createXMLStreamReader(is);
XMLReaderWithoutNamespace xr = new XMLReaderWithoutNamespace(xsr);
transforms = (Transforms) jaxbUnmarshaller.unmarshal(xr);
I had no namespace defined in the pojo when I tested this.
Solution taken from this answer.
I'm using JAXB to marshall an Java object to XML.
Example;
com.abc.Defter.java object package-info.java:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://www.example.com",
xmlns = {#javax.xml.bind.annotation.XmlNs(namespaceURI = "http://www.example.com", prefix = "abc")})
package com.abc;
com.xyz.Xbrl.java object package-info.java:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://www.example2.com",
xmlns = {#javax.xml.bind.annotation.XmlNs(namespaceURI = "http://www.example2.com", prefix = "xyz")})
package com.xyz;
Marshalling result xml string;
<abc:defter xmlns:abc="http://www.example.com" xmlns:xyz="http://www.example2.com">
<xyz:xbrl>
<xyz:unit id="try">
<measure>iso4217:TRY</measure>
</xyz:unit>
</xyz:xbrl>
</abc:defter>
All namespaces are in the root directory, but I do not want it. I would like the following example;
<abc:defter xmlns:abc="http://www.example.com">
<xyz:xbrl xmlns:xyz="http://www.example2.com">
<xyz:unit id="try">
<measure>iso4217:TRY</measure>
</xyz:unit>
</xyz:xbrl>
</abc:defter>
How can I do it JAXB or another method using?
I am trying to create this kind(xsd inside) of documents. some examples are here. Because of constant values in root-element and some other constant elements i generated a template with eclipse:
<?xml version="1.0" encoding="UTF-8"?>
<invoice:response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:invoice="http://www.forum-datenaustausch.ch/invoice" xmlns="http://www.forum-datenaustausch.ch/invoice" xsi:schemaLocation="http://www.forum-datenaustausch.ch/invoice generalInvoiceResponse_440.xsd" language="de">
<invoice:processing>
<invoice:transport from="" to="">
<invoice:via sequence_id="0" via=""/>
</invoice:transport>
</invoice:processing>
<invoice:payload response_timestamp="0">
<invoice:invoice request_date="2001-12-31T12:00:00" request_id="" request_timestamp="0"/>
</invoice:payload>
</invoice:response>
But simple unmarshalling and marshalling changes the content:
<?xml version="1.0" encoding="UTF-8"?>
<response xmlns="http://www.forum-datenaustausch.ch/invoice" xmlns:ns1="http://www.w3.org/2000/09/xmldsig#" xmlns:ns0="http://www.w3.org/2001/04/xmlenc#" language="de">
<processing>
<transport from="" to="">
<via via="" sequence_id="0"/>
</transport>
</processing>
<payload response_timestamp="0">
<invoice request_timestamp="0" request_date="2001-12-31T12:00:00.0" request_id=""/>
</payload>
</response>
for some reason the schema location attribute is gone. this could be added manually before marshalling. the 2nd problem is, all prefixes are gone.
i don't know who consumes the produced xml (do they unmarshal with handwritten code? with or without validation?). Because of this i want an output that is most similar to given examples and valid.
So is there either a way to leave existing elements and attributes untouched and to let moxy add namespace prefixes to each element?
The following should help. This question is also being handled on the EclipseLink forum:
http://www.eclipse.org/forums/index.php/t/487391/
for some reason the schema location attribute is gone.
You can specify the following property on the Marshaller to output a schema location:
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.forum-datenaustausch.ch/invoice generalInvoiceResponse_440.xsd");
2nd problem is, all prefixes are gone.
The namespace prefixes are gone, but the namespace qualification is the same (all elements have the same local name and namespace URI). In the first document the invoice prefix is assigned to the http://www.forum-datenaustausch.ch/invoice namespace, and in the second document that namespace is assigned as the default namespace
CONTROLLING NAMESPACE PREFIXES AT DESIGN TIME
You can provide MOXy hints at what namespace prefixes should be used by leveraging the #XmlSchema annotation (see: http://blog.bdoughan.com/2011/11/jaxb-and-namespace-prefixes.html).
package-info
#XmlSchema(
elementFormDefault=XmlNsForm.QUALIFIED,
namespace="http://www.forum-datenaustausch.ch/invoice",
xmlns={
#XmlNs(prefix="invoice", namespaceURI="http://www.forum-datenaustausch.ch/invoice"),
#XmlNs(prefix="ds", namespaceURI="http://www.w3.org/2000/09/xmldsig#"),
#XmlNs(prefix="xenc", namespaceURI="http://www.w3.org/2001/04/xmlenc#")
}
)
package forum16559889;
import javax.xml.bind.annotation.*;
Response
package forum16559889;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Response {
}
Demo
package forum16559889;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
Response response = new Response();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.forum-datenaustausch.ch/invoice generalInvoiceResponse_440.xsd");
marshaller.marshal(response, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<invoice:response xsi:schemaLocation="http://www.forum-datenaustausch.ch/invoice generalInvoiceResponse_440.xsd" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" xmlns:invoice="http://www.forum-datenaustausch.ch/invoice" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
CONTROLLING THE NAMESPACE PREFIXES AT RUNTIME
You cam leverage MOXy's NamespacePrefixMapper extension to control the namespace prefixes used at runtime.
package forum16559889;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.oxm.NamespacePrefixMapper;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
Response response = new Response();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.forum-datenaustausch.ch/invoice generalInvoiceResponse_440.xsd");
marshaller.setProperty(MarshallerProperties.NAMESPACE_PREFIX_MAPPER, new NamespacePrefixMapper() {
#Override
public String getPreferredPrefix(String namespaceUri,
String suggestion, boolean requirePrefix) {
if("http://www.forum-datenaustausch.ch/invoice".equals(namespaceUri)) {
return "invoice";
} else if("http://www.w3.org/2000/09/xmldsig#".equals(namespaceUri)) {
return "ds";
} else if("http://www.w3.org/2001/04/xmlenc#".equals(namespaceUri)) {
return "xenc";
} else {
return null;
}
}
});
marshaller.marshal(response, System.out);
}
}
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.
I need to validate my JAXB objects before marshalling to an XML file. Prior to JAXB 2.0, one could use a javax.xml.bind.Validator. But that has been deprecated so I'm trying to figure out the proper way of doing this. I'm familiar with validating at marshall time but in my case I just want to know if its valid. I suppose I could marshall to a temp file or memory and throw it away but wondering if there is a more elegant solution.
Firstly, javax.xml.bind.Validator has been deprecated in favour of javax.xml.validation.Schema (javadoc). The idea is that you parse your schema via a javax.xml.validation.SchemaFactory (javadoc), and inject that into the marshaller/unmarshaller.
As for your question regarding validation without marshalling, the problem here is that JAXB actually delegates the validation to Xerces (or whichever SAX processor you're using), and Xerces validates your document as a stream of SAX events. So in order to validate, you need to perform some kind of marshalling.
The lowest-impact implementation of this would be to use a "/dev/null" implementation of a SAX processor. Marshalling to a null OutputStream would still involve XML generation, which is wasteful. So I would suggest:
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(locationOfMySchema);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setSchema(schema);
marshaller.marshal(objectToMarshal, new DefaultHandler());
DefaultHandler will discard all the events, and the marshal() operation will throw a JAXBException if validation against the schema fails.
You could use a javax.xml.bind.util.JAXBSource (javadoc) and a javax.xml.validation.Validator (javadoc), throw in an implementation of org.xml.sax.ErrorHandler (javadoc) and do the following:
import java.io.File;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.util.JAXBSource;
import javax.xml.validation.*;
public class Demo {
public static void main(String[] args) throws Exception {
Customer customer = new Customer();
customer.setName("Jane Doe");
customer.getPhoneNumbers().add(new PhoneNumber());
customer.getPhoneNumbers().add(new PhoneNumber());
customer.getPhoneNumbers().add(new PhoneNumber());
JAXBContext jc = JAXBContext.newInstance(Customer.class);
JAXBSource source = new JAXBSource(jc, customer);
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(new File("customer.xsd"));
Validator validator = schema.newValidator();
validator.setErrorHandler(new MyErrorHandler());
validator.validate(source);
}
}
For More Information, See My Blog
http://blog.bdoughan.com/2010/11/validate-jaxb-object-model-with-xml.html
This how we did it. I had to find a way to validate the xml file versus an xsd corresponding to the version of the xml since we have many apps using different versions of the xml content.
I didn't really find any good examples on the net and finally finished with this. Hope this will help.
ValidationEventCollector vec = new ValidationEventCollector();
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
URL xsdURL = getClass().getResource("/xsd/" + xsd);
Schema schema = sf.newSchema(xsdURL);
//You should change your jaxbContext here for your stuff....
Unmarshaller um = (getJAXBContext(NotificationReponseEnum.NOTIFICATION, notificationWrapper.getEnteteNotification().getTypeNotification()))
.createUnmarshaller();
um.setSchema(schema);
try {
StringReader reader = new StringReader(xml);
um.setEventHandler(vec);
um.unmarshal(reader);
} catch (javax.xml.bind.UnmarshalException ex) {
if (vec != null && vec.hasEvents()) {
erreurs = new ArrayList < MessageErreur > ();
for (ValidationEvent ve: vec.getEvents()) {
MessageErreur erreur = new MessageErreur();
String msg = ve.getMessage();
ValidationEventLocator vel = ve.getLocator();
int numLigne = vel.getLineNumber();
int numColonne = vel.getColumnNumber();
erreur.setMessage(msg);
msgErreur.setCode(ve.getSeverity())
erreur.setException(ve.getLinkedException());
erreur.setPosition(numLigne, numColonne);
erreurs.add(erreur);
logger.debug("Erreur de validation xml" + "erreur : " + numLigne + "." + numColonne + ": " + msg);
}
}
}