2015-08-14 33 views
0

我有一個需求,我正在努力解決這個問題。我剛剛嘗試瞭解XSLT是什麼。如何爲可能涉及多個for循環的XML編寫XSLT

在這個需求中我有一個XML,每個報表節點下都有一個業務標籤。我的XSLT應該是這樣的,我需要以逐列的方式獲得與此業務相關的所有正確安全性的View/Initiate/Rescind/Approve/Cancel &(好吧,我不確定如何提出正確的問題)。

<?xml version="1.0" encoding="UTF-8"?> 
<Report_Data> 
<Report> 
    <Business>Electronic</Business> 
    <View> 
     <Security>View A</Security> 
    </View> 
    <View> 
     <Security>View B</Security> 
    </View> 
    <View> 
     <Security>View C</Security> 
    </View> 
    <Initiate> 
     <Security>Initiated A01</Security> 
    </Initiate> 
    <Initiate> 
     <Security>Initiated Z01</Security> 
    </Initiate> 
</Report> 
<Report> 
    <Business>Adjustment</Business> 
    <View> 
     <Security>View CE</Security> 
    </View> 
    <View> 
     <Security>View MFK</Security> 
    </View> 
    <View> 
     <Security>View VW</Security> 
    </View> 
    <View> 
     <Security>View KKL</Security> 
    </View> 
    <Initiate> 
     <Security>Initiated 004</Security> 
    </Initiate> 
    <Initiate> 
     <Security>Initiated M16</Security> 
    </Initiate> 
    <Approve> 
     <Security>Approve Manager</Security> 
    </Approve> 
    <Approve> 
     <Security>Approve AXE</Security> 
    </Approve> 
    <Approve> 
     <Security>Approve LKL</Security> 
    </Approve> 
</Report> 
<Report> 
    <Business>Purge Event</Business> 
    <View> 
     <Security>View Administrator</Security> 
    </View> 
    <View> 
     <Security>View Auditor</Security> 
    </View> 
    <View> 
     <Security>View Developer</Security> 
    </View> 
    <Initiate> 
     <Security>Initiate Administrator</Security> 
    </Initiate> 
    <Initiate> 
     <Security>Initiate Developer</Security> 
    </Initiate> 
    <Cancel> 
     <Security>Cancel HR</Security> 
    </Cancel> 
    <Cancel> 
     <Security>Cancel Administrator</Security> 
    </Cancel> 
    <Cancel> 
     <Security>Cancel Developer</Security> 
    </Cancel> 
    <Cancel> 
     <Security>Cancel Initiator</Security> 
    </Cancel> 
</Report> 
<Report> 
    <Business>Generated Event</Business> 
    <View> 
     <Security>View Process Administrator</Security> 
    </View> 
    <View> 
     <Security>View Developer</Security> 
    </View> 
    <View> 
     <Security>View Auditor</Security> 
    </View> 
    <Rescind> 
     <Security>Rescind Process Administrator</Security> 
    </Rescind> 
    <Rescind> 
     <Security>Rescind Developer</Security> 
    </Rescind> 
</Report> 
<Report> 
    <Business>Expense</Business> 
    <Rescind> 
     <Security>Rescind Sr Developer</Security> 
    </Rescind> 
    <Cancel> 
     <Security>Cancel developer</Security> 
    </Cancel> 
    <Cancel> 
     <Security>Cancel Sr Developer</Security> 
    </Cancel> 
    <Correct> 
     <Security>Correct ADMIN</Security> 
    </Correct> 
</Report> 
</Report_Data> 

輸出:

Business,View,Initiate,Rescind,Approve,Cancel,Correct 
Electronic,View A,Initiated A01,,,,    
Electronic,View B,Initiated Z01,,,,    
Electronic,View C,,,,,     
Adjustment,View CE,Initiated 004,,Approve Manager,,  
Adjustment,View MFK,Initiated M16,,Approve AXE,,   
Adjustment,View VW,,,Approve LKL,,  
Adjustment,View KKL,,,,,      
Purge Event,View Administrator,Initiate Administrator,,,Cancel HR, 
Purge Event,View Auditor,Initiate Developer,,,Cancel Administrator, 
Purge Event,View Developer,,,,Cancel Developer, 
Purge Event,,,,,Cancel Initiator, 
Generated Event,View Process Administrator,,Rescind Process Administrator,,,    
Generated Event,View Developer,,Rescind Developer,,,    
Generated Event,View Auditor,,,,,     
Expense,,,Rescind Sr Developer,,Cancel developer,Correct ADMIN 
Expense,,,,,Cancel Sr Developer, 

