使用 StaX 和 DTD 验证标记

Validating markup with StaX and DTD

我有一个简单的 XML 文件和一个表示它的简单 DTD。

我正在使用假设,即可行,尽管可能不适合,以使用 DTD 验证标记(相反到一个实际的模式,我认为这是最强大和最合适的选择)。

基于这个假设,我想我会尝试使用 StaX。

问题

即使提供了有效的 DTD,StaX 似乎也不会验证任何内容,并且 XML 文件与 DTD 中定义的架构不匹配。

我原以为下面的代码会因某些与验证相关的堆栈跟踪而失败,而不是在我的 foo.xml 文件中打印所有元素名称。

我想我的假设可能有很大的问题,但是我在浏览的相关SO问题和其他网络文献中都找不到任何具体的解释。

下面的代码和资源。

foo.dtd

<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT foo (bar+) >
<!ATTLIST foo
    foo CDATA   #REQUIRED
>
<!ELEMENT bar (#PCDATA) >
<!ATTLIST bar
    bar (bar|blah) #REQUIRED
>

foo.xml

<?xml version="1.0" encoding="UTF-8"?>
<foo foo="foo">
<!-- attribute "blah" invalid -->
<bar bar="bar" blah="blah">bar</bar>
<!-- invalid -->
<bar />
</foo>

代码

XMLInputFactory xif = XMLInputFactory.newFactory();

// not sure if this is required? doens't seem to help
xif.setProperty(XMLInputFactory.IS_VALIDATING, "true"); 

// adding dtd
xif.setXMLResolver(
    new XMLResolver() {
        public Object resolveEntity(String publicID, String systemID, String baseURI, String namespace) throws XMLStreamException {
            if ("foo.dtd".equals(systemID)) {
                 return Main.class.getResourceAsStream("foo.dtd");
            }
            else {
                return null;
            }
        };
    }
);
XMLStreamReader reader = null;

try {
    reader = xif.createXMLStreamReader(Main.class.getResourceAsStream("foo.xml"));
    while (reader.hasNext()) {
        switch (reader.next()) {
            // prints local name just for testing 
            // - expecting this to fail at some point
            case XMLStreamReader.START_ELEMENT: 
            System.out.println(reader.getLocalName());
        }

    }
}
finally {
    if (reader != null) {
        try {
            reader.close();
        }
        catch (Exception e) {}
    }
}

输出(期待一些堆栈跟踪)

foo
bar
bar

我看到的第一个问题是您正在测试 systemID ("foo.dtd".equals(systemID)),但是您的 XML 文件没有指向 DTD 的系统标识符。

尝试将带有系统标识符的文档类型声明添加到您的 XML:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo SYSTEM "foo.dtd">
<foo foo="foo">
    <!-- attribute "blah" invalid -->
    <bar bar="bar" blah="blah">bar</bar>
    <!-- invalid -->
    <bar />
</foo>

此外,according to this question,您将需要使用不同的 StAX 实现,因为默认不支持验证。

就像该问题的答案中所建议的那样,我尝试在我的类路径中使用 woodstox by adding the woodstox 5.0.3 jar and the stax2 api 4.0.0 jar(并将带有系统标识符的文档类型添加到 XML)并得到了预期的异常:

foo
Nov 28, 2016 3:09:53 PM so.test2.SOTest2 main
SEVERE: null
com.ctc.wstx.exc.WstxValidationException: Element <bar> has no attribute "blah"
 at [row,col {unknown-source}]: [5,5]
    at com.ctc.wstx.exc.WstxValidationException.create(WstxValidationException.java:50)
    at com.ctc.wstx.sr.StreamScanner.reportValidationProblem(StreamScanner.java:580)
    at com.ctc.wstx.sr.ValidatingStreamReader.reportValidationProblem(ValidatingStreamReader.java:383)
    at com.ctc.wstx.sr.InputElementStack.reportProblem(InputElementStack.java:849)
    at com.ctc.wstx.dtd.DTDValidatorBase.doReportValidationProblem(DTDValidatorBase.java:497)
    at com.ctc.wstx.dtd.DTDValidatorBase.reportValidationProblem(DTDValidatorBase.java:479)
    at com.ctc.wstx.dtd.DTDValidator.validateAttribute(DTDValidator.java:251)
    at com.ctc.wstx.sr.AttributeCollector.validateAttribute(AttributeCollector.java:729)
    at com.ctc.wstx.sr.InputElementStack.resolveAndValidateElement(InputElementStack.java:535)
    at com.ctc.wstx.sr.BasicStreamReader.handleStartElem(BasicStreamReader.java:3059)
    at com.ctc.wstx.sr.BasicStreamReader.nextFromTree(BasicStreamReader.java:2919)
    at com.ctc.wstx.sr.BasicStreamReader.next(BasicStreamReader.java:1123)
    at so.test2.SOTest2.main(SOTest2.java:56)