xslt 不会选择在 XSLT 转换中动态更改名称空间以进行进一步转换

Posted

技术标签:

【中文标题】xslt 不会选择在 XSLT 转换中动态更改名称空间以进行进一步转换【英文标题】:Changing namespace dynamically in XSLT transformation does not picked by xslt for further transformation 【发布时间】:2013-06-01 05:01:10 【问题描述】:

XSLT 示例:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:xslnsv="http://sample2.1">
<xsl:output method="xml" indent="yes"/>

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

<xsl:template match="//xslnsv:Activity">
  <xsl:copy>
    <xsl:copy-of select="@*" />
    <xsl:if test="not(@IsForCompensation) 
      and (./xslnsv:IsForCompensationSpecified)">
      <xsl:attribute name="IsForCompensation">
        <xsl:value-of 
          select="./xslnsv:IsForCompensationSpecified" />
      </xsl:attribute>
    </xsl:if>
    <xsl:apply-templates
      select="@*|node()[local-name() 
        != 'IsForCompensationSpecified']" />
  </xsl:copy>
</xsl:template>    
</xsl:stylesheet>

这里我们有一个命名空间 xmlns:xslnsv="http://sample2.2" 当我们有一个具有相同命名空间的 xml 时,它可以工作

<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://sample2.2" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
  <ElementAtLevel1>
    <ElementAtLevel2 Id="cf9d2" Name="Pool 1">    
      <Activities>
        <Activity Id="ef84125a">          
          <IsForCompensationSpecified
            >false</IsForCompensationSpecified>
        </Activity>
        <Activity Id="39c5b8d8" Name="Task 1">
          <IsForCompensationSpecified 
            >true</IsForCompensationSpecified>
        </Activity>
      </Activities>
    </ElementAtLevel2>  
  </ElementAtLevel1>
  <ExtendedAttributes />
</Package>

产生输出为:

<?xml version="1.0"?>
<Package xmlns="http://sample2.2" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ElementAtLevel1>
    <ElementAtLevel2 Id="cf9d267d-e1ed-4616-adfb-d24d6844f775"
                     Name="Pool 1">    
      <Activities>
        <Activity Id="ef84125a-0a01-4d76-9b3b-413ffb3c7a74"    
                  IsForCompensation="false"/>
        <Activity Id="39c5b8d8-9a72-40d1-b3e4-8cd973ccdf03" 
                  Name="Task 1" 
                  IsForCompensation="true"/>
      </Activities>
    </ElementAtLevel2>  
  </ElementAtLevel1>
  <ExtendedAttributes/>
</Package>

但问题是: 我们有一些具有不同命名空间的 xml,即 http://sample2.1 具有不同命名空间的示例 xml

<?xml version="1.0"?>
<Package xmlns="http://sample2.1" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ElementAtLevel1>
    <ElementAtLevel2 Id="cf9d267d-e1ed-4616-adfb-d24d6844f775" 
                     Name="Pool 1">    
      <Activities>
        <Activity Id="ef84125a-0a01-4d76-9b3b-413ffb3c7a74" 
                  IsForCompensation="false"/>
        <Activity Id="39c5b8d8-9a72-40d1-b3e4-8cd973ccdf03" 
                  Name="Task 1" 
                  IsForCompensation="true"/>
      </Activities>
    </ElementAtLevel2>  
  </ElementAtLevel1>
  <ExtendedAttributes/>
</Package>

那么我们没有正确的输出。

<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://sample2.1" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
  <ElementAtLevel1>
    <ElementAtLevel2 Id="cf9d2" Name="Pool 1">    
      <Activities>
        <Activity Id="ef84125a">
          <IsForCompensationSpecified
            >false</IsForCompensationSpecified>
        </Activity>
        <Activity Id="39c5b8d8" Name="Task 1">
          <IsForCompensationSpecified
            >true</IsForCompensationSpecified>
        </Activity>
      </Activities>
    </ElementAtLevel2>  
  </ElementAtLevel1>
  <ExtendedAttributes />
</Package>

