I'm trying to unmarshal an XML string that does not match the JAXB class. I expected this to throw an exception, but instead, a new JAXB object is returned.
xmlStr (Input XML)
<urn1:RgBad
xmlns:urn1="urn:none">
</urn1:RgBad>
Correct XML
<urn:Rg
xmlns:urn="urn:test"
Code
(clazz = Rg.class)
JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StreamSource source = new StreamSource(new StringReader(xmlStr));
//Returns an actual Rg object, even though the source root element and namespace are different.
(unmarshaller.unmarshal(source, clazz)).getValue();
You can try to add your schema to verify your xml file when using JAXB
try {
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = sf.newSchema(new File( "yourSchema.xsd" ));
JAXBContext jc = JAXBContext.newInstance(clazz.getPackage().getName());
Unmarshaller u = jc.createUnmarshaller();
u.setSchema(schema);
u.setEventHandler(ehdler);
obj = u.unmarshal(xml);
} catch (JAXBException e) {
} finally {
}
Related
I want to map object from a SOAPResponse, so that, I'm using Marshall, but there is an error.
Response
<env:Envelope
xmlns:env='http://schemas.xmlsoap.org/soap/envelope/'>
<env:Header></env:Header>
<env:Body>
<ns1:cancelShipmentResponse
xmlns:ns1='http://yurticikargo.com.tr/ShippingOrderDispatcherServices'
xmlns:ns2='http://yurticikargo.com.tr/ShippingOrderDispatcherServices'
xmlns:ns3='http://yurticikargo.com.tr/WSExceptions/'>
<ShippingOrderResultVO>
<outFlag>1</outFlag>
<outResult>Yurtiçi Kargo sisteminde kayıtlı TR kullanıcı adlı bir kullanıcı yok!</outResult>
<errCode>1625</errCode>
<count>0</count>
<senderCustId>0</senderCustId>
</ShippingOrderResultVO>
</ns1:cancelShipmentResponse>
</env:Body></env:Envelope>
My Object
#XmlRootElement(name = "cancelShipmentResponse", namespace = "http://yurticikargo.com.tr/ShippingOrderDispatcherServices")#XmlAccessorType(XmlAccessType.FIELD)public class CancelShipmentResponse {#XmlElement(name = "ShippingOrderResultVO")ShipingOrderResultVO shipingOrderResultVO;}
My Codes
JAXBContext jc = JAXBContext.newInstance(CancelShipmentRequest.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
CancelShipmentRequest rc = (CancelShipmentRequest) unmarshaller.unmarshal(soapResponse.getSOAPBody());
How can I get the node details in the ValidationEventHandler in JAXB when an exception occurs while Unmarshalling?
My code:
JAXBContext jaxbContext = JAXBContext.newInstance("com.piyush");
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new StreamSource(new File("D:/liferay-develop/workspace/cat_test/v1/STD_MP.xsd")));
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
jaxbUnmarshaller.setSchema(schema);
ValidationEventCollector validationCollector= new ValidationEventCollector();
jaxbUnmarshaller.setEventHandler( validationCollector );
STDMP ts = (STDMP)jaxbUnmarshaller.unmarshal(xml_gkuzu);
if(validationCollector.hasEvents())
{
for(ValidationEvent event:validationCollector.getEvents())
{
String msg = event.getMessage();
System.out.println(msg);
// How to get the node details here ? I'm getting null value for node.
}
I am not very familiar with Marshaller but I gave a try in a project and the XML was generated fine. But when I apply the XML validation (with the .xsd file) it shows an Error saying that <Timestamp> is empty. I debugged the application and I see that inside my Bean the Timestamp is not empty but when the Marshaller generates the XML it is really empty. In the other hand, other attributes which uses XMLGregorianCalendar are present in the XML. I don't know what is happening.
This is the XML generator function:
public void generateXMLReportFile(String fileOutputDirectory,
String xmlOutputFileName,
CRSOECD crsOECD)
throws JAXBException, FileNotFoundException, IOException
{
String encoding = "UTF-8";
// Schema location to write to generated XML file.
String schemaLocation = "urn:oecd:ties:crs:v1 CrsXML_v1.0.xsd";
// Generate The Report:
JAXBContext context = JAXBContext.newInstance(CRSOECD.class);
StringWriter xml = new StringWriter();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, schemaLocation);
marshaller.marshal(crsOECD, xml);
try (OutputStream out = new FileOutputStream(fileOutputDirectory + xmlOutputFileName)) {
byte[] bytes = xml.toString().getBytes(encoding);
out.write(bytes, 0, bytes.length);
log.info("XML generated.");
}
}
If I debug, I see that the element which is empty in the XML is not empty in the Bean crsOECD.
Guys I just found the answer. The problem was that my element was tagged like this:
#XmlElement(name = "Timestamp", required = true)
#XmlSchemaType(name = "dateTime")
And I was passing only a Date with no Time.
I get an UnmarshalException (unexpected element) when unmarshallering XML, which i have just run through marshaller. I looks like the marshalling process generates XML, which cannot be unmarshalled.
ObjectFactory objectFactory = new ObjectFactory();
EjendomSoegType type = objectFactory.createEjendomSoegType();
EjendomSoegningKriterierType krit = new EjendomSoegningKriterierType();
{
krit.setBy("Skovlunde");
}
type.setEjendomSoegningKriterier(krit);
JAXBElement<EjendomSoegType> soeg = objectFactory.createEjendomSoeg(type);
// create JAXBContext which will be used to update writer
JAXBContext context = JAXBContext.newInstance(EjendomSoegType.class);
// marshall or convert jaxbElement containing element to xml format
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter writer = new StringWriter();
marshaller.marshal(soeg, writer);
String xml = writer.toString();
System.out.println( xml );
Unmarshaller unmarshaller = context.createUnmarshaller();
StringReader reader = new StringReader(xml);
soeg = (JAXBElement<EjendomSoegType>) unmarshaller.unmarshal(reader);
The following UnmarshalException is thrown by the last line of the code unmarshaller.unmarshal(reader):
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://rep.oio.dk/tinglysning.dk/service/message/elektroniskakt/1/", local:"EjendomSoeg"). Expected elements are (none)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:609)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:244)
The generated XML looks like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns8:EjendomSoeg xmlns:ns2="http://rep.oio.dk/tinglysning.dk/schema/model/1/"
xmlns:ns4="http://rep.oio.dk/cpr.dk/xml/schemas/core/2005/03/18/"
xmlns:ns3="http://rep.oio.dk/kms.dk/xml/schemas/2005/03/11/"
xmlns:ns5="http://rep.oio.dk/bbr.dk/xml/schemas/2005/03/11/"
xmlns:ns6="http://rep.oio.dk/bbr.dk/xml/schemas/2005/12/15/"
xmlns:ns7="http://rep.oio.dk/tinglysning.dk/schema/elektroniskakt/1/"
xmlns:ns8="http://rep.oio.dk/tinglysning.dk/service/message/elektroniskakt/1/">
<ns7:EjendomSoegningKriterier>
<By>Skovlunde</By>
</ns7:EjendomSoegningKriterier>
</ns8:EjendomSoeg>
Why is the UnmarshalException thrown?
ADDITIONAL INFORMATION ADDED LATER: The createEjendomSoeg method in ObjectFactory has the #XmlElementDecl tag.
/**
* Create an instance of {#link JAXBElement }{#code <}{#link EjendomSoegType }{#code >}}
*
*/
#XmlElementDecl(namespace = "http://rep.oio.dk/tinglysning.dk/service/message/elektroniskakt/1/", name = "EjendomSoeg")
public JAXBElement<EjendomSoegType> createEjendomSoeg(EjendomSoegType value) {
return new JAXBElement<EjendomSoegType>(_EjendomSoeg_QNAME, EjendomSoegType.class, null, value);
}
The #XmlElementDecl annotations on the ObjectFactory are not being picked up. To have ObjectFactory processesed you need to create the JAXBContext on this class, or on the package of your generated model.
JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
If there isn't an #XmlRootElement or #XmlElementDecl corresponding to the element you are trying to unmarshal then you will need to use an unmarshal method that takes a Class parameter.
soeg = unmarshaller.unmarshal(new StreamSource(reader), EjendomSoegType.class);
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);
}
}
}