Java中的误导性XSD验证错误

时间:2013-10-18 08:29:18

标签: java validation xsd

我遇到了Java中XSD验证库提供的错误消息文本的问题。就我而言,似乎Apache Xerces是在引擎盖下使用的。 我发现Java中的错误至少具有误导性。

我从Java得到的错误是:

  

cvc-complex-type.2.4.b:元素“root”的内容不完整。预计会有一个'{subnode1,subnode2}'。

但另一个XML编辑器XMLSpy 2004说:

  

'subnode1'后面的强制元素:subnode2,subnode3

这里的第二个错误似乎更准确,因为没有丢失子节点1。但是,subnode2和subnode3都是。

这是我的XML文件:

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="schema.xsd">
    <subnode0/>
    <subnode1/>
</root>

这是我使用的XSD:

<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="root" type="NodeType">
    </xs:element>

    <xs:complexType name="NodeType">
        <xs:sequence>
            <xs:element name="subnode0" type="subnode0_Type" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element name="subnode1" type="subnode1_Type" minOccurs="0" maxOccurs="unbounded"/>
            <xs:element name="subnode2" type="subnode2_Type" minOccurs="1" maxOccurs="1"/>
            <xs:element name="subnode3" type="subnode3_Type" minOccurs="1" maxOccurs="1"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="subnode0_Type">
    </xs:complexType>
    <xs:complexType name="subnode1_Type">
    </xs:complexType>
    <xs:complexType name="subnode2_Type">
    </xs:complexType>
    <xs:complexType name="subnode3_Type">
    </xs:complexType>
</xs:schema>

这是我的Java代码,它调用验证API。

public class XsdErrorTextMain {

    private final static DocumentBuilder documentBuilder;
    private final static SchemaFactory schemaFactory;

    static {
        try {
            final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            documentBuilderFactory.setNamespaceAware(true);
            documentBuilder = documentBuilderFactory.newDocumentBuilder();
        } catch (final ParserConfigurationException e) {
            throw new RuntimeException(e);
        }

        schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    }

    public static void main(final String[] args) throws Exception {
        final Document xmlDocument = readXmlFile("file.xml");
        final Schema schema = readXsdFile("schema.xsd");

        runXmlValidation(xmlDocument, schema);
    }

    private static void runXmlValidation(final Document xmlDocument, final Schema schema) throws SAXException {
        try {
            final Validator validator = schema.newValidator();
            validator.setErrorHandler(new ErrorHandler() {

                @Override
                public void warning(final SAXParseException exception) {
                    return;
                }

                @Override
                public void error(final SAXParseException exception) throws SAXException {
                    displayValidationError(exception);
                }

                @Override
                public void fatalError(final SAXParseException exception) throws SAXException {
                    throw new RuntimeException("A fatal error was raised during the validation", exception);
                }
            });

            validator.validate(new DOMSource(xmlDocument));
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void displayValidationError(final SAXParseException validationError) {
        System.out.println("Line: " + validationError.getLineNumber());
        System.out.println("Column: " + validationError.getColumnNumber());
        System.out.println("Message: " + validationError.getMessage());
    }

    private static Document readXmlFile(final String path) throws IOException, SAXException {
        try (final InputStream xmlInputStream = ClassLoader.getSystemResource(path).openStream()) {
            return documentBuilder.parse(xmlInputStream);
        }
    }

    private static Schema readXsdFile(final String path) throws IOException, SAXException {
        final Document xsdDocument = readXmlFile(path);
        return schemaFactory.newSchema(new DOMSource(xsdDocument));
    }
}

我在这里遗漏了什么吗? 您是否知道可能更准确地计算错误消息的其他实现? 任何输入都表示赞赏。 谢谢!

1 个答案:

答案 0 :(得分:1)

严格地说,您所谓的XMLSpy 2004给您的信息具有误导性。鉴于subnode1上的“无界”maxOccurs,人们无法确切地说出在最后一次遇到的subnode1之后会出现什么:是否应该有另一个或更多的subnode1,然后是子节点2和3?

唯一可以肯定的是,在出现错误时,subnode1或subnode2之一可能会跟随。

这正是你得到的另一条消息(你说的是Xerces,它与标准的.NET相匹配)告诉你......

想象一下,有人使用此信息构建一个编辑器,通过构建有效XML所需的步骤(例如,智能感知或图形编辑器中的某些上下文菜单)驱动用户。您认为哪一个更好地指导用户?我会将Xerces消息称为正确的消息,因为一旦我填写了一个subnode1,XML Spy 2004消息将继续建议只有一个节点2和3(如果我没有填写2,为什么3? ?如果我想继续添加subnode1,该怎么办?)

我的观点是,它取决于您对这些消息的期望。我个人认为,对于我所描述的任务类型,Xerces(以及所示的.NET也是正确的)消息是正确的。有些人可能会发现它过于迭代,即只有在你击中子节点后才会被告知“准备”一个子节点...我的问题,我猜......

但是想象一下另一个场景,你所展示的整个节点集都包含在一个可选的序列中,然后该序列后面跟着一个强制的子节点....然后,在你的情况下,该消息应该是{ {1}}。然后,如果上面提到的可选序列重复,该怎么办?

最后,考虑到XSD模型的本质,人们可以实现这种“向前看”,为您提供可能性......事实上,选项很容易复合到信息变得无用的地步......而下一步是什么?现在总是一件好事。