2010-04-14 101 views
4

想象一下,您有一個XML文檔,並想象您擁有DTD,但文檔本身並未實際指定DOCTYPE ......您如何插入DOCTYPE聲明,最好通過在解析器上指定它(類似於您可以如何設置將被解析的文檔的架構),或通過XMLFilter等插入必要的SAX事件?將文檔類型插入XML文檔(Java/SAX)

我發現許多人提到EntityResolver,但是這是什麼調用一次DOCTYPE在分析過程中發現和它是用來指向本地DTD文件。 EntityResolver2似乎有我在找什麼,但我還沒有找到任何使用的例子。

這是我來迄今最接近:(代碼就是Groovy,但足夠接近,你應該能夠理解吧...)

import org.xml.sax.* 
import org.xml.sax.ext.* 
import org.xml.sax.helpers.* 

class XmlFilter extends XMLFilterImpl { 
    public XmlFilter(XMLReader reader) { super(reader) } 

    @Override public void startDocument() { 
     super.startDocument()   
     super.resolveEntity(null, 
      'file:///./entity.dtd') 
     println "filter startDocument" 
    } 
} 

class MyHandler extends DefaultHandler2 { 
    public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) { 
     println "entity: $name, $publicId, $baseURI, $systemId" 
     return new InputSource(new StringReader('<!ENTITY asdf "&#161;">')) 
    } 
} 

def handler = new MyHandler() 

def parser = XMLReaderFactory.createXMLReader() 
parser.setFeature 'http://xml.org/sax/features/use-entity-resolver2', true 
def filter = new XmlFilter(parser) 
filter.setContentHandler(handler) 
filter.setEntityResolver(handler) 

filter.parse(new InputSource(new StringReader('''<?xml version="1.0" ?> 
    <test>one &asdf; two! &nbsp; &iexcl;&pound;&cent;</test>'''))); 

我看到resolveEntity叫,但仍創下

org.xml.sax.SAXParseException:實體「asdf」被引用,但未聲明。
在com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1231)
在org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333)

我想這是因爲沒有辦法添加解析器知道的SAX事件,我只能通過解析器上游的過濾器添加事件,這些過濾器傳遞給ContentHandler。所以文檔必須有效進入XMLReader。任何方式在這個?我知道我可以修改原始流以添加文檔類型或可能做一個轉換來設置DTD ...任何其他選項?

回答

1

你可以嘗試DoctypeChanger如你所說,其修改原始數據流:

DoctypeChanger是一個Java類,可以讓你從一個字節流中添加,修改或刪除DOCTYPE聲明,因爲它被送到到一個XML解析器中。

InputStream in = ... // get your XML InputStream 
DOCTYPEChangerStream changer = new DOCTYPEChangerStream(in); 
changer.setGenerator( 
    new DoctypeGenerator() { 
     public Doctype generate(Doctype old) { 
      return new DoctypeImpl("rootElement", "pubId", "sysId", "internalSubset"); 
     } 
    } 
); 
// .. and pass it on to the parser. 
1

我會使用xslt樣式表進行標識轉換,並使用xsl:output元素以及doctype-system屬性(如果我想添加公共標識符,則使用doctype-public)。

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
<xsl:output doctype-system="test.dtd"/> 
    <xsl:template match="node()|@*"> 
     <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 
+0

在你不想做一個改變你的文章的底部剛剛看到。對於那個很抱歉。 – 2010-04-17 06:53:20

+0

沒問題...這不一定是_bad_解決方案,如果您嘗試流式傳輸XML文檔而不是將其緩存在內存中,則不太實用。 我想你可以使用Transform API,請參閱:javax.xml.transform.Transformer.setOutputProperty()和javax.xml.transform.OutputKeys.DOCTYPE_SYSTEM。實際上,現在我朝這個方向看起來更遠了一點,也許我需要一個javax.xml.transform.sax.SAXResult? – 2010-04-19 13:14:54