【问题标题】:Dynamic Name Space动态名称空间
【发布时间】:2013-12-19 16:09:13
【问题描述】:

我是 XML 转换 (XSLT) 的新手。 我正在尝试找出我面临的命名空间问题并需要一些帮助。

详情如下:

XSLT 文件:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <Address xmlns="http://gov.co.uk">
            <xsl:variable name="NameSpace">http://gov.co.uk/Test/1</xsl:variable>
            <xsl:element name="Name" namespace="{$NameSpace}">
              <ForeName>Rex</ForeName>
              <MiddleName>XYZ</MiddleName>
            </xsl:element>
            <xsl:element name="street" namespace="{$NameSpace}">
              <DoorNo>123</DoorNo>
            </xsl:element>
        </Address>
    </xsl:template>
</xsl:stylesheet>

输出 XML:

<?xml version="1.0" encoding="utf-8"?>
<Address xmlns="http://gov.co.uk">
    <Name xmlns="http://gov.co.uk/Test/1">
        <ForeName xmlns="http://gov.co.uk">Rex</ForeName>
        <MiddleName xmlns="http://gov.co.uk">XYZ</MiddleName>
    </Name>
    <street xmlns="http://gov.co.uk/Test/1">
        <DoorNo xmlns="http://gov.co.uk">123</DoorNo>
    </street>
</Address>

如您所见,ForeName 的命名空间是 xmlns="http://gov.co.uk" 派生自根节点 Address,但我需要它派生自最近的父节点Name,即xmlns="http://gov.co.uk/test/1"。

我下面有这样的东西

<xsl:element name="ForeName" namespace="{$NameSpace}">REX</xsl:element>

但我不相信这一点,因为我的 XSLT 文件中有数百个元素,我需要像上面一样修改每个元素。

有什么办法可以强制子元素命名空间从其父节点派生?

【问题讨论】:

    标签: xml xslt xml-namespaces


    【解决方案1】:

    &lt;Address&gt;&lt;ForeName&gt;&lt;MiddleName&gt; 具有相同命名空间的原因是您将它们添加为文字结果元素。 文字元素获取样式表中其范围内的命名空间定义的副本。 在您的示例中,这是在&lt;Address xmlns="http://gov.co.uk"&gt; 行上完成的。 另一方面,使用 xslt 命令生成的元素(如&lt;xsl:element&gt;&lt;xsl:copy&gt;不要复制样式表中定义的命名空间。

    如果您需要将除&lt;Address&gt; 之外的所有文字元素放入命名空间http://gov.co.uk/Test/1 中,只需在&lt;xsl:stylesheet&gt; 元素中添加默认命名空间定义xmlns="http://gov.co.uk/Test/1" 并使用&lt;xsl:element name="Address" namespace="http://gov.co.uk"&gt; 创建&lt;Address&gt; 元素。

    如果您的文字元素有多个不同的命名空间,则将该命名空间定义添加到您的文字元素最近的父级中。例如使用&lt;xsl:element name="Address" namespace="http://gov.co.uk" xmlns="http://gov.co.uk/Test/1"&gt;&lt;Address&gt; 的所有文字后代放入http://gov.co.uk/Test/1 命名空间。

    【讨论】:

      【解决方案2】:

      这不是关于 XSLT,而是更多关于一般的命名空间。当您声明一个命名空间时,该节点和所有后续节点都处于活动状态

      因此,如果您希望 ForeNameMiddlename 的默认命名空间与父 Name 的默认命名空间相匹配,则只需不要在子元素上声明它,如下所示:

      <?xml version="1.0" encoding="utf-8"?>
      <Address xmlns="http://gov.co.uk">
          <Name xmlns="http://gov.co.uk/Test/1">
              <ForeName>Rex</ForeName>
              <MiddleName>XYZ</MiddleName>
          </Name>
          <street xmlns="http://gov.co.uk/Test/1">
              <DoorNo>123</DoorNo>
          </street>
      </Address>
      

      【讨论】:

      • 我认为他需要处理一个非静态命名空间。
      • @CarloCannas 我不太确定,但如果没有输入 XML 继续,很难说。 OP 采用的方法适用于使用属性值模板的动态命名空间,但似乎命名空间是固定的,OP 只是希望它级联到子元素,如图所示。
      【解决方案3】:

      有什么方法可以强制子元素命名空间从其父节点派生?

      不直接,最简单的方法可能是分两个阶段进行转换。首先进行当前的转换,将结果存储在一个变量中,然后重新处理该变量的内容以将任何非命名空间元素“移动”到作为参数的特定命名空间中:

      <xsl:param name="NameSpace" select="'http://gov.co.uk/Test/1'" />
      
      <xsl:template match="/">
        <xsl:variable name="stageOne">
          <!-- your current processing here -->
        </xsl:variable>
        <xsl:apply-templates mode="fix-ns" select="msxsl:node-set($stageOne)" />
      </xsl:template>
      
      <!-- copy everything as-is ... -->
      <xsl:template match="@*|node()" mode="fix-ns">
        <xsl:copy><xsl:apply-templates select="@*|node()" mode="fix-ns" /></xsl:copy>
      </xsl:template>
      
      <!-- ... except element in no namespace, which we move into $NameSpace -->
      <xsl:template match="*[namespace-uri() = '']" mode="fix-ns">
        <xsl:element name="{local-name()}" namespace="{$NameSpace}">
          <xsl:apply-templates select="@*|node()" mode="fix-ns" />
        </xsl:element>
      </xsl:template>
      

      如果您确实希望无命名空间节点继承其父节点的命名空间,而不是为它们都使用固定的命名空间,那么您可以将最终模板更改为更像

      <xsl:template match="*[namespace-uri() = '']" mode="fix-ns">
        <xsl:element name="{local-name()}" namespace="{namespace-uri(..)}">
          <xsl:apply-templates select="@*|node()" mode="fix-ns" />
        </xsl:element>
      </xsl:template>
      

      第三个选项 - 你提到你已经知道你可以通过在任何地方使用&lt;xsl:element&gt; 来做你需要的事情,但是你

      我的 XSLT 文件中有数百个元素,我需要像上面一样修改每个元素。

      请记住,XSLT 样式表本身就是一个 XML 文档,因此您可以使用如下所示的简单一次性样式表对现有样式表进行必要的编辑。注意使用xsl:namespace-alias输出XSLT标签:

      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
                      xmlns:out-xsl="urn:result" exclude-result-prefixes="out-xsl">
      
        <xsl:namespace-alias stylesheet-prefix="out-xsl" result-prefix="xsl" />
      
        <xsl:template match="@*|node()">
          <xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy>
        </xsl:template>
      
        <!-- match any literal result element <foo> which is not in a namespace -->
        <xsl:template match="xsl:template//*[namespace-uri() = '']">
          <!-- replace it with <xsl:element name="foo" namespace="{$NameSpace}"> -->
          <out-xsl:element name="{local-name()}" namespace="{{$NameSpace}}">
            <xsl:apply-templates select="@*|node()" />
          </out-xsl:element>
        </xsl:template>
      </xsl:stylesheet>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-20
        • 1970-01-01
        相关资源
        最近更新 更多