I experienced that Jaxb2Marshaller failed to validate my invalid XML against the XSD while doing the unmarshalling. I'm using Spring 4.0.0.RELEASE and Java 7.
Here is an example:
The XSD file: fruit.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:attribute name="quantity" type="xs:string" />
<xs:element name="fruit">
<xs:complexType>
<xs:sequence>
<xs:choice>
<xs:element ref="banana" />
<xs:element ref="apple" />
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="banana">
<xs:complexType>
<xs:attribute ref="quantity" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="apple">
<xs:complexType>
<xs:attribute ref="quantity" use="required" />
</xs:complexType>
</xs:element>
</xs:schema>
I generated the JAXB POJOs from this XSD with Spring Tool Suite to com.testraptor.xml.jaxb
package.
My invalid XML file: invalid.xml
<?xml version="1.0" encoding="UTF-8"?>
<fruit>
<banana quantity="5" />
<apple quantity="3" />
</fruit>
As you can see I broke the schema because there is a choice in the fruit tag and I used banana and apple at the same time.
My Spring configuration file: app-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/oxm
http://www.springframework.org/schema/oxm/spring-oxm-4.0.xsd">
<oxm:jaxb2-marshaller id="marshaller" context-path="com.fruit.xml.jaxb" />
</beans>
The main class to test: Main.java
package com.fruit.test;
import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.xml.sax.SAXException;
import com.fruit.xml.jaxb.Fruit;
public class Main {
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext(
"app-context.xml");
//--------------------------------SCHEMA VALIDATION IS OMITTED
Jaxb2Marshaller jaxb2Unmarshaller = (Jaxb2Marshaller) appContext
.getBean("marshaller");
Resource schemaResource = appContext
.getResource("classpath:fruit.xsd");
jaxb2Unmarshaller.setSchema(schemaResource);
Resource xml = appContext.getResource("classpath:invalid.xml");
try {
Fruit fruit = (Fruit) jaxb2Unmarshaller.unmarshal(new StreamSource(xml.getInputStream()));
System.out.println("SCHEMA VALIDATION IS OMITTED:");
System.out.println("Apple quantity is " + fruit.getApple().getQuantity());
System.out.println("Banana quantity is " + fruit.getBanana().getQuantity());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//--------------------------------SCHEMA VALIDATION IS PASSED
SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema;
try {
schema = sf.newSchema(schemaResource.getURL());
JAXBContext context = JAXBContext.newInstance(Fruit.class);
Unmarshaller unmarshaller = (Unmarshaller) context.createUnmarshaller();
unmarshaller.setSchema(schema);
Fruit fruit2 = (Fruit) unmarshaller.unmarshal(xml.getInputStream());
System.out.println("SCHEMA VALIDATION IS PASSED:");
System.out.println("Apple quantity is " + fruit2.getApple().getQuantity());
System.out.println("Banana quantity is " + fruit2.getBanana().getQuantity());
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
When I run the code I get the following result:
INFO: Loading XML bean definitions from class path resource [app-context.xml]
dec. 26, 2013 9:33:22 DE org.springframework.oxm.jaxb.Jaxb2Marshaller createJaxbContextFromContextPath
INFO: Creating JAXBContext with context path [com.fruit.xml.jaxb]
SCHEMA VALIDATION IS OMITTED:
Apple quantity is 3
Banana quantity is 5
javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 4; columnNumber: 24; cvc-complex-type.2.4.d: Invalid content was found starting with element 'apple'. No child element is expected at this point.]
at ...
As you can see the Jaxb2Marshaller forgot to validate my invalid XML file while the Unmarshaller could validate it.
Can anyone give me a clue what could be wrong with my code?
I had the same problem when configuring Jaxb2Marshaller
at runtime. The proposed solution to set the schema via Spring XML got me thinking: What's the difference between my code setting the property and Spring?
The answer is simple: Jaxb2Marshaller
implements org.springframework.beans.factory.InitializingBean
which defines the lifecycle method afterPropertiesSet()
and this method is called by Springs BeanFactory
after the properties have been set.
So the Solution is to call afterPropertiesSet()
after setSchema()
has been used to set the schema property. Because in that method the schema property (which is a org.springframework.core.io.Resource
array) is actually used to set the internal schema field of type javax.xml.validation.Schema
which is later used for schema validation.
Example:
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
Resource schema = new ClassPathResource("fruit.xsd");
marshaller.setSchema(schema);
marshaller.setContextPath("com.fruit.xml.jaxb");
// manually call Spring lifecycle method which actually loads the schema resource
marshaller.afterPropertiesSet();
// use marshaller...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With