如何在 SAXParseException 的情况下获取 XML 元素信息

How to get XML element information in case of SAXParseException

在标准 java 环境中根据 xsd 模式验证 xml 源时,我无法找到一种方法来获取有关验证失败的元素的信息(在许多情况下具体案例)。

捕获SAXParseException时,元素的信息消失了。但是,当调试到 xerces.XmlSchemaValidator 时,我可以看到原因是特定的错误消息未定义为泄露有关元素的信息。

例如(在我的 java 演示中也是如此)"cvc-mininclusive-valid" 错误是这样定义的: cvc-minInclusive-valid:对于类型“{2}”的 minInclusive“{1}”,值“{0}”不是方面有效的。 https://wiki.xmldation.com/Support/Validator/cvc-mininclusive-valid

我更希望生成这样的消息: cvc-type.3.1.3:元素“{0}”的值“{1}”无效。 https://wiki.xmldation.com/Support/Validator/cvc-type-3-1-3

当调试到 xerces.XMLSchemaValidator 时,我可以看到有两次连续调用 reportSchemaError(...) - 第二次只发生,如果第一个调用 return 没有异常抛出。

有没有办法配置验证器使用第二种报告方式或用元素信息丰富 SAXParseException?

请参阅下面我的复制&粘贴&可运行示例代码以获得进一步解释:

String xsd =
            "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
                    "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" version=\"1.0\">" +
                    "<xs:element name=\"demo\">" +
                    "<xs:complexType>" +
                    "<xs:sequence>" +

                    // given are two elements that cannot be < 1
                    "<xs:element name=\"foo\" type=\"xs:positiveInteger\" minOccurs=\"0\" maxOccurs=\"unbounded\" />" +
                    "<xs:element name=\"bar\" type=\"xs:positiveInteger\" minOccurs=\"0\" maxOccurs=\"unbounded\" />" +

                    "</xs:sequence>" +
                    "</xs:complexType>" +
                    "</xs:element>" +
                    "</xs:schema>";

    String xml =
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                    "<demo>" +

                    "<foo>1</foo>" +
                    // invalid!
                    "<foo>0</foo>" +
                    "<bar>2</bar>" +

                    "</demo>";

    Validator validator = SchemaFactory
            .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
            .newSchema(new StreamSource(new StringReader(xsd)))
            .newValidator();


    try {
        validator.validate(new StreamSource(new StringReader(xml)));
    } catch (SAXParseException e) {

        // unfortunately no element or line/column info:
        System.err.println(e.getMessage());

        // better, but still no element info:
        System.err.println(String.format("Line %s -  Column %s - %s",
                e.getLineNumber(),
                e.getColumnNumber(),
                e.getMessage()));
    }

尝试使用错误处理程序:

    public class LoggingErrorHandler implements ErrorHandler {

    private boolean isValid = true;

    public boolean isValid() {
        return this.isValid;
    }

    @Override
    public void warning(SAXParseException exc) {
        System.err.println(exc);
    }

    @Override
    public void error(SAXParseException exc) {
        System.err.println(exc);
        this.isValid = false;
    }

    @Override
    public void fatalError(SAXParseException exc) throws SAXParseException {
        System.err.println(exc);
        this.isValid = false;
        throw exc;
    }
}

并在验证器中使用它:

        Validator validator = SchemaFactory
                .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
                .newSchema(new StreamSource(new StringReader(xsd)))
                .newValidator();
        LoggingErrorHandler errorHandler = new LoggingErrorHandler();
        validator.setErrorHandler(errorHandler);
        validator.validate(new StreamSource(new StringReader(xml)));
        return errorHandler.isValid();

这没有很好的记录,但如果您有最新版本的 Xerces-J(请参阅 SVN Rev 380997),您可以验证 DOMSource 并从您的 Validator 查询ErrorHandler 检索验证器在报告错误时正在处理的当前 Element 节点。

例如,您可以这样写 ErrorHandler

public class ValidatorErrorHandler implements ErrorHandler {

private Validator validator;

public ValidatorErrorHandler(Validator v) {
    validator = v;
}

...

public void error(SAXParseException spe) throws SAXException {
    Node node = null;
    try {
        node = (Node) 
            validator.getProperty(
                "http://apache.org/xml/properties/dom/current-element-node");
    }
    catch (SAXException se) {}
    ...
}

然后用这个 ErrorHandler 调用 Validator,例如:

Validator validator = SchemaFactory
        .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
        .newSchema(new StreamSource(new StringReader(xsd)))
        .newValidator();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new InputSource(new StringReader(xml));
ErrorHandler errorHandler = new ValidatorErrorHandler(validator);
validator.setErrorHandler(errorHandler);
validator.validate(new DOMSource(doc));

获取发生错误的元素

我知道这已经过时了,但 Michael Glavassevich 的回答很有魅力!我还不能投票或发表评论,但这一位提供了他真正深入的知识。