如何在解析文档时强制SAX解析器(特别是Java中的Xerces)在输入文档中没有任何 doctype时使用DTD?这甚至可能吗?
以下是我的方案的更多细节:
我们有一堆XML文档符合由多个不同系统生成的相同DTD(我无法更改)。其中一些系统会在其输出文档中添加doctype,而其他系统则不会。有些使用命名字符实体,有些则不使用。 有些使用命名字符实体而没有声明doctype。我知道这不是犹太教,但这是我必须使用的。
我正在研究需要用Java解析这些文件的系统。目前,它通过首先将XML文档作为流读取,尝试检测是否已定义doctype,并添加doctype声明(如果尚未存在)来处理上述情况。问题是这个代码有问题,我想用更清洁的东西替换它。
文件很大,所以我不能使用基于DOM的解决方案。我也在尝试解析字符实体,因此无法帮助使用XML Schema。
如果您有解决方案,请直接发布而不是链接到它?如果在将来有一个带有死链接的正确解决方案,那么Stack Overflow就不会很好。
答案 0 :(得分:2)
我认为如果文档没有DOCTYPE,那么设置DOCTYPE是没有理智的。正如你已经做的那样,可能的解决方案是写假的。如果您正在使用SAX,则可以使用此假InputStream并伪造DefaultHandler实现。 (仅适用于latin1单字节编码)
我知道这个解决方案也很难看,但只有一个适用于大数据流。
这是一些代码。
private enum State {readXmlDec, readXmlDecEnd, writeFakeDoctipe, writeEnd};
private class MyInputStream extends InputStream{
private final InputStream is;
private StringBuilder sb = new StringBuilder();
private int pos = 0;
private String doctype = "<!DOCTYPE register SYSTEM \"fake.dtd\">";
private State state = State.readXmlDec;
private MyInputStream(InputStream source) {
is = source;
}
@Override
public int read() throws IOException {
int bit;
switch (state){
case readXmlDec:
bit = is.read();
sb.append(Character.toChars(bit));
if(sb.toString().equals("<?xml")){
state = State.readXmlDecEnd;
}
break;
case readXmlDecEnd:
bit = is.read();
if(Character.toChars(bit)[0] == '>'){
state = State.writeFakeDoctipe;
}
break;
case writeFakeDoctipe:
bit = doctype.charAt(pos++);
if(doctype.length() == pos){
state = State.writeEnd;
}
break;
default:
bit = is.read();
break;
}
return bit;
}
@Override
public void close() throws IOException {
super.close();
is.close();
}
}
private static class MyHandler extends DefaultHandler {
@Override
public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException {
System.out.println("resolve "+ systemId);
// get real dtd
InputStream is = ClassLoader.class.getResourceAsStream("/register.dtd");
return new InputSource(is);
}
... // rest of code
}