I'm creating a rest api that will consume an xml document and create two objects from that document. The objects will then be persisted on a database using hibernate. This is what I have right now:
#PUT
#Path("saveVehicle")
#Consumes("application/xml;charset=utf-8")
#Produces("text/plain")
public String saveVehicleData(String xml) {
DBClient client = new DBClient();
Session session = client.getVehicleSession();
JAXBContext jaxbContext;
try {
Reader reader = new StringReader(xml);
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader xmlReader = factory.createXMLStreamReader(reader);
jaxbContext = JAXBContext.newInstance(VehicleData.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
VehicleData vehicle = (VehicleData) jaxbUnmarshaller.unmarshal(xmlReader);
session.saveOrUpdate(vehicle);
session.flush();
jaxbContext = JAXBContext.newInstance(VehicleOwner.class);
jaxbUnmarshaller = jaxbContext.createUnmarshaller();
VehicleOwner owner = (VehicleOwner) jaxbUnmarshaller.unmarshal(xmlReader);
session.saveOrUpdate(owner);
session.flush();
return vehicle.getRegistrationNo() + " " + owner.getName();
} catch (JAXBException e) {
e.printStackTrace();
return "JAXBException " + e.getMessage() + "\nCaused by " + e.getCause();
} catch (XMLStreamException e) {
e.printStackTrace();
return "XMLStreamException " + e.getMessage();
}
}
As you can see, this method is supposed to take in xml as a string. It will then generate a vehicle and a vehicle owner from that document. The document contains information about vehicles and their owners but I want them in separate database tables.
The classes use jaxb annotations for all the fields that I want to store on the database.
However, When I try to run this, I get the following stacktrace:
java.lang.ClassCastException: org.package.VehicleOwner cannot be cast to org.package.VehicleData
org.package.rest.LoginController.saveVehicleData(LoginController.java:369)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60)
com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:185)
com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)
com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:302)
com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108)
com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1542)
com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1473)
com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1419)
com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1409)
com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:409)
com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:558)
com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:733)
javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
Before I added the mapping for vehicle owners, I was able to instantiate a VehicleData and store it on the database. But now it seems like it is expecting this xml document to be mapped to a VehicleOwner. I thought it would be possible to map the document to different classes. Is that not the case? if it is, How would I do it?
It is possible to map document to different classes, but apparently you don't do this. Just doing:
jaxbContext = JAXBContext.newInstance(VehicleData.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
VehicleData vehicle = (VehicleData) jaxbUnmarshaller.unmarshal(xmlReader);
Does not magically map you XML structure to VehicleData structure. You have to define or generate mappings so that the same XML would map to different structures (VehicleData/VehicleOwner) in different JAXB contexts. You also have to make sure these contexts do not intersect (I guess they do in your case).
Related
I need to log XML message.
I use this code:
//From object to xml
public String createMarshalerDealInfoType(DealInfoType dealInfoType) {
StringWriter contactStr = null;
try {
JAXBContext jaxbContext = JAXBContext.newInstance(DealInfoType.class);
Marshaller jaxbUnmarshaller = jaxbContext.createMarshaller();
contactStr = new StringWriter();
jaxbUnmarshaller.marshal(dealInfoType, contactStr);
} catch (JAXBException e) {
log.error(e.getMessage());
}
return contactStr.toString();
}
In test class:
ResponseType ResponseType = woNspDealWS.createRequestWS(DealRequestType);
String DealResponce = updateDealEsb.createMarshalerDealInfoType(ResponseType.getDealInfo());
log.debug("Response: \n " + DealResponce);
Problem: in log output I see only first line of responce, not whole message
18:01:42,975 DEBUG updateDeal_Test:73 - Response: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
How do I make to print all response in XML?
SOLVED:
resolved problem with use annotation #XmlRootElement for test class.
The object which you have passed in test class might be empty
ResponseType.getDealInfo()
For solving this problem need use annotation #XmlRootElement in test class
I am new to jaxb usage, I am able to marshal a java class with xml annotations to .xml but not able to retrive data while unmarshalling. When I do a sysout on my unmarshalled data, it print the address of the context rather than the actual values. I am not sure as where I go wrong.
<collections>
<collectionclass="testclass">
<group>
<header code="T123" type="toys"/>
<obj1 location="1" shelf="4" />
<obj2 location="7" shelf="2" count="3"/>
<associations>
<association type="String" associatedName="train" associatedFieldSize="0"/>
<association type="DataLength" associatedName="ship" associatedFieldSize="0"/>
</associations>
</obj2>
</group>
<collectionclass="testclass">
</collections>
Also I would like to know more about terms like "jaxb context" and "java models/ java model classes" generated on unmarshalling an xml document and how it comes into picture.
Thanks in advance!
Try like below.
try {
File file = new File("C:\\file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Customer customer = (Customer) jaxbUnmarshaller.unmarshal(file);
System.out.println(customer);
} catch (JAXBException e) {
e.printStackTrace();
}
i have an unmanaged extension for my neo4j server.
and the code like the following.
#Path("/helloworld")
public class HelloWorldResource {
private final GraphDatabaseService database;
public HelloWorldResource(#Context GraphDatabaseService database) {
this.database = database;
}
#GET
#Produces(MediaType.TEXT_PLAIN)
#Path("/{nodeId}")
public Response hello(#PathParam("nodeId") long nodeId) {
String res = "";
try ( Transaction ignored = database.beginTx();)
{
//##problem
Result result = database.execute( "MATCH (n:KISI) where id(n)=1 return n" );
} catch (Exception e) {
res = "Error = " + e.getMessage();
}
return Response
.status(Status.OK)
.entity(("nodeId =" + nodeId + " " + res).getBytes(Charset
.forName("UTF-8"))).build();
}
}
When i deploy the code i got 500 internal error.
if i remove the code
Result result = database.execute( "MATCH (n:KISI) where id(n)=1 return
n" );
then everything is fine.
i checked the log file and the error is the following
Aug 13, 2015 3:34:36 AM com.sun.jersey.spi.container.ContainerResponse
mapMappableContainerException SEVERE: The exception contained within
MappableContainerException could not be mapped to a response,
re-throwing to the HTTP container
java.lang.NoSuchMethodError:
org.neo4j.graphdb.GraphDatabaseService.execute(Ljava/lang/String;)Lorg/neo4j/graphdb/Result;
at
org.neo4j.examples.server.unmanaged.HelloWorldResource.hello(HelloWorldResource.java:55)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606) at
com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60)
at
com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$ResponseOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:205)
at
com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)
at
org.neo4j.server.rest.transactional.TransactionalRequestDispatcher.dispatch(TransactionalRequestDispatcher.java:139)
at
com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288)
at
com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
at
com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108)
at
com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)
at
com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)
at
com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1469)
at
com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1400)
at
com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1349)
at
com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1339)
at
com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:416)
at
com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
at
com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:699)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:848) at
org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:698)
at
org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:505)
at
org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:211)
at
org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1096)
at
org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:432)
at
org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:175)
at
org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1030)
at
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:136)
at
org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:52)
at
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:445) at
org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:268) at
org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:229)
at
org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358)
at
org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:601)
at
org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:532)
at java.lang.Thread.run(Thread.java:745)
so whats wrong with my code?
I guess that your Neo4j distribution version and maven dependency version in pom.xml are not same.
But there are several things to check:
1) You should always close Result object.
Example:
try(Result result = database.execute( "MATCH (n:KISI) where id(n)=1 return n" )) {
// do stuff here
}
```
2) Exception occurs not in try-catch but later. You should change your code to this:
try ( Transaction tx = database.beginTx()) {
String query = "MATCH (n:KISI) where id(n)=1 return n";
// use result with try-with-resource to ensure that it will be closed
try(Result result = database.execute(query)) {
// do stuff you need with result here
return Response.ok("nodeId =" + nodeId).build();
}
tx.success(); // mark transaction as successful
} catch (Exception e) {
// If exception occurs - send exception message with 500 status code
// It's good idea to write Exception stacktrace to log there
return Response.serverError().entity(e.getMessage()).build()
}
3) You should check how unmanaged extension .jar file is build.
All Neo4j dependencies should be provided in pom.xml (there are already there in Neo4j distribution).
Check that your database version and your dependency version in pom.xml are same. GraphDatabaseService::execute method is invented recently (2.2.3 if I remember correctly). Probably your database distribution is older than your maven dependencies.
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);
}
}
}