請任何幫助深表感謝。

謝謝,

+0

你是否真的想要輸出顯示像那樣或結構的二維行和列電子表格/數據庫表/數據框導入XML? – Parfait

+0

@SasukeSurendra:請注意備用解決方案發布。 –

回答

0

首先,這並不簡單。如果您對XSLT不熟悉,那麼這不是您接受的最佳項目。

現在,如果我理解了正確的要求,您希望將具有相同位置的報表的所有值組合到一行中,並且一直這樣做,直到所有類別中的值都用完。這可以使用遞歸命名模板來完成:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="text" encoding="UTF-8"/> 

<xsl:template match="/Report_Data"> 
    <xsl:text>Business,View,Initiate,Rescind,Approve,Cancel,Correct&#10;</xsl:text> 
    <xsl:for-each select="Report"> 
     <xsl:call-template name="write-rows"/> 
    </xsl:for-each> 
</xsl:template> 

<xsl:template name="write-rows"> 
    <xsl:param name="i" select="1"/> 
    <!-- write to output --> 
    <xsl:value-of select="Business"/> 
    <xsl:text>,</xsl:text> 
    <xsl:value-of select="View[$i]/Security"/> 
    <xsl:text>,</xsl:text> 
    <xsl:value-of select="Initiate[$i]/Security"/> 
    <xsl:text>,</xsl:text> 
    <xsl:value-of select="Rescind[$i]/Security"/> 
    <xsl:text>,</xsl:text> 
    <xsl:value-of select="Approve[$i]/Security"/> 
    <xsl:text>,</xsl:text> 
    <xsl:value-of select="Cancel[$i]/Security"/> 
    <xsl:text>,</xsl:text> 
    <xsl:value-of select="Correct[$i]/Security"/> 
    <xsl:text>&#10;</xsl:text> 
    <!-- recursive call --> 
    <xsl:if test="$i &lt; count(View) or $i &lt; count(Initiate) or $i &lt; count(Rescind) or $i &lt; count(Approve) or $i &lt; count(Cancel) or $i &lt; count(Correct)"> 
     <xsl:call-template name="write-rows"> 
      <xsl:with-param name="i" select="$i + 1"/> 
     </xsl:call-template> 
    </xsl:if> 
</xsl:template> 

</xsl:stylesheet> 
+0

謝謝Michael。這正是我正在尋找的東西。是的,我知道這個新項目對我來說有點困難。但是由於我以前的表現很好,所以期望值非常高。現在做2個月的XSLT,不時學習點點滴滴。謝謝你的幫助!非常感激。 – SasukeSurendra

+0

不知道在哪裏(網站的詳細信息),我可以找到所有這些例子,如果你讓我知道,那麼你有我的心充滿感謝。 – SasukeSurendra

+0

「*哪裏(網站的詳細信息)我可以找到所有這些例子*」恐怕我不知道你指的是什麼。 –

0

這是一個非常簡單的問題,以及良好的學習exersize的XSLT。您沒有指定XSLT版本(當您在StackOverflow上發佈XSLT問題時,這通常是非常重要的)。我假定XSLT 2.0,但在這種情況下,該解決方案同樣適用於XSLT 1.0。

這個小小的樣式表將您給定的示例輸入文檔轉換爲您的預期輸出文檔。

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="2.0"> 

<xsl:output method="text" encoding="utf-8" /> 
<xsl:strip-space elements="*" /> 

<xsl:template match="/"> 
    <xsl:text>Business,View,Initiate,Rescind,Approve,Cancel,Correct&#x0A;</xsl:text> 
    <xsl:apply-templates select="Report_Data/Report/View" /> 
