2010-03-09 39 views
10

如何在解析文檔時強制SAX解析器(特別是Java中的Xerces)使用DTD,而在輸入文檔中沒有任何文檔類型?這甚至有可能嗎?如果沒有在輸入文件中指定一個,我如何強制SAX解析器使用DTD?

這裏是我的方案的一些細節:

我們有一堆符合由多個不同的系統(其中沒有我可以改變)產生相同的DTD XML文檔。其中一些系統將doctype添加到他們的輸出文檔中,而其他系統則不會。有些使用命名字符實體,有些則不。 一些使用命名字符實體而不聲明文檔類型。我知道這不是猶太教,但這是我必須與之合作。

我正在研究需要使用Java解析這些文件的系統。目前,它通過首先在XML文檔中讀取一個流來處理上述情況,嘗試檢測是否定義了文檔類型,並添加一個doctype聲明(如果尚不存在)。問題是這個代碼有問題,我想用更乾淨的東西替換它。

文件很大,所以我不能使用基於DOM的解決方案。我也在嘗試解析字符實體,所以它不幫助使用XML Schema。

如果你有一個解決方案,你可以直接發佈它,而不是鏈接到它?它不會堆棧溢出很好,如果將來有一個正確的解決方案與死鏈接。

回答

1

我認爲設置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 
}