2012-06-01 13 views
1

我有這個輸入:如何修復XSLT中的消除轉換?

<root> 
    <sector> 
     <nodeA id="a"> 
      <section id="i"> 
       <item1 id="1" method="delete"/> 

       <item1 id="1" method="create"> 
        <somechild>a</somechild> 
       </item1> 
       <item1 id="1" method="change"> 
        <somechild>a</somechild> 
       </item1> 
      </section> 
      <section id="i"> 
       <item1 id="1" method="change"> 
        <somechild>a</somechild> 
       </item1> 
      </section> 
      <section id="i"> 
       <item1 id="1" method="delete"/> 
       <item1 id="1" method="create"> 
        <somechild>a</somechild> 
       </item1> 
      </section> 
     </nodeA> 
    </sector> 
</root> 

我的XSL是:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 

    <xsl:template match="*[not(.//*[@id!=''])][@method='delete']"> 
     <xsl:if test="not(following::*[not(.//*[@id!=''])][@id=current()/@id][../@id = current()/../@id][generate-id(../..) = generate-id(current()/../..)])"/>  
    </xsl:template>  

    <xsl:template match="*[not(.//*[@id!=''])][@method!='delete']"> 
     <xsl:if test="not(following::*[not(.//*[@id!=''])][@method='delete'][@id=current()/@id][../@id = current()/../@id][generate-id(../..) = generate-id(current()/../..)])"/> 
    </xsl:template>  

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

我的輸出:

<root> 
    <sector> 
     <nodeA id="a"> 
      <section id="i"> 
      </section> 
      <section id="i"> 
      </section> 
      <section id="i"> 
      </section> 
     </nodeA> 
    </sector> 
</root> 

預期輸出:

<root> 
    <sector> 
     <nodeA id="a"> 
      <section id="i"> 
       <item1 id="1" method="delete"/> <!-- leave this node --> 
      </section> 
      <section id="i"> 
      </section> 
      <section id="i"> 
       <item1 id="1" method="create"> <!-- leave this node --> 
        <somechild>a</somechild> 
       </item1> 
      </section> 
     </nodeA> 
    </sector> 
</root> 

的想法是,我想刪除元素節點的組合與

  • 一個方法創建之後一個或多個修改之後一個刪除方法,剩下的不變。
  • 它必須是相同的元素名稱,例如<item1>@id,並且在相同父級下例如<section id=1>

任何人都可以幫助我進行轉型嗎?

謝謝。 約翰

+1

你的問題越來越棘手:) –

回答

1

約翰,我有一個解決方案,你可能會也可能不喜歡。我做了一次「優化」來簡化一些事情。我認爲,既然您正在將那些section節點與相同的相同對待,那麼您可能不介意將它們全部合併到結果文檔中。看看這是否可以接受。

XSLT 2.0轉型:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 

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

    <xsl:template match="nodeA"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <xsl:for-each-group select="*" group-by="concat(name(), '|', @id)"> 
       <xsl:copy> 
        <xsl:apply-templates select="@*"/> 
        <xsl:variable name="merged"> 
         <xsl:for-each-group select="current-group()/*" 
              group-by="concat(name(), '|', @id)"> 
          <xsl:copy-of select="current-group()"/> 
         </xsl:for-each-group> 
        </xsl:variable> 
        <xsl:apply-templates select="$merged/*"/> 
       </xsl:copy> 
      </xsl:for-each-group> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*[@method = 'create'] 
          [following-sibling::*[@method = 'change'] 
               [following-sibling::*[@method = 'delete']]]"/> 
    <xsl:template match="*[@method = 'change'] 
          [preceding-sibling::*[@method = 'create']] 
          [following-sibling::*[@method = 'delete']]"/> 

    <xsl:template match="*[@method = 'delete'] 
          [preceding-sibling::*[@method = 'change'] 
               [preceding-sibling::*[@method = 'create']]]"/> 

</xsl:stylesheet> 

當適用於您的輸入文檔生成:

<root> 
    <sector> 
     <nodeA id="a"> 
     <section id="i"> 
      <item1 id="1" method="delete"/> 
      <item1 id="1" method="create"> 
        <somechild>a</somechild> 
       </item1> 
     </section> 
     </nodeA> 
    </sector> 
