I have a simple XML file and a simple DTD representing it.
I am working with the assumption that it is feasible, albeit maybe not suitable, to validate markup using a DTD (as opposed to an actual schema, which I believe is the most powerful and suitable option).
Based on this assumption, I figured I'd give it a try with StaX.
Problem
StaX doesn't seem to validate anything even when provided a valid DTD, and the XML file does not match the schema defined in the DTD.
I was expecting the code below to fail with some validation-related stack trace, instead of printing all the element names in my foo.xml file.
I figure there might be something very wrong with my assumptions, but I couldn't find any specific explanation in the relevant SO questions I browsed, nor in other online literature.
Code and resources below.
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>
code
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) {}
}
}
Output (was expecting some stack trace)
foo
bar
bar
The first issue I can see is that you're testing the systemID ("foo.dtd".equals(systemID)
) but your XML file doesn't have a system identifier pointing to the DTD.
Try adding a doctype declaration with a system identifier to your 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>
Also, according to this question, you will need to use a different StAX implementation as the default doesn't support validation.
Like suggested in the answer to that question, I tried using woodstox by adding the woodstox 5.0.3 jar and the stax2 api 4.0.0 jar to my classpath (and adding the doctype with the system identifier to the XML) and got the expected exception:
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)