使用StaX和DTD验证标记

时间:2016-11-24 15:46:28

标签: java xml dtd stax

我有一个简单的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

1 个答案:

答案 0 :(得分:1)

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

尝试将带有系统标识符的doctype声明添加到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实现,因为默认情况下不支持验证。

与该问题的答案中建议的一样,我尝试使用woodstoxwoodstox 5.0.3 jarstax2 api 4.0.0 jar添加到我的类路径中(并将带有系统标识符的doctype添加到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)