2009-08-04 119 views
3

它我有一个源文件:XSLT插入元素,如果不存在

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

这个样式表succesfuly副本<Element1/><Element2 Value="foo"/><Element3 Value="bar"/>分成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>。有没有更好的方法比使用一个样式表插入内容,然后有第二个样式表删除重复的元素?

回答

2

这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" /> 

你可以删除所有多余的命名空间处理,一旦你这样做,最终会与约什 - 戴维斯提出的,或多或少的解决方案。

2

我会使用类似的东西:

<?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:variable name="node" select="." /> 
    <xsl:copy> 
     <xsl:copy-of select="@*|*"/> 

     <xsl:for-each select="document('elements.xml')/elements/*"> 
     <xsl:if test="not($node/*[name() = name(current())])"> 
      <xsl:copy-of select="." /> 
     </xsl:if> 
     </xsl:for-each> 
    </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 

凡elements.xml中是您在其中存储默认

<?xml version="1.0" encoding="utf-8" ?> 
<elements> 
    <Element1/> 
    <Element2 Value="foo"/> 
    <Element3 Value="bar"/> 
</elements> 

使用<for-each>我们遍历默认元素加入哪些元素的文件,检查是否有该名称的元素作为当前节点的子元素,如果没有,则添加它。

+0

+1这是问题的良好开端。它可以使用一个更普遍的事实,显然可能有更多的元素被替换为比ItemToBeSubstituted [@Id ='MatchId']`,但这不是很难做到。 – Tomalak 2009-08-05 09:45:14