我正在使用woodstox为XML文件实现StAX解析器。假设我的文件系统中的公共目录中有一个匹配DTD的有效XML文件。
/path/to/test.xml
/path/to/test.dtd
XML使用相对系统标识符声明引用其DTD,如下所示:
<!DOCTYPE test SYSTEM "test.dtd">
从验证的角度来看,一切似乎都很好。 (是吗?xmllint不会抱怨。)但是,当我尝试使用下面的代码解析文件时,woodstox会抛出 java.io.FileNotFoundException ,因为它无法找到相对的DTD文件。在我看来,实现尝试相对于工作目录而不是相对于XML文件对象访问DTD文件。
import java.io.FileInputStream;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
public class Test {
public static void main( String[] args ) throws Exception {
FileInputStream fileInputStream = new FileInputStream( args[0] );
XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
XMLStreamReader xsr = xmlInputFactory.createXMLStreamReader(fileInputStream);
while( xsr.hasNext() ) {
if( xsr.next() == XMLStreamConstants.DTD ) {
System.err.println( xsr.getText() );
}
}
}
}
答案 0 :(得分:3)
您将需要提供自己的XMLResolver
接口实现(在SAX世界中称为EntityResolver)以帮助解析器找到DTD。 XMLInputFactory
有setXMLResolver()
方法可以为您完成。
有关该主题的更多信息:
了解解析器需要解析SYSTEM URI时,了解一下究竟发生了什么,这也是一个好主意。例如,Woodstox有an internal (and a default) implementation of the XMLResolver
(以及a proxy between the SAX's EntityResolver
and a StAX XMLResolver
)。看看它对你的DTD“文件名”的作用,你会发现它为什么会这样运作。
答案 1 :(得分:1)
@Pavel Veller的回答是正确的。这是一个正在使用的具体示例:
/**
* Responsible for parsing the specified XML file and creating objects for
* insertion into the MySQL database.
*
* @author cameronhudson
*
*/
public class Parser {
/**
* Creates a new XMLStreamReader from the specified file.
*
* @param file The relative path of the file to load.
* @return An XMLStreamReader to be used for parsing.
*/
private static XMLStreamReader getXmlReader(String filename) {
// Initialize an XMLStreamReader
XMLStreamReader reader;
// Instantiate an XMLInputFactory and set an XMLResolver
XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setXMLResolver(new XMLResolver() {
@Override
public Object resolveEntity(String publicID, String systemID,
String baseURI, String namespace) throws XMLStreamException {
/*
* The systemID argument is the same dtd file specified in the xml file
* header. For example, if the xml header is <!DOCTYPE dblp SYSTEM
* "dblp.dtd">, then systemID will be "dblp.dtd".
*
*/
return Parser.filenameToStream(systemID);
}
});
// Get the XML file as an InputStream.
InputStream stream = Parser.filenameToStream(filename);
// Instantiate a new XMLStreamReader.
try {
reader = factory.createXMLStreamReader(stream);
} catch (XMLStreamException e) {
System.err.println(e);
return null;
}
return reader;
}
/**
* Converts a local resource filename into a path dependent on the runtime
* environment.
*
* @param filename The local path of the resource within /src/main/resources/.
* @return An input stream of the file.
*/
private static InputStream filenameToStream(String filename) {
return Thread.currentThread().getContextClassLoader()
.getResourceAsStream(filename);
}
}