代码之家  ›  专栏  ›  技术社区  ›  Kaypro II

如果输入文件中没有指定DTD,如何强制SAX解析器使用DTD?

  •  11
  • Kaypro II  · 技术社区  · 15 年前

    任何

    下面是我的场景的更多细节:

    有些使用命名字符实体而不声明doctype。

    我正在开发一个需要用Java解析这些文件的系统。目前,它正在处理上述情况,方法是首先以流的形式读取XML文档,尝试检测它是否定义了doctype,如果还没有doctype声明,则添加一个doctype声明。问题是这段代码有缺陷,我想用更干净的代码替换它。

    我不能使用基于DOM的解决方案 . 我也在尝试解决角色实体,所以 没有帮助 使用XML架构。

    如果你有一个解决方案,可以直接发布而不是链接到它吗?如果将来有一个正确的解决方案使用死链接,堆栈溢出就不会有多大好处。

    1 回复  |  直到 14 年前
        1
  •  2
  •   user1516873    12 年前

    我认为如果文档没有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
    }
    
    推荐文章