I have to validate an xml file against an xsd grammer. What follows is a simple simulation of my original requirement.
There's a root element named <foo>
It has four child elements, let's denote them as child1, child2, child3 and child4 for the sake of simplicity.
<foo />
a valid xml.After going through some existing answers, I figured out that the more verbose answer given in [1]
is not a practical solution to my problem since there are more than 10 child elements
would yield to a combinatorial explosion. Then, in the latter part of the answer states that xsd 1.1
relaxes some of the constraints, so that the use of <xs:all>
along with maxOccurs="unbounded"
is allowed.
Then I used xerces library, and tried using xsd 1.1 features with java. What follows is my sample code. However, when multiple elements are presented in the xml, I am getting the following error. cvc-complex-type.2.4.a: Invalid content was found starting with element 'child1'. One of '{child2, child3, child4}' is expected.
xsd schema with xsd 1.1 extensions.
<?xml version="1.1" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
elementFormDefault="qualified" vc:minVersion="1.1">
<xs:element name="foo">
<xs:complexType>
<xs:all>
<xs:element name="child1" vc:minOccurs="0"
vc:maxOccurs="unbounded" />
<xs:element name="child2" vc:minOccurs="0"
vc:maxOccurs="unbounded" />
<xs:element name="child3" type="xs:string" />
<xs:element name="child4" type="xs:string" />
</xs:all>
</xs:complexType>
</xs:element>
</xs:schema>
xml payload
<foo>
<child1> a </child1>
<child1> b </child1>
<child3> c </child3>
<child2> d </child2>
<child4> e </child4>
<child2> g </child2>
<child1> f </child1>
</foo>
maven dependency
<dependency>
<groupId>org.opengis.cite.xerces</groupId>
<artifactId>xercesImpl-xsd11</artifactId>
<version>2.12-beta-r1667115</version>
<exclusions>
<exclusion>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.exist-db.thirdparty.org.eclipse.wst.xml</groupId>
<artifactId>xpath2</artifactId>
<version>1.2.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>edu.princeton.cup</groupId>
<artifactId>java-cup</artifactId>
<version>10k</version>
<scope>runtime</scope>
</dependency>
or
<dependency>
<groupId>org.ibissource</groupId>
<artifactId>ibis-xerces</artifactId>
<version>2.12.2-xml-schema-1.1</version>
</dependency>
sample java code:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.StringJoiner;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public class CrtDataValidatorImpl {
public void validate(String xsdFilePath, String data) {
final XmlErrorHandler errorHandler = new XmlErrorHandler();
try {
final File schemaFile = new File(xsdFilePath);
final SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/XML/XMLSchema/v1.1");
final Schema schema = schemaFactory.newSchema(schemaFile);
final Validator validator = schema.newValidator();
validator.setErrorHandler(errorHandler);
final Source xmlSource = new StreamSource(new StringReader(data));
validator.validate(xmlSource);
} catch (SAXException e) {
System.out.println(e.getMessage());
} catch (IOException e) {
}
System.out.println(String.format("Number of errors: %s", errorHandler.getExceptions().size()));
for (Exception ex : errorHandler.getExceptions())
System.out.println(ex.getMessage());
}
public static void main(String[] args) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("./src/main/resources/simple-payload.xml"))) {
final StringJoiner sj = new StringJoiner("\n");
String line = br.readLine();
while (line != null) {
sj.add(line);
line = br.readLine();
}
System.out.println(sj.toString());
new CrtDataValidatorImpl().validate("./src/main/resources/sample.xsd", sj.toString());
}
}
static class XmlErrorHandler implements ErrorHandler {
private Collection<SAXParseException> exceptions;
public XmlErrorHandler() {
this.exceptions = new ArrayList<>();
}
public Collection<SAXParseException> getExceptions() {
return exceptions;
}
@Override
public void warning(SAXParseException exception) {
exceptions.add(exception);
}
@Override
public void error(SAXParseException exception) {
exceptions.add(exception);
}
@Override
public void fatalError(SAXParseException exception) {
exceptions.add(exception);
}
}
}
I tried debugging the issue, unfortunately it didn't shed any light in figuring out the cause. What is missing here? Any help would be appreciated. Thanks.
[1] XSD - how to allow elements in any order any number of times?
The minOccurs
and maxOccurs
attribute should not be in the vc
namespace.
By placing minOccurs
and maxOccurs
in the "http://www.w3.org/2007/XMLSchema-versioning"
namespace, the real minOccurs
and maxOccurs
are absent. Thus, the default values of 1
come into play, explaining the observed validation error message.