使用动态命名空间进行XSLT转换

时间:2010-07-27 19:15:04

标签: xslt namespaces

我需要将XML文件转换为另一个XML文件,其中源文件的动态命名空间设置为xmlns="whatever"。我的XSLT在没有命名空间的情况下运行正常,但我没有得到命名空间的输出。如何将源文件的架构应用于目标文件?

所有帮助都表示赞赏,并提前致谢!

修改

我正在尝试将命名空间uri复制到生成的文件中:

<xsl:param name="schema">
    <xsl:value-of select="namespace-uri()" />
</xsl:param>

<xsl:element name="root" namespace="$schema">

我已经验证架构是否保持正确的值,但问题是程序似乎过于字面意思:

<root xmlns="$schema">

这是解决这个问题的正确方法吗?

编辑x2:

我实施了亚历杭德罗的建议:

<xsl:element name="root" namespace="{$schema}"/> 

这在大多数情况下都有效,除了我必须将命名空间放在每个元素上,否则我在结果中得到以下结构:

<root xmlns="NAMESPACE">
    <foo xmlns="">

除了将namespace={$schema}放在每一行之外,有没有办法用这个命名空间覆盖所有元素?赏金并接受最佳答案!

编辑x3: 更好的例子:

如果我这样做:

<xsl:element name="root" namespace="{namespace-uri()}>
  <xsl:element name="foo">
    <xsl:element name="bar">
    <!--etc-->
    </xsl:element>
  </xsl:element>
</xsl:element>

我明白了:

<root xmlns="NAMESPACE">
  <foo xmlns="">
    <bar>
    <!--etc-->
    </bar>
  </foo>
<root>

我想将它们全部放在命名空间NAMESPACE下,所以我做了:

<xsl:element name="root" namespace="{namespace-uri()}>
  <xsl:element name="foo" namespace="{namespace-uri()}>
    <xsl:element name="bar" namespace="{namespace-uri()}>
    <!--etc-->
    </xsl:element>
  </xsl:element>
</xsl:element>

然而,打字很难看,也很乏味。是否有更简单的方法将命名空间覆盖在所有元素上? (希望这能澄清我的需要)

3 个答案:

答案 0 :(得分:4)

假设您有这个XML输入:

<root xmlns="survivors"> 
  <louis/> 
  <francis/> 
</root> 

意味着每个元素都在默认命名空间下,其URI是“幸存者”。

正如Welbog所写,您可以选择francis元素:

/*/*[local-name()='francis']

/*[local-name()='root']/*[local-name()='francis']

但是,这也从这些XML输入中选择francis元素:

<root xmlns="survivors" xmlns:n="no-survivors"> 
  <louis/> 
  <n:francis/> 
</root> 

<root xmlns="survivors"> 
  <louis/> 
  <francis xmlns="no-survivors"/> 
</root> 

您还可以使用一些名称空间URI来强化谓词。但是,哪一个?选项可以是根元素的默认命名空间,如:

/*/*[local-name()='francis'][namespace-uri()=namespace-uri(/*)]

这肯定会使XPath表达非常冗长。

在XSLT 2.0中,您可以使用xsl:xpath-default-namespace属性,如:

<xsl:value-of select="/root/francis" xpath-default-namespace="survivors"/> 

但这对你的情况不利,因为你事先不知道URI。

编辑xsl:element的属性是AVT(属性值模板),所以你需要这个:

<xsl:element name="root" namespace="{$schema}"/> 

另外,我建议你将param声明为字符串数据类型(不像现在这样的RTF),如:

<xsl:param name="schema" select="namespace-uri()"/> 

编辑2 :也许我不清楚。在每种情况下,您都不需要xsl:element/@namespace。在您的声明之后,每个元素只在一个默认命名空间中,这个样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()">
        <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[local-name()='bar']">
        <xsl:element name="newbar" namespace="{namespace-uri()}">
                <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

使用此输入:

<root xmlns="whatever">
    <foo/>
    <bar/>
</root>

输出:

<root xmlns="whatever">
    <foo></foo>
    <newbar></newbar>
</root>

编辑2 :我告诉您,在复制元素时,您还要复制扩展QName所需的命名空间。所以,如果想改变这个:

<root xmlns="whatever">
    <foo/>
    <bar/>
</root>

进入这个:

<root xmlns="whatever">
    <foo>
        <bar/>
    </foo>
</root> 

您可以使用此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="*[1]|following-sibling::*[1]"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:1)

由于命名空间是XPath引用的任何元素的全名的一部分,并且由于您事先不知道源文件的命名空间,因此您必须使用元素的本地名称。重新访问而不是他们的全名。

假设你有一个像这样的源文件:

<root xmlns="survivors">
  <louis/>
  <francis/>
</root>

使用XSLT访问这些元素的一种方法是指定与源文件的默认命名空间匹配的命名空间:

<xsl:stylesheet
  version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:surv="survivors"
>
  <xsl:template match="surv:louis">
    <!-- ETC -->

当您知道命名空间时,这种方式有效。当知道命名空间时,可以使用XPath函数local-name()忽略它,如下所示:

<xsl:stylesheet
  version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:template match="*[local-name() = 'louis']">
    <!-- ETC -->

使用local-name()表示您可以忽略源文档中的命名空间。但是,如果在不同的命名空间中有多个具有相同本地名称的元素,则必须小心。它不是一个强大的解决方案,但如果你不能信任你的命名空间,那么你无论如何也没有那么多的选择。

我认为拥有变量名称空间本身就是一个更大的问题。如果这是你的控制,你应该纠正它。如果它不在你的控制之下,你应该推动纠正它。

答案 2 :(得分:0)

XSLT很少在真空中执行。它几乎总是其他应用程序的一部分,它加载XSLT,加载源文档,然后生成输出。

假设以上是您的方案,我不会直接使用XSLT解决此问题。相反,我使用标准XML技术来检查源XML文档并发现默认命名空间。然后,我将加载XSLT文档并使用字符串替换或其他一些技术来注入在变换中的适当点处生成的命名空间。然后我继续正常运行转换。

这将使您的XSLT编写和维护更加自然。我是XSLT的专家并经常使用它,这就是我解决它的方法。我无法想象必须经常使用local-name()比较的样式表的 ugliness 。太痛苦了。 (当然,在更复杂的情况下,可能别无选择。幸运的是,你的不是其中之一。)

如果你没有这个选择,我会同情。