我修改了 xslt 以动态更改命名空间。 带有新更改的示例 xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:xslnsv="http://sample2.2" >

  <xsl:output method="xml" indent="yes"/>
  <xsl:variable name="vUrl" select="'http://sample2.2'"/>

  <xsl:template match="*[namespace-uri()='http://sample2.1']">
    <xsl:element name="name()" namespace="$vUrl">
      <xsl:copy-of select="@*"/>
       <xsl:apply-templates/>
    </xsl:element>
  </xsl:template> 
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
 <xsl:template match="//xslnsv:Activity">
   <xsl:copy>
     <xsl:copy-of select="@*" />
     <xsl:if test="not(@IsForCompensation) 
                   and (./xslnsv:IsForCompensationSpecified)">
       <xsl:attribute name="IsForCompensation">
         <xsl:value-of 
           select="./xslnsv:IsForCompensationSpecified" />
       </xsl:attribute>
     </xsl:if>
     <xsl:apply-templates select="@*
       |node()[local-name() != 'IsForCompensationSpecified']" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

在我看来,它可以更改命名空间,但在更改命名空间后无法选择元素。可能是指来自源 xml 的旧命名空间,即 2.1

但我仍然没有得到正确的输出;我得到以下输出。

<?xml version="1.0"?>
 <Package xmlns="http://sample2.1" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <ElementAtLevel1>
 <ElementAtLevel2 Id="cf9d2" Name="Pool 1">    
 <Activities>
 <Activity Id="ef84125a">          
 <IsForCompensationSpecified>false</IsForCompensationSpecified>
 </Activity>
 <Activity Id="39c5b8d8" Name="Task 1">
 <IsForCompensationSpecified>true</IsForCompensationSpecified>
 </Activity>
 </Activities>
 </ElementAtLevel2>  
 </ElementAtLevel1>
 <ExtendedAttributes/>
 </Package>

【问题讨论】:

您似乎对 namespacenamespace prefix 感到困惑。至少在您的第一个示例中,“ns0”是一个命名空间前缀。但在第二个示例中,“ns0”和“ns1”用作 uri。所以这让我很困惑。 谢谢你是对的,我已经更新了 当您说“它无法选择命名空间并因此没有发生转换”时,您的意思是处理器不输出任何 XML 吗?或者您认为它运行的是身份模板而不是名称空间更改模板?请显示实际的输出 XML 以及相应的输入 XML,以便我们诊断正在发生的事情.. 我已经更新了 xslt 和 xml 【参考方案1】:

[根据 OP 对问题的修订进行修订。]

如果我对您的理解正确,您希望如果在输入中遇到命名空间http://sample2.1 中带有本地名称Activity 的元素,那么(1)带有match="*[namespace-uri()='http://sample2.1']" 的模板将匹配它并将其移动到命名空间@987654322 @,然后 (2) 带有match="//xslnsv:Activity" 的模板将被触发。这样理解正确吗?

如果是这样,这里有两个问题。

首先,命名空间更改模板在命名空间http://sample2.2 中生成一个新元素节点,但您显示的代码中没有任何内容尝试将任何模板应用于该新元素节点。

第二个问题是 XSLT 1.0 模板只匹配输入文档中的元素;它们不会也不能匹配样式表构造的节点。这是 XSLT 1.0 和 XSLT 2.0 之间的重大区别之一。 XSLT 1.0 的一个通用扩展允许创建的节点与模板匹配;如果您想尝试,请查找有关节点集扩展的信息。

一个更简单的解决方案是将您的样式表一分为二:一个将元素从旧命名空间移动到新命名空间,第二个用于处理新命名空间中的元素。

(我还应该注意,我无法重现您的结果。当我根据您提供的输入运行您提供的样式表时,我在命名空间http://sample2.2 中得到输出,而不是在命名空间http://sample2.1 中。我假设你有是复制/粘贴错误的受害者。)

【讨论】:

以上是关于xslt 不会选择在 XSLT 转换中动态更改名称空间以进行进一步转换的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 XSLT 替换 XML 节点名称中的字符 - 更改根元素

为啥在 XSLT 转换期间出现此错误:XSLT 转换失败?

XslCompiledTransform 中的 XSLT 复制和排序转换引发异常

如何在 XSLT 输出中控制名称空间前缀(特别是默认名称空间)?

为啥我的标签在 XSLT 转换后展开(以及如何修复)?

XSLT 转换 XML:选择 uuid 并按顺序排序