2015-08-14 15 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好的,我可以看到我缺少对应于空视图的行。修复即将到来.. –