如何删除多余的标签?

How to remove extra tag?

要更新的第一个 xml 文件:

<Conf key="11" title="tit">
  <Item key="1" >
    <Lock con="E0" />
    <Vol title="te" description="de">
      <All con="1" title="uu" />
      <Widget title="11" description="11" />
    </Vol>
    <Am title="re" description="de">
      <Hold con="50"  />
      <Widget title="22" description="22" />
    </Am>
  </Item>
  <Item key="2" title="tit">
    <Lock const="E0" />
    <Vol title="ty">
      <All con="1" title="fg" />
      <Widget title="33" description="33"  />
    </Vol>
    <Am title="gh" description="gh">
      <Hold const="50" />
      <Widget title="44" description="44" />
    </Am>
  </Item>
</Conf>

从中获取数据以更新第一个文件的第二个 xml 文件“updates.xml”:

<Profile title="u">
  <Item key="1">
    <Vol>
      <Widget title="AA"/>
    </Vol>
    <Am>
      <Widget title="BB" description="BB" />
    </Am>
  </Item>
  <Item key="2">
    <Vol>
      <Widget title="CC" description="CC" />
    </Vol>
    <Am>
      <Widget title="Dd" description="DD" />
    </Am>
  </Item>
</Profile>

update.xml 文件与输入文件的不同之处在于,它只包含那些作为 Widget 标签祖先的标签,并且缺少一些属性。在 XSLT1.0 转换之后,输入树被转移到输出树,替换了“updates.xml”文件中的 Widget 标签。如果输入文件中有 Widget 标签,但“updates.xml”文件中没有,则将输入文件中的 widget 标签传输到输出树。现在转换文件:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exslt="http://exslt.org/common"
  exclude-result-prefixes="exslt"
  version="1.0">
  <xsl:output method="xml"/>

  <xsl:param name="updates" select="document('updates.xml')"/>
  <xsl:key name="replacement" match="Widget" use="local-name(parent::*)"/>

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

  <xsl:template match="Widget">
    <xsl:variable name="parentName" select="local-name(parent::*)"/>
    <xsl:variable name="replacement">
      <xsl:for-each select="$updates">
        <xsl:copy-of select="key('replacement', $parentName)"/>
      </xsl:for-each>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="exslt:node-set($replacement)/*">
        <xsl:copy-of select="$replacement"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="."/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

转换文件后:

<?xml version="1.0" encoding="utf-8"?>
<Conf key="11" title="tit">
  <Item key="1">
    <Lock con="E0" />
    <Vol title="te" description="de">
      <All con="1" title="uu" />
      <Widget title="AA" />
      <Widget title="CC" description="CC" />
    </Vol>
    <Am title="re" description="de">
      <Hold con="50" />
      <Widget title="BB" description="BB" />
      <Widget title="Dd" description="DD" />
    </Am>
  </Item>
  <Item key="2" title="tit">
    <Lock const="E0" />
    <Vol title="ty">
      <All con="1" title="fg" />
      <Widget title="AA" />
      <Widget title="CC" description="CC" />
    </Vol>
    <Am title="gh" description="gh">
      <Hold const="50" />
      <Widget title="BB" description="BB" />
      <Widget title="Dd" description="DD" />
    </Am>
  </Item>
</Conf>

如何保证XSLT1.0改造后的输出文件没有多余的Widget标签,输出文件是这样的:

<?xml version="1.0" encoding="utf-8"?>
<Conf key="11" title="tit">
  <Item key="1">
    <Lock con="E0" />
    <Vol title="te" description="de">
      <All con="1" title="uu" />
      <Widget title="AA" />
    </Vol>
    <Am title="re" description="de">
      <Hold con="50" />
      <Widget title="BB" description="BB" />
    </Am>
  </Item>
  <Item key="2" title="tit">
    <Lock const="E0" />
    <Vol title="ty">
      <All con="1" title="fg" />
      <Widget title="CC" description="CC" />
    </Vol>
    <Am title="gh" description="gh">
      <Hold const="50" />
      <Widget title="Dd" description="DD" />
    </Am>
  </Item>
</Conf>

我会这样处理,不需要使用 exslt:node-set() 扩展函数。

创建一个复合键,它使用 Item@keyWidget 父元素的 local-name()。使用该复合键值从用于将上下文切换到 updates.xml 文档的 xs:for-each 中的 $updates 中查找相应的 Widget

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:exslt="http://exslt.org/common"
    exclude-result-prefixes="exslt"
    version="1.0">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:variable name="updates" select="document('updates.xml')"/>
    <xsl:key name="replacement" match="Widget" use="concat(../../@key, local-name(..))"/>
    
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="Widget">
        <xsl:variable name="key" select="concat(../../@key, local-name(..))"/>
        <xsl:variable name="replacement">
            <xsl:for-each select="$updates">
                <xsl:copy-of select="key('replacement', $key)"/>
            </xsl:for-each>
        </xsl:variable>
        <xsl:choose>
            <xsl:when test="$replacement">
                <xsl:copy-of select="$replacement"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy-of select="."/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    
</xsl:stylesheet>