</xsl:template> 

<xsl:template match="View"> 
    <xsl:variable name="i" select="count(preceding-sibling::View) + 1" /> 
    <xsl:value-of select="concat(../Business,',',.,',',../Initiate[$i],',',../Rescind[$i],',',../Approve[$i],',',../Cancel[$i],',',../Correct[$i],'&#x0A;')" /> 
</xsl:template> 

</xsl:stylesheet> 

學習筆記

避開拉風變換(xsl:for-each等)在可能情況下,尤其是當你學習。通常,推式轉換(如利用模板)將爲您提供更簡單,更高效且更易維護的解決方案。推動學習也更好,因爲它更符合XSLT的精神。 XSLT意味着功能性,而不是程序性的。

修正

起初,我雖然是人口最多的孩子總是視圖,我的第一個解決方案取決於這一點。感謝Michael的評論,我注意到這是不正確的。因此,我們需要稍微調整一下計算Report報告中哪個孩子是人口最多的那個孩子。我也假定總是有一個商業元素。

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="2.0"> 

<xsl:output method="text" encoding="utf-8" /> 
<xsl:strip-space elements="*" /> 

<xsl:template match="/"> 
    <xsl:text>Business,View,Initiate,Rescind,Approve,Cancel,Correct&#x0A;</xsl:text> 
    <xsl:apply-templates select="Report_Data/Report" /> 
</xsl:template> 

<xsl:template match="Report"> 
    <xsl:variable name="child-node-names" select="('View','Initiate','Rescind','Approve','Cancel','Correct')" /> 
    <xsl:variable name="max-children" select="max(for $n in $child-node-names return count(*[local-name() eq $n]))" /> 
    <xsl:variable name="most-populous-child" select="(for $n in $child-node-names return $n[ count(current()/*[local-name() eq $n]) eq $max-children])[1]" /> 
    <xsl:apply-templates select="*[local-name() eq $most-populous-child]" /> 
</xsl:template> 


<xsl:template match="View|Initiate|Rescind|Approve|Cancel|Correct"> 
    <xsl:variable name="i" select="count(preceding-sibling::*[local-name() eq local-name(current())]) + 1" /> 
    <xsl:value-of select="concat(../Business,',',../View[$i],',',../Initiate[$i],',',../Rescind[$i],',',../Approve[$i],',',../Cancel[$i],',',../Correct[$i],'&#x0A;')" /> 
</xsl:template> 

</xsl:stylesheet> 

請注意,您還可以使用xsl:for-each-group/xsl:sort做另一種備用解決方案。感興趣的,這裏是分組解決方案...

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="2.0"> 

<xsl:output method="text" encoding="utf-8" /> 
<xsl:strip-space elements="*" /> 

<xsl:template match="/"> 
    <xsl:text>Business,View,Initiate,Rescind,Approve,Cancel,Correct&#x0A;</xsl:text> 
    <xsl:apply-templates select="Report_Data/Report" /> 
</xsl:template> 

<xsl:template match="Report"> 
    <xsl:for-each-group select="* except Business" group-by="local-name()"> 
    <xsl:sort select="count(current-group())" data-type="number" order="descending" /> 
    <xsl:variable name="group" select="position()" /> 
    <xsl:apply-templates select="current-group()[$group eq 1]" /> 
    </xsl:for-each-group> 
</xsl:template> 

<xsl:template match="View|Initiate|Rescind|Approve|Cancel|Correct"> 
    <xsl:variable name="i" select="count(preceding-sibling::*[local-name() eq local-name(current())]) + 1" /> 
    <xsl:value-of select="concat(../Business,',',../View[$i],',',../Initiate[$i],',',../Rescind[$i],',',../Approve[$i],',',../Cancel[$i],',',../Correct[$i],'&#x0A;')" /> 
</xsl:template> 

</xsl:stylesheet> 
+0

「*這是一個非常簡單的問題*」您可能希望保留該語句,直到結果與預期輸出相匹配。 –

+0

@ michael.hor257k好的,我可以看到我缺少對應於空視圖的行。修復即將到來.. –