2015-01-08 77 views
1

我工作的一個項目,我有一些XML輸入轉換爲一些XML輸出,爲此我使用XSLT版本1XPath表達式來選擇唯一的節點

輸入XML文件我m的工作是巨大的像10k +行,但我已經花了一小時的更多時間把它燒到下面的代碼片段,這就解決了這個問題。

這是輸入XML

<QueryInput > 
    <Subject> 
    <Content> 
     <MunicipalityCode>0217</MunicipalityCode> 
    </Content> 
    </Subject> 
    <QueryResultStep> 
    <Multistep> 
     <IterationResponse> 
     <QueryResult> 
      <Kommune>0217</Kommune> 
     </QueryResult> 
     </IterationResponse> 
     <IterationResponse> 
     <QueryResult> 
      <Kommune>0217</Kommune> 
     </QueryResult> 
     </IterationResponse> 
     <IterationResponse> 
     <QueryResult> 
      <Kommune>0223</Kommune> 
     </QueryResult> 
     </IterationResponse> 
     <IterationResponse> 
     <QueryResult> 
      <Kommune>0223</Kommune> 
     </QueryResult> 
     </IterationResponse> 
    </Multistep> 
    </QueryResultStep> 
</QueryInput> 

輸出XML應包含每個 「Kommune」 一次,刪除重複。爲此,我製作了以下XSLT代碼。

<?xml version="1.0" encoding="utf-8"?> 
<xsl:transform version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       exclude-result-prefixes="xsl xsi xsd"> 

    <xsl:output method="xml" indent="yes"/> 
    <xsl:template match="/"> 

    <QueryResult> 
     <xsl:variable name="something"> 
     <KommuneCollection> 
      <xsl:for-each select="QueryInput/QueryResultStep/Multistep/IterationResponse/QueryResult/Kommune[not(.=preceding::*)]"> 
      <NewKommune> 
       <xsl:value-of select="."/> 
      </NewKommune> 
      </xsl:for-each> 
     </KommuneCollection> 
     </xsl:variable> 
     <xsl:copy-of select="$something"/> 
    </QueryResult> 
    </xsl:template> 
</xsl:transform> 

將會產生以下的(幾乎是正確的)輸出:

<KommuneCollection> 
    <NewKommune>0223</NewKommune> 
</KommuneCollection> 

但應該產生

<KommuneCollection> 
    <NewKommune>0217</NewKommune> 
    <NewKommune>0223</NewKommune> 
</KommuneCollection> 

如果我在輸入XML刪除<MunicipalityCode>0217</MunicipalityCode>,突然它的工作原理的 - 但我真的不明白爲什麼。不是爲什麼會發生,我也不知道如何解決這個問題。任何幫助是極大的讚賞!

編輯:通過將輸入XML複製到Notepad ++,安裝XPathenizer工具,顯示窗口並輸入此XPath表達式QueryInput/QueryResultStep/Multistep/IterationResponse/QueryResult/Kommune[not(.=preceding::*)]並執行表達式,可以輕鬆地複製該問題。結果可以在右側看到。我懷疑問題在於XSLT中的for-each標記中使用的XPath表達式。

+3

參見XSLT 1.0分組標準文章:http://www.jenitennison.com/xslt/grouping/muenchian.html –

回答

1

您的謂詞可能會奏效,但未包含「217」,因爲/QueryInput/Subject/Content/MunicipalityCode碰巧有值「217」。

如果您調整謂詞過濾器,以匹配前面的Kommune元素前述任一元素代替,那麼它會產生預期的結果:

[not(.=preceding::Kommune)] 

然而,這是不是很有效。如果你的文件很大,那麼使用xsl:key()meunchian method將會更高性能。

<?xml version="1.0" encoding="utf-8"?> 
<xsl:transform version="1.0" 
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    exclude-result-prefixes="xsl xsi xsd"> 

    <xsl:output method="xml" indent="yes"/> 
    <xsl:key name="Kommune" match="Kommune" use="."/> 
    <xsl:template match="/">  
     <QueryResult> 
      <xsl:variable name="something"> 
       <KommuneCollection> 
        <xsl:for-each 
          select="QueryInput/QueryResultStep/Multistep/ 
            IterationResponse/QueryResult/ 
            Kommune[generate-id(.) = 
              generate-id(key('Kommune',.)[1])]"> 
         <NewKommune> 
          <xsl:value-of select="."/> 
         </NewKommune> 
        </xsl:for-each> 
       </KommuneCollection> 
      </xsl:variable> 
      <xsl:copy-of select="$something"/> 
     </QueryResult> 
    </xsl:template> 
</xsl:transform> 
2

As michael.hor257k說,Muenchian的分組將幫助你(處理大文件)。但是,下面將是你目前嘗試的正確版本:

<xsl:transform version="1.0" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exclude-result-prefixes="xsl xsi xsd"> 
<xsl:output method="xml" indent="yes"/> 
<xsl:template match="/"> 
    <QueryResult> 
     <KommuneCollection> 
      <xsl:for-each select="QueryInput/QueryResultStep/Multistep/IterationResponse/QueryResult/Kommune[not(. = preceding::QueryResult/Kommune)]"> 
       <NewKommune> 
        <xsl:value-of select="."/> 
       </NewKommune> 
      </xsl:for-each> 
     </KommuneCollection> 
    </QueryResult> 
</xsl:template> 
</xsl:transform> 

注意:這種方式效率不高。當你使用Muenchian的分組時,你會感覺到不同。

+0

如果我理解這個正確時,'[不(=前述:: *。 )]'表達式選擇*所有*前面的節點 - 我原以爲它只從父節點獲得了前面的節點。非常感謝您的回答 - 幫助我在XPath中獲得更多洞察力! PS:我要研究這種Muenchian的分組方法。再次感謝。 – Andersnk

+1

要選擇前面的節點,我們有前同步軸。要糾正你的語句,'*'只匹配elment節點(不是所有節點)。 –

相關問題