</root> 

我想,這將是更容易的item1節點摺疊成扁平有序列表,然後只篩選出您想要刪除的序列。我不得不將它們複製到一個變量中,以便與原始文檔樹「分離」,否則這些軸將在原始父節點內查找。從「相同」父節點獲得「相同」item1節點的平面列表後,過濾創建 - 更改 - 刪除序列實際上是小菜一碟。我只是翻譯你的規則直接進入XPath的謂詞 - 一個用於create隨後change後跟一個delete,另一個用於change通過create前面和後面一個delete,並通過create最後一個delete通過change之前之前。他們都「知道」是相同的name()@id和來自相同的(name()@id)父,所以我們不必檢查該部分。

+0

謝謝你的解決方案。我仍然試圖實現這個匹配我更大的xml輸入。我會盡快給您回覆。再次感謝 – John

1

好吧,這並不容易,但它在xslt 1.0中起作用!

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="1.0"> 
<!-- 
    copies all nodes with id 
    if there first preceding delete followed by the first preceding create 
    --> 
    <xsl:template match="*[not(.//*[@id!=''])]"> 
     <xsl:variable name="id" select="@id"/> 
     <xsl:variable name="parentId" select="../@id"/> 
     <xsl:variable name="precedings" select="preceding::*[@id=$id][../@id=$parentId]"/> 
     <xsl:variable name="lastDelete" select="($precedings[@method='delete'])[last()]"/> 
     <xsl:variable name="lastCreate" select="($precedings[@method='create'])[last()]"/> 
     <xsl:variable name="openCreate" select="$lastDelete[following::* = $lastCreate] or (not($lastDelete) and $lastCreate)"/> 
     <xsl:if test="not(following::*[@id=$id][../@id=$parentId][@method='delete'] and $openCreate)"> 
      <xsl:copy> 
       <xsl:apply-templates select="@*"/> 
       <xsl:apply-templates select="node()"/> 
      </xsl:copy> 
     </xsl:if> 
    </xsl:template> 

    <!-- 
    copies all deletes, if they have no preceding creates 
    --> 
    <xsl:template match="*[not(.//*[@id!=''])][@method='delete']" priority="10"> 
     <xsl:variable name="id" select="@id"/> 
     <xsl:variable name="parentId" select="../@id"/> 
     <xsl:variable name="precedings" select="preceding::*[@id=$id][../@id=$parentId]"/> 
     <xsl:variable name="precCreate" select="$precedings[@method='create']"/> 
     <xsl:if test="not($precCreate)"> 
      <xsl:copy> 
       <xsl:apply-templates select="@*"/> 
       <xsl:apply-templates select="node()"/> 
      </xsl:copy> 
     </xsl:if> 
    </xsl:template> 

<!-- 
    copies all creates, if they have no following deletes 
    --> 

    <xsl:template match="*[not(.//*[@id!=''])][@method='create']" priority="10"> 
     <xsl:variable name="id" select="@id"/> 
     <xsl:variable name="parentId" select="../@id"/> 
     <xsl:variable name="followings" select="following::*[@id=$id][../@id=$parentId]"/> 
     <xsl:variable name="followDelete" select="$followings[@method='delete']"/> 
     <xsl:if test="not($followDelete)"> 
      <xsl:copy> 
       <xsl:apply-templates select="@*"/> 
       <xsl:apply-templates select="node()"/> 
      </xsl:copy> 
     </xsl:if> 
    </xsl:template> 

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

</xsl:stylesheet> 

測試它...沒有保證!

+0

它的工作原理,但是它缺少一個我相信的要求。那些'[@id = $ id]'的'item1'節點也應該屬於「相同」父節點,纔有資格進行「合併」。需要將'[parent :: */@ id =(current()/ parent :: */@ id)]'添加到這些'[@id = $ id]'謂詞中。 –

+0

然後我用'create' -'''''lelete'序列的各種組合來玩,結果與John的要求不符。我想你的轉換需要更多的測試和調整,但你絕對是在這裏的東西。 –

+0

好吧,我做了一些改變:我誤解了第二個要求,我解決了這個問題。還有一些錯誤。 –