XSLT 仅当元素不存在时插入元素

2024-04-21

我有一个源文件:

<?xml version="1.0"?>
<source>
  <ItemNotSubstituted/>
  <ItemToBeSubstituted Id='MatchId' />
</source>

以及包含我想要替换到源中的内容的样式表:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" method="xml" omit-xml-declaration="no" version="1.0"/>
  <xsl:preserve-space elements="//*"/>

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

  <xsl:template match="ItemToBeSubstituted[@Id = 'MatchId']">
    <xsl:copy>
      <xsl:copy-of select="@*|*"/>
      <Element1/>
      <Element2 Value="foo"/>
      <Element3 Value="bar"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

此样式表已成功复制<Element1/><Element2 Value="foo"/><Element3 Value="bar"/> into ItemToBeSubstituted。但是当我使用不同的源文档时,其中ItemToBeSubstituted已有内容:

<?xml version="1.0"?>
<source>
  <ItemNotSubstituted/>
  <ItemToBeSubstituted Id='MatchId'>
    <Element3 Value="baz"/>
  </ItemToBeSubstituted>
</source>

我得到这个输出:

<?xml version="1.0"?>
<source>
  <ItemNotSubstituted/>
  <ItemToBeSubstituted Id="MatchId">
    <Element3 Value="baz"/>
    <Element1/>
    <Element2 Value="foo"/>
    <Element3 Value="bar"/>
  </ItemToBeSubstituted>
</source>

我只想替换样式表中源文档中尚不存在的元素。这是将样式表应用于第二个文档后我正在寻找的输出,仅包含<Element3>源文档中的元素:

<?xml version="1.0"?>
<source>
  <ItemNotSubstituted/>
  <ItemToBeSubstituted Id="MatchId">
    <Element3 Value="baz"/>
    <Element1/>
    <Element2 Value="foo"/>
  </ItemToBeSubstituted>
</source>

使用 XSL 执行此操作的最佳方法是什么?样式表可能包含许多要替换的元素。所以我不想使用需要<xsl:if>围绕每一个元素。有没有比使用一个样式表插入内容,然后使用第二个样式表删除重复元素更好的方法?


此 XSLT 1.0 解决方案可实现您的预​​期:

<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:subst="http://tempuri.org/mysubst"
>

  <!-- expand this section to contain all your default elements/values -->
  <subst:defaults>
    <subst:element name="ItemToBeSubstituted" id="MatchId">
      <subst:Element1/>
      <subst:Element2 Value="foo"/>
      <subst:Element3 Value="bar"/>
    </subst:element>
  </subst:defaults>

  <!-- this makes the above available as a variable -->
  <xsl:variable name="defaults" select="document('')/*/subst:defaults" />

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

  <!-- expand the match expression to contain all elements 
       names that need default values -->
  <xsl:template match="ItemToBeSubstituted">
    <xsl:copy>
      <xsl:copy-of select="@*|*"/>
      <xsl:call-template name="create-defaults" />
    </xsl:copy>
  </xsl:template>

  <!-- this does all the heavy lifting -->
  <xsl:template name="create-defaults">
    <xsl:variable name="this" select="." />

    <xsl:for-each select="
      $defaults/subst:element[@name = name($this) and @id = $this/@Id]/*
    ">
      <xsl:if test="not($this/*[name() = local-name(current())])">
        <xsl:apply-templates select="." />
      </xsl:if>
    </xsl:for-each>
  </xsl:template>

  <!-- create the default nodes without namespaces -->
  <xsl:template match="subst:*">
    <xsl:element name="{local-name()}">
      <xsl:apply-templates select="subst:*|@*" />
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

使用单独的命名空间(“subst”)使您能够在样式表中保留默认值。这是否是一件好事取决于,至少你不必有两个文件。

如果您希望样式表与默认值分离,请将它们放入额外的文件中并使用此行。

<xsl:variable name="defaults" select="document('defaults.xml')/subst:defaults" />

一旦执行此操作,您就可以放弃所有额外的命名空间处理,并最终或多或少地得到 Josh Davis 提出的解决方案。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

XSLT 仅当元素不存在时插入元素 的相关文章

随机推荐