javaxmlxsdschemaxerces

XSD - How to allow elements in any order while some with occurance [0-1] whereas others with [0-unbounded]


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.

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?


Solution

  • 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.