解析XML并获取没有绑定名称空间的DOM树 - Java

时间:2011-09-07 16:23:07

标签: java xml dom xslt sax

我有一个类似XML的文件:

<p>something</p>
<ac:image>
    <ri:attachment ri:filename="IMAGE.PNG" />
</ac:image>
<ac:macro ac:name="screenshot">
    <ac:default-parameter>IMAGE.ss</ac:default-parameter>
</ac:macro>
<p>something</p>

我需要使用XSLT模板对其进行转换 - 我想将所有<ac:image>替换为<ac:macro ac:name="screenshot">。通常,解析和转换格式良好且众所周知的XML非常容易。我的情况完全不同。

如您所见,它没有根元素和XML序言。但这不是问题,我可以添加<?xml version="1.0"?>并使用任意元素(如<root>)包装内容以避免异常:

Caused by: org.jdom.input.JDOMParseException: Error on line 1: Content is not allowed in prolog.

示例XML包含三个名称空间 - 默认,acri。由于代码将在客户指定的内容上运行,因此可能还有一些我不了解的其他命名空间。在解析XML之前,我无法绑定所有名称空间,因此我遇到了一个异常:

Caused by: org.xml.sax.SAXParseException: Content is not allowed in prolog.

我在Internet上找到了SAX解析器能够以不解析命名空间的模式解析XML的地方。在默认模式下,您获得namespace=acelement=macro,而在非命名空间模式下,您没有获得名称空间和element=ac:macro。这是理想的。您只需在解析器上设置SAX功能:namespaces=falsenamespace-prefixes=true

final XMLReader sax = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
sax.setFeature("http://xml.org/sax/features/validation", false);
sax.setFeature("http://xml.org/sax/features/namespaces", false);
sax.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
sax.parse(new InputSource(new StringReader(content))); // parse returns void

它不会抛出任何异常,因此看起来XML被解析而没有错误。但是,我需要一个DOM树,以便我可以使用XSLT对其进行转换。让我们使用JDOM:

// all classes are org.jdom.*
final SAXBuilder sax = new SAXBuilder(false); // validate=false
sax.setFeature("http://xml.org/sax/features/namespaces", false);
sax.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
final Document document = sax.build(new StringInputStream(content));

不幸的是,我得到了一个例外:

Caused by: org.jdom.IllegalNameException: The name "" is not legal for JDOM/XML elements: XML names cannot be null or empty.
    at org.jdom.Element.setName(Element.java:206)
    at org.jdom.Element.<init>(Element.java:140)
    at org.jdom.Element.<init>(Element.java:152)
    at org.jdom.DefaultJDOMFactory.element(DefaultJDOMFactory.java:138)
    at org.jdom.input.SAXHandler.startElement(SAXHandler.java:511)
    at org.apache.xerces.parsers.AbstractSAXParser.startElement(Unknown Source)
    at org.apache.xerces.impl.dtd.XMLDTDValidator.startElement(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanStartElement(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentScannerImpl$ContentDispatcher.scanRootElementHook(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
    at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
    at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
    at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
    at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
    at org.jdom.input.SAXBuilder.build(SAXBuilder.java:453)
    at org.jdom.input.SAXBuilder.build(SAXBuilder.java:770)
    at com.screensnipe.confluence.macro.XhtmlImageMacroReplacer.replaceImageMacroInText(XhtmlImageMacroReplacer.java:118)

JDOM抱怨非法标记名<>。我当然没有这样的。看起来JDOM在SAXHandler.java:511中有一个错误,element = factory.element(localName);应该是element = factory.element(qName);

我也尝试过XOM。 XOM does not work with "namespaces" feature set to false

我也试过TagSoup库。我不喜欢它,因为它弄乱了输出XML。添加XML prolog和根元素不是问题。弄乱名称空间是。

<?xml version="1.0"?>
<html xmlns="http://www.w3.org/1999/xhtml">
    <body>
        <p>something</p>
        <ac:image xmlns:ac="urn:x-prefix:ac"> <!-- :( -->
             <ri:attachment xmlns:ri="urn:x-prefix:ri" ri:filename="IMAGE.PNG" />
        </ac:image>
        ...

问题是:如何从我的XML获取DOM树? (Java)无需编写我的JDOM版本。我很感激一个有效的解决方案。只需解析并获取DOM树。不像TagSoup库那样破坏名称空间的树。

或更多以目标为中心的问题:如何在不触及其他代码的情况下将<ac:image>替换为<ac:macro ac:name="screenshot">? (Java)所有其他标签,名称空间或其他应该不受影响。 (不建议使用任何正则表达式)

1 个答案:

答案 0 :(得分:1)

如果您愿意像添加周围的根元素那样进行预处理,那么您也可以查看XML文件中的名称空间前缀,并为每个添加到您添加的根元素的虚拟声明添加

然后,您将不需要可以被告知不解析名称空间前缀的解析器。