如何在XSLT中循环遍历常见的id值

时间:2014-11-07 17:35:15

标签: xslt

我最近开始研究XSLT。我需要根据id字段对节点值进行分组。但是,id字段可以是重复值。例如,下面假设是XML结构。

<ProductResults>	
<ProductResult>	                    
		<ProductId>1000</ProductId>
		<Location>Bangalore</Location>
		<ModuleNumber>02</ModuleNumber>
                <StoreId>1234<StoreId>				
</ProductResult>
<ProductResult>	    	
		<ProductId>2000</ProductId>
		<ModuleNumber>03</ModuleNumber>
		<Location>Bangalore</Location>
                <StoreId>1234<StoreId>	 
</ProductResult>
<ProductResult>	
		<ProductId>1000</ProductId>
		<ModuleNumber>01</ModuleNumber>
		<Location>Mumbai</Location>
                <StoreId>1234<StoreId>	
</ProductResult>
<ProductResult>		
		<ProductId>4000</ProductId>
		<ModuleNumber>02</ModuleNumber>
		<Location>Kolkata</Location>
                <StoreId>1234<StoreId>	
</ProductResult>
<ProductResult>		
		<ProductId>1000</ProductId>
		<ModuleNumber>03</ModuleNumber>
		<Location>Chennai</Location>
                <StoreId>1234<StoreId>	
</ProductResult>
</ProductResults>

如果您注意到,ProductId 1000重复三次。我应该编写XSLT来生成以下输出。

{
  "StoreId": "1234",
  "Locations": [
    {
      "ProductId": "1000",
      "Locations": [
        {
          "Location": "Bangalore",
          "ModuleNumber": "02"
        },
        {
          "Location": "Mumbai",
          "ModuleNumber": "01"
        },
        {
          "Location": "Chennai",
          "ModuleNumber": "03"
        }
      ]
    },
    {
      "ProductId": "2000",
      "Locations": [
        {
          "Location": "Bangalore",
          "ModuleNumber": "03"
        }
      ]
    },
    {
      "ProductId": "4000",
      "Locations": [
        {
          "Location": "Kolkata",
          "ModuleNumber": "02"
        }
      ]
    }
  ]
}

由于ProductId字段在重复,我不能直接使用foreach循环,这会为同一个ProductId创建额外的代码块。对此有何建议?。

提前致谢。

1 个答案:

答案 0 :(得分:0)

以下XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" 
            version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
  <xsl:apply-templates select="@*|node()" />
</xsl:template> 
<xsl:template match="@*|node()">
<xsl:variable name="uniqueProducts" 
              select="count(//ProductId[not(. = following::ProductId)])"/>
{
 "StoreId": <xsl:value-of select="ProductResult/StoreId"/>,
 "Locations": [
 <xsl:for-each select="//ProductId[not(. = following::ProductId)]">
   <xsl:sort select="."/>
  {
  "ProductId": "<xsl:value-of select="."/>,
  "Locations": [
  <xsl:call-template name="location">
       <xsl:with-param name="productId" select="."/>
  </xsl:call-template>
  ]
  <xsl:if test="position() &lt; $uniqueProducts">,</xsl:if>
</xsl:for-each>
]}
</xsl:template>
<xsl:template  name="location">
<xsl:param name="productId"/>
<xsl:for-each select="//ProductResult[ProductId=$productId]">
{
"Location": "<xsl:value-of select="Location"/>",
"ModuleNumber": "<xsl:value-of select="ModuleNumber"/>"
    }
   <xsl:if test="position() != last()">,</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

应用于以下内容时(已更正,因为您未关闭示例中的StoreId输入XML:

<ProductResults>    
  <ProductResult>                       
     <ProductId>1000</ProductId>
     <Location>Bangalore</Location>
     <ModuleNumber>02</ModuleNumber>
     <StoreId>1234</StoreId>                
  </ProductResult>
  <ProductResult>           
     <ProductId>2000</ProductId>
     <ModuleNumber>03</ModuleNumber>
     <Location>Bangalore</Location>
     <StoreId>1234</StoreId>     
  </ProductResult>
  <ProductResult>   
     <ProductId>1000</ProductId>
     <ModuleNumber>01</ModuleNumber>
     <Location>Mumbai</Location>
     <StoreId>1234</StoreId>    
  </ProductResult>
  <ProductResult>       
     <ProductId>4000</ProductId>
     <ModuleNumber>02</ModuleNumber>
     <Location>Kolkata</Location>
     <StoreId>1234</StoreId>    
  </ProductResult>
  <ProductResult>       
     <ProductId>1000</ProductId>
     <ModuleNumber>03</ModuleNumber>
     <Location>Chennai</Location>
     <StoreId>1234</StoreId>    
  </ProductResult>
</ProductResults>

产生以下
输出XML:

{
"StoreId": 1234,
"Locations": [

{
  "ProductId": "1000,
  "Locations": [

{
"Location": "Bangalore",
"ModuleNumber": "02"
    }
   ,
{
"Location": "Mumbai",
"ModuleNumber": "01"
    }
   ,
{
"Location": "Chennai",
"ModuleNumber": "03"
    }
  ]
  ,
{
  "ProductId": "2000,
  "Locations": [

{
"Location": "Bangalore",
"ModuleNumber": "03"
    }
  ]
  ,
 {
  "ProductId": "4000,
  "Locations": [

 {
  "Location": "Kolkata",
  "ModuleNumber": "02"
    }
  ] 
]}

请注意,这只是分组的一个示例,例如一个额外的for-each循环可以调整它以照顾多个StoreId 为简化起见,我刚刚为使用StoreId提供的<xsl:value-of select="ProductResult/StoreId"/>编写此内容,而不是检查不同的StoreId值并处理所有这些值。

有关详细参考,您可以查看Jeni Tennison http://www.jenitennison.com/xslt/grouping/muenchian.xml关于Muenchian Grouping的文章,以及 你可以,例如看一下http://www.dpawson.co.uk/xsl/sect2/N4486.html的XSLT分组。

更新:刚刚调整了首次发布的XSLT,并说明了其工作原理 - <xsl:for-each select="//ProductId[not(. = following::ProductId)]">循环处理所有唯一ProductId值,并调用<xsl:template name="location">当前ProductId作为参数,并为与ProductId匹配的每个位置生成输出。虽然更新的方法不使用Muenchian分组,但它可能仍然对您有价值所以我会保留参考。