XSLT 仅在元素不存在时插入 [英] XSLT Insert element only if it doesn't exist

查看:31
本文介绍了XSLT 仅在元素不存在时插入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个源文件:

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

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

And a stylesheet containing content I want to substitute into the 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>

此样式表成功地将 复制到 ItemToBeSubstituted>.但是当我使用不同的源文档时,其中 ItemToBeSubstituted 已经有内容:

This stylesheet succesfuly copies <Element1/><Element2 Value="foo"/><Element3 Value="bar"/> into ItemToBeSubstituted. But when I use a different source document, in which ItemToBeSubstituted already has content:

<?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>

我只想替换源文档中不存在的样式表中的元素.这是将样式表应用到第二个文档后我正在寻找的输出,只有源文档中的 元素:

I would like to only substitute elements from the stylesheet that do not already exist in the source document. This is the output I'm looking for after applying the stylesheet to the second document, with only the <Element3> element from the source document:

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

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

What is the best approach for doing this with XSL? The stylesheet may contain many elements to be substituted. So I don't want to use an approach that requires an <xsl:if> around every single element. Is there a better way than using one stylesheet to insert the content, then having a second stylesheet that removes duplicate elements?

推荐答案

这个 XSLT 1.0 解决方案符合您的意图:

This XSLT 1.0 solution does what you intend:

<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")使您能够在样式表中保留默认值.这是否是一件好事取决于,至少您不必有两个文件.

The use of a separate namespace ("subst") enables you to keep the defaults within the stylesheet. Whether this is a good thing or not depends, at least you don't have to have two files lying around.

如果您希望将样式表与默认值分离,请将它们放在一个额外的文件中并改用这一行.

If you prefer to have the stylesheet decoupled from the default values, put them in an extra file and use this line instead.

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

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

You could drop all the the extra namespace handling once you do this, and would end up with the solution Josh Davis proposed, more or less.

这篇关于XSLT 仅在元素不存在时插入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