2009-08-04 84 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