<?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>
我们得到的文档是可用的,xpath可查询和可遍历等等.
将此文档转换为用于写入数据接收器的文本格式使用一百个SO中描述的标准Transformer方法“如何将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();
它完美无缺.
但现在我想将该文档中的单个任意节点转换为字符串. DOMSource构造函数接受的Node指针与它接受Document一样(实际上Document只是Node的子类,所以它就是我所知道的相同的API).因此,在上面的代码片段中的“theXMLDocument”中传递一个单独的Node非常有用……直到我们到达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) ......
那讲得通. (“com.sun.org.apache”很难阅读,但无论如何.)这是有道理的,因为自定义属性的命名空间是在根节点声明的,但现在变换器从子节点开始并且可以在树中看到“在它上面”的声明.所以我认为我理解这个问题,或者至少是症状,但我不知道如何解决它.
>如果这是一个String-to-Document转换,我们将使用DocumentBuilderFactory实例并可以调用.setNamespaceAware(false),但这是另一个方向.
> transform.setOutputProperty()的所有可用属性都不会影响namespaceURI查找,这是有意义的.
>没有相应的setInputProperty或类似函数.
>输入解析器不支持名称空间,这就是“上游”代码如何创建其文档交给我们.我不知道如何将特定的状态标志交给转换代码,这是我真正想做的事情.
>我相信有可能(以某种方式)将一个xmlns:CustomAttr =“http:// BlitherBlither”属性添加到Thing节点,与根MainTag相同.但是在那时,输出不再是与读入的XML相同的XML,即使它“意味着”相同的东西,并且文本字符串最终将在未来进行比较.我们不知道在异常被抛出之前是否需要它,然后我们可以添加它并再试一次…… ick.就此而言,更改Node会改变原始Document,这实际上应该是一个只读操作.
建议吗?是否有某种方式告诉变形金刚,“看,不要强调你的笨拙小脑袋是否是单独的输出是合法的XML,它不会被自己解析(但你不知道),只是生成文本,让我们担心它的背景“?
解决方法
我假设你的伪代码是这样的:
<?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>
有了这个假设,这是我的建议:
所以你想从“大”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 ...
编辑:@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>
希望能让你超越“东西”的东西:)