【问题标题】:Insert a doctype into an XML document (Java/ SAX)将 doctype 插入 XML 文档 (Java/SAX)
【发布时间】:2011-02-08 03:57:40
【问题描述】:

假设您有一个 XML 文档并假设您有 DTD,但文档本身实际上并未指定 DOCTYPE ...您将如何插入 DOCTYPE 声明,最好通过在解析器上指定它(类似如何为将要解析的文档设置架构)或通过XMLFilter 等插入必要的 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 的文档必须是有效的。有什么办法吗?我知道我可以修改原始流以添加 doctype 或可能进行转换以设置 DTD...还有其他选项吗?

【问题讨论】:

    标签: java xml sax


    【解决方案1】:

    您可以尝试 DoctypeChanger 按照您的建议修改原始流:

    DoctypeChanger 是一个 Java 类,当它被输入 XML 解析器时,它允许您从字节流中添加、修改或删除 DOCTYPE 声明。

    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.
    

    【讨论】:

      【解决方案2】:

      我会使用 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>
      

      【讨论】:

      • 刚刚在您的帖子底部看到您不想进行转换。对此感到抱歉。
      • 没问题... 这不一定是一个 解决方案,如果您尝试流式传输 XML 文档而不是将其缓冲在内存中,则它并不实用。我认为您可以使用 Transform API,请参阅:javax.xml.transform.Transformer.setOutputProperty() 和 javax.xml.transform.OutputKeys.DOCTYPE_SYSTEM。实际上,现在我朝那个方向看的更远了,也许 javax.xml.transform.sax.SAXResult 是我需要的?
      猜你喜欢
      • 2021-09-27
      • 2013-02-22
      • 1970-01-01
      • 2012-05-01
      • 1970-01-01
      • 2022-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多