进行DOM节点到字符串转换,但存在名称空间问题

Ti *_*rga 9 java xml

所以我们有一个带有自定义命名空间的XML文档.(XML是由我们无法控制的软件生成的.它由名称空间 - 不知道的 DOM解析器解析;标准的Java7SE/Xerces东西,但也在我们的有效控制之外.)输入数据如下所示:

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<MainTag xmlns="http://BlahBlahBlah" xmlns:CustomAttr="http://BlitherBlither">
    .... 18 blarzillion lines of XML ....
    <Thing CustomAttr:gibberish="borkborkbork" ... />
    .... another 27 blarzillion lines ....
</MainTag>
Run Code Online (Sandbox Code Playgroud)

我们得到的文档是可用的,xpath可查询和可遍历等等.

将此文档转换为用于写入数据接收器的文本格式使用标准的变换器方法,如"我如何将XML文档更改为Java字符串?"中所述.问题:

Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter stringwriter = new StringWriter();
transformer.transform (new DOMSource(theXMLDocument), new StreamResult(stringwriter));
return stringwriter.toString();
Run Code Online (Sandbox Code Playgroud)

它完美无缺.

但现在我想将单个任意节点从该Document转换为字符串.一个DOMSource构造函数接收节点的指针一样的,因为它接受一个Document(实际上文件只是节点的子类,所以它是相同的API,据我可以告诉).因此,在上面的代码片段中的"theXMLDocument"中传递一个单独的节点非常有用......直到我们到达Thing.

那时,transform()抛出一个异常:

java.lang.RuntimeException: Namespace for prefix 'CustomAttr' has not been declared.
    at com.sun.org.apache.xml.internal.serializer.SerializerBase.getNamespaceURI(Unknown Source)
    at com.sun.org.apache.xml.internal.serializer.SerializerBase.addAttribute(Unknown Source)
    at com.sun.org.apache.xml.internal.serializer.ToUnknownStream.addAttribute(Unknown Source)
    ......
Run Code Online (Sandbox Code Playgroud)

那讲得通.("com.sun.org.apache"很难阅读,但无论如何.)这是有道理的,因为自定义属性的命名空间是在根节点声明的,但现在变换器从子节点开始并且可以在树中看到"在它上面"的声明.所以我认为我理解这个问题,或者至少是症状,但我不知道如何解决它.

  • 如果这是一个String-to-Document转换,我们将使用一个DocumentBuilderFactory实例并且可以调用.setNamespaceAware(false),但这是另一个方向.

  • 没有可用的属性transformer.setOutputProperty()影响namespaceURI查找,这是有道理的.

  • 没有这样的相应setInputProperty或类似的功能.

  • 输入解析器不是名称空间感知,这就是"上游"代码如何创建其文档交给我们.我不知道如何将特定的状态标志交给转换代码,这是我真正想做的事情.

  • 相信有可能(以某种方式)xmlns:CustomAttr="http://BlitherBlither"向Thing节点添加一个属性,与根MainTag相同.但是在那时,输出不再是与读入的XML相同的XML,即使它"意味着"相同的东西,并且文本字符串最终将在未来进行比较.我们不知道在异常被抛出之前是否需要它,然后我们可以添加它并再试一次...... ick.就此而言,更改Node会改变原始Document,这实际上应该是一个只读操作.

建议吗?是否有某种方式告诉变形金刚,"看,不要强调你的笨拙小脑袋是否输出是单独的合法XML,它不会被自己解析(但你不知道) ,只是制作文本,让我们担心它的背景"?

mar*_*rty 6

鉴于你引用的错误消息"前缀'CustomAttr'的命名空间尚未声明.",我假设你的伪代码是这样的:

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<MainTag xmlns="http://BlahBlahBlah" xmlns:CustomAttr="http://BlitherBlither">
    .... 18 blarzillion lines of XML ....
    <Thing CustomAttr:attributeName="borkborkbork" ... />
    .... another 27 blarzillion lines ....
</MainTag>
Run Code Online (Sandbox Code Playgroud)

有了这个假设,这是我的建议:所以你想从"大"XML中提取"Thing"节点.标准方法是使用一点XSLT来做到这一点.您准备XSL转换:

Transformer transformer = transformerFactory.newTransformer(new StreamSource(new File("isolate-the-thing-node.xslt")));
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setParameter("elementName", stringWithCurrentThing);    // parameterize transformation for each Thing
...
Run Code Online (Sandbox Code Playgroud)

编辑:@Ti,请注意上面的参数化指令(以及下面的xslt).

文件'isolate-the-thing-node.xslt'可能是以下内容:

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:custom0="http://BlahBlahBlah"
    xmlns:custom1="http://BlitherBlither"
    version="1.0">
    <xsl:param name="elementName">to-be-parameterized</xsl:param>
    <xsl:output encoding="utf-8" indent="yes" method="xml" omit-xml-declaration="no" />

    <xsl:template match="/*" priority="2" >
            <!--<xsl:apply-templates select="//custom0:Thing" />-->
            <!-- changed to parameterized selection: -->
            <xsl:apply-templates select="custom0:*[local-name()=$elementName]" />
    </xsl:template>

    <xsl:template match="node() | @*" priority="1">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*" />
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)

希望能让你超越"东西"的东西:)