2

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

我从 Java 得到的错误是:

cvc-complex-type.2.4.b:元素“根”的内容不完整。需要“{subnode1, subnode2}”之一。

但是另一个 XML 编辑器 XMLSpy 2004 是这样说的:

'subnode1'之后的''中预期的强制性元素:subnode2,subnode3

第二个错误在这里似乎更准确,因为没有丢失 subnode1。但是,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>

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

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));
    }
}

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

4

1 回答 1

1

严格地说,您所称的 XMLSpy 2004 给您的信息具有误导性。鉴于 subnode1 上的“无界”maxOccurs,我们无法真正确定在最后遇到的 subnode1 之后应该发生什么:是否应该有另一个或多个 subnode1,然后是子节点 2 和 3?

唯一可以确定的是 subnode1 或 subnode2 之一可能会跟随,此时引发错误。

这正是您收到的另一条消息(您说的是 Xerces,顺便说一句,它与标准 .NET 相匹配)告诉您...

想象一下,有人使用此信息构建一个编辑器,该编辑器驱动用户完成构建有效 XML 所需的步骤(例如 Intellisense,或图形编辑器中的某些上下文菜单)。你觉得哪一个更能引导用户?我认为 Xerces 消息是正确的,因为一旦我填写了子节点 1,XML Spy 2004 消息将继续建议节点 2 和 3(如果我没有填写 2,为什么要选择 3 ? 如果我想继续添加 subnode1s 怎么办?)

我的观点是,它可能取决于您对这些消息的期望。不过,我个人的看法是,对于我所描述的任务类型,Xerces(以及如所示的 .NET)消息是正确的消息。有些人可能会觉得它太迭代了,即只有在你点击一个 subnode2 之后才会告诉你“准备”一个 subnode3 ......你的问题,我猜......

但是再想象另一种情况,您显示的整个节点集都包装在一个可选序列中,并且该序列后面是一个强制性的 subnode4.... ,那么在您的情况下,消息应该是 read '' after 'subnode1': subnode2, subnode3, subnode4。然后,如果我上面提到的可选序列正在重复怎么办?

最后,鉴于 XSD 模型的性质,人们可以实现这种“前瞻性”来为您提供可能性......问题是,选项很容易复合到信息变得无用的地步......而接下来的总是好东西到现在。

于 2013-10-18T13:53:20.130 回答