如果在输入文档中没有任何文字类型的情况下解析文档时,如何强制使用SAX解析器(特别是
Java中的Xerces)来使用DTD?这是甚么可能吗?
以下是我的场景的更多细节:
我们有一堆XML文档符合由多个不同系统生成的相同DTD(我不能更改).其中一些系统在其输出文档中添加了一个doctype,另一些则不会.有些使用命名字符实体,有些不行.有些使用命名字符实体,而不声明一个doctype.我知道这不是犹太教,但这是我必须合作的.
我正在使用需要在Java中解析这些文件的系统.目前,它正在处理上述情况,首先将XML文档作为流读取,尝试检测是否定义了一个doctype,如果尚未存在,则添加一个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 }