开发者

pull unique groups from xml using xslt

I realize you can't use arrays in xsl and normally to do the task below it would take an array. Here's what I need...开发者_运维问答

Sample xml code...

<products>
 <product>
  <productNumber>1</productNumber>
  <productType>TypeA</productType>
 </product>
 <product>
  <productNumber>2</productNumber>
  <productType>TypeB</productType>
 </product>
 <product>
  <productNumber>3</productNumber>
  <productType>TypeA</productType>
 </product>
 <product>
  <productNumber>4</productNumber>
  <productType>TypeC</productType>
 </product>
 <product>
  <productNumber>5</productNumber>
  <productType>TypeA</productType>
 </product>
</products>

Above is a listing of unique "products" and each product is assigned a "productType" that could be repeated several times throughout the xml. I would like the xsl to pull a single entry for each "productType" without repeats.

The end result of above would be something like...

TypeA
TypeB
TypeC

And not ....

TypeA
TypeB
TypeA
TypeC
TypeA

I can't be the only one that has looked for this kind of functionality.

thoughts?


This transformation:

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

 <xsl:key name="kProdByType"
      match="product" use="productType"/>

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

 <xsl:template match="/products">
  <products>
    <xsl:apply-templates select=
     "product[generate-id()
             =
             generate-id(key('kProdByType', productType)[1])
             ]
     "/>
  </products>
 </xsl:template>

 <xsl:template match="product">
   <productType value="{productType}">
    <xsl:apply-templates mode="copy"
     select="key('kProdByType', productType)"/>
   </productType>
 </xsl:template>

 <xsl:template match="product" mode="copy">
  <xsl:call-template name="identity"/>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<products>
 <product>
  <productNumber>1</productNumber>
  <productType>TypeA</productType>
 </product>
 <product>
  <productNumber>2</productNumber>
  <productType>TypeB</productType>
 </product>
 <product>
  <productNumber>3</productNumber>
  <productType>TypeA</productType>
 </product>
 <product>
  <productNumber>4</productNumber>
  <productType>TypeC</productType>
 </product>
 <product>
  <productNumber>5</productNumber>
  <productType>TypeA</productType>
 </product>
</products>

produces the wanted, correct grouping:

<products>
    <productType value="TypeA">
        <product>
            <productNumber>1</productNumber>
            <productType>TypeA</productType>
        </product>
        <product>
            <productNumber>3</productNumber>
            <productType>TypeA</productType>
        </product>
        <product>
            <productNumber>5</productNumber>
            <productType>TypeA</productType>
        </product>
    </productType>
    <productType value="TypeB">
        <product>
            <productNumber>2</productNumber>
            <productType>TypeB</productType>
        </product>
    </productType>
    <productType value="TypeC">
        <product>
            <productNumber>4</productNumber>
            <productType>TypeC</productType>
        </product>
    </productType>
</products>

Do note: This is an example of the well-known Muenchian method for grouping, which is the fastest known gouping technique in XSLT 1.0.

XSLT 2.0 solution:

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

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

 <xsl:template match="/products">
   <xsl:for-each-group select="product"
        group-by="productType">
     <productType value="{productType}">
      <xsl:apply-templates select="current-group()"/>
     </productType>
   </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>

when this XSLT 2.0 transformation is applied on the provided XML document, exactly the same, correctly-grouped result is produced.


Dimitre got me going on the right path. Here is the code that I came up with that worked for my needs, a simple deliniated stream of output to support an AJAX call...

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8"/>
<xsl:key name="kProdByBrand" match="Products/Product" use="Brand"/>

 <xsl:template match="Products">
   <xsl:for-each 
     select="Product
     [generate-id() = generate-id(key('kProdByBrand', Brand)[1])]"><xsl:sort
     select="Brand" /><xsl:value-of select="Brand" />|</xsl:for-each>
 </xsl:template>

</xsl:stylesheet>

Given an xml that has many many members that look something like this...

<Product>
    <Brand>Brand</Brand>
    <OldPN>myCompany Part Number</OldPN>    
            ...
</Product>

The output looks like this...

Brand|Brand1|Brand2|Brand3|
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