使用命名空间时无法在 XSLT 中复制和修改属性

Posted

技术标签:

【中文标题】使用命名空间时无法在 XSLT 中复制和修改属性【英文标题】:Unable to copy and modify attribute in XSLT when using a namespace 【发布时间】:2015-06-05 20:23:18 【问题描述】:

我正在尝试转换 XML 文档并修改单个元素的属性,但如果根元素具有命名空间属性,则不会应用转换。只需删除 xmlns 就可以很好地使用我的代码。

我的 XML:

<?xml version="1.0"?>
<BIDomain xmlns="http://www.oracle.com/biee/bi-domain">
  <BIInstance name="coreapplication">
    <SecurityOptions sslManualConfig="false" sslEnabled="false" ssoProvider="Custom" ssoEnabled="false">
      <SecurityService>
        <EndpointURI>bisecurity/service</EndpointURI>
      </SecurityService>
    </SecurityOptions>
  </BIInstance>
</BIDomain>

使用的 XSL:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" standalone="yes" />

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

  <!-- Add the new attributes -->
  <xsl:template match="SecurityOptions">
    <xsl:copy>
            <xsl:attribute name="ssoProviderLogoffURL"/>
            <xsl:attribute name="ssoProviderLogonURL"/>
            <xsl:attribute name="sslVerifyPeers">
                <xsl:value-of select="'false'" />
            </xsl:attribute>           
            <xsl:apply-templates  select="node() | @*"/>
        </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

最终结果是相同的 XML。如果我从根元素中删除命名空间定义 &lt;BIDomain xmlns="http://www.oracle.com/biee/bi-domain"&gt; 转换正常应用。我假设我做错了什么并且命名空间属性干扰了匹配。

有什么想法吗?

【问题讨论】:

【参考方案1】:

您尝试匹配的元素位于命名空间(默认命名空间)中,因此您需要在 XSLT 中正确使用命名空间:

<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:bi="http://www.oracle.com/biee/bi-domain">
                  <!--   ^----- here   -->
  <xsl:output method="xml" version="1.0" standalone="yes" />

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

  <!-- Add the new attributes -->
                <!--   v------- and here   -->
  <xsl:template match="bi:SecurityOptions">
    <xsl:copy>
      <xsl:attribute name="ssoProviderLogoffURL"/>
      <xsl:attribute name="ssoProviderLogonURL"/>
      <xsl:attribute name="sslVerifyPeers">
        <xsl:value-of select="'false'" />
      </xsl:attribute>           
      <xsl:apply-templates  select="node() | @*"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

【讨论】:

打败我。为了清楚起见,我要补充一点:很容易假设默认命名空间意味着您不需要命名空间限定 XSLT 中的元素 - 但您确实这样做了。您可以通过将 http://www.oracle.com/biee/bi-domain 设置为 XSLT 的默认 xmlns 来实现这一点,但这会让人很困惑...... @DanField 在 XSLT 中使用默认命名空间不会消除在 match=""select="" 属性中为其声明和使用前缀的需要。 XPath 1.0 不允许非空默认命名空间。 规范可能不允许这样做,但各种实现都可以。例如,请参阅xsltransform.net/6qVRKwH。无论如何,这将是非常糟糕的做法。我更多是为了强调这个想法(当你不加前缀时,它假设它在默认命名空间中) @DanField 是的,我知道你的主要观点是什么。您链接到的那个处理器工作正常 - SecurityOptions 模板只是未使用。您可以看到这一点,因为结果中没有 ssoProviderLogoffURL 属性。 你说得对。我忽略了那部分。谢谢。【参考方案2】:

xmlns 的工作原理是所有节点都从其父节​​点继承 xmlns 属性。这意味着,除非另有说明,否则当您的文档根目录包含 xmlns="http://www.oracle.com/biee/bi-domain" 它将该名称空间应用于所有子树。

因此,您实际上是在寻找命名空间为 "http://www.oracle.com/biee/bi-domain"SecurityOptions 标记。

这意味着您的 XSLT 实际上需要有这样的东西:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tmp="http://www.oracle.com/biee/bi-domain">

在顶部,模板匹配如下所示:

<xsl:template match="tmp:SecurityOptions">

注意 tmp: 匹配 xmlns:tmp;这称为命名空间前缀,允许 xml 将小字符串 tmp 匹配到大字符串 "http://www.oracle.com/biee/bi-domain"

【讨论】:

谢谢,就是这样。但是我标记了以前的答案,因为他发布得更快。

以上是关于使用命名空间时无法在 XSLT 中复制和修改属性的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 xslt 将父节点命名空间复制到子元素?

xslt 中的 XML 命名空间处理

xslt copy-of到结果文档中的不同命名空间

XSLT 匹配和命名空间 [重复]

XSLT:复制 <mathml> 的所有子元素,不带 m: 命名空间,而无需为每个元素创建模板

xsd:any 元素的命名空间前缀并使用 XSLT 添加命名空间前缀