Re: [xsl] Summarizing sibling nodes

Subject: Re: [xsl] Summarizing sibling nodes
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Wed, 14 Mar 2001 18:14:06 +0000
Hi Wayne,

The problems here are in your XPath expressions. They're not doing quite what you expect.

Let's look at that for-each select first:
The XPath:
    Truck[not(VINModelYearName/preceding::Truck/VINModelYearName)]
  | Truck[not(VINModelName/preceding::Truck/VINModelName)]

translates into (sort-of) English as

"All Truck element children that do not have a VINModelYearName child that are preceded (somewhere in the document) by a Truck element with a VINModelYearName child, ALONG WITH
all Truck element children that do not have a VINModelName child that are preceded (somewhere in the document) by a Truck element with a VINModelName child"


I don't think you want this. Something like (let's just look at the first half)
    Truck[not(VINModelYearName = preceding::Truck/VINModelYearName)]
is closer, it says

"All Truck element children that do not have a VINModelYearName child (whose string value is) equal to the VINModelYearName child of a preceding Truck element"

But even that's not right. Putting this together with a similar second term, you'd in effect be getting all Truck children with a new VINModelYearName along with all Truck children with a new VINModelName -- but this isn't the list you want. (What if you encountered a new Truck, but you'd had both its model name and its model year before, just not together -- you'd miss it.)

Similarly, the XPath in the count expression will be wrong.

I think what you're missing is that XPath expressions are evaluated with respect to a context node. This is making your logic hard to construct.

I know the count part is wrong. I think I would put whatever pattern I use
in my for-each in my count to return the same set of results to count.

Nope: the context node has changed, so the expression won't return the same thing.


 I'm
not sure though, because basically what I'm trying to do is create nodes on
the fly matching a particular pattern. The pattern can change, because I
want it for all the years, and for any Model (Which I could make a list of,
but it would be quite long).

Well, sort of. Basically, yours is a grouping problem. (Hint: look up "grouping" in the XSL FAQ.) And it admits of a nice elegant solution if you use the XSLT idiom known as "Muenchian grouping", using a key expression. In fact, your analysis hints at this solution, in stipulating that you want to find nodes based on "a particular pattern": that's what the key will let you do.


So, a key declaration such as

<xsl:key name="TruckModelYear" match="Truck" use="concat(VINModelName, VINModelYearName)"/>

Creates a key you can use to retrieve your trucks. Each Truck is assigned a "TruckModelYear" key whose value is the combination of its VINModelName AND VINModelYearName children.

Then, the instruction

<xsl:for-each select="Truck[generate-id() =
generate-id(key('TruckModelYear', concat(VINModelName, VinModelYearName)))[1]]">


[nb: untested] iterates over your Truck element children -- except it skips those that are not the first that match any given key. [Explanation: it keeps only those whose generated ID matches a generated ID for the first node (in document order) in the set of nodes (Trucks) with the same key value, i.e. same VINModelName and VINModelYearName.]

Then inside the for-each, the expression

count(key('TruckModelYear', concat(VINModelName, VinModelYearName)))

will count the number of nodes matched by the key of the current Truck, that is, the number of Trucks that have the same key as the present Truck (the context node).

This is an advanced answer, I know ... but it's neater and cleaner than writing the (fancy and cumbersome) XPath to do it by brute force.

Hope that helps! (and folks, please check my logic),
Wendell

At 07:42 PM 3/14/01, you wrote:
I'm rather new to XSL (Few months) and am running across a little problem,
hoping someone here can help me. Thanks for your time.

I have the following XML. . .

<Truck-list>
    <Truck>
      <VINModelName>
        T600
      </VINModelName>
      <VINModelYearName>
        1999
      </VINModelYearName>
    </Truck>
    <Truck>
      <VINModelName>
        T100
      </VINModelName>
      <VINModelYearName>
        1999
      </VINModelYearName>
    </Truck>
   <Truck>
      <VINModelName>
        T600
      </VINModelName>
      <VINModelYearName>
        2000
      </VINModelYearName>
    </Truck>
   <Truck>
      <VINModelName>
        T600
      </VINModelName>
      <VINModelYearName>
        1999
      </VINModelYearName>
    </Truck>
</Truck-list>

What I am trying to accomplish is a summary of the truck somewhat like this
(Except in HTML and prettier):

Build Year: 1999
Model: T100
Count: 1

Build Year: 1999
Model: T600
Count: 2

Build Year: 2000
Model: T100
Count: 1

Build Year: 2000
Model: T600
Count: 1

The XSL chunk I have currently is as follows (I think this is the closest
I've been, but I've tried lots of things here):

<xsl:template match="Truck-list">
   [HTML table build]
     <xsl:for-each
select="Truck[not(VINModelYearName/preceding::Truck/VINModelYearName)]
             |Truck[not(VINModelName/preceding::Truck/VINModelName)]">
           <tr>
                  <td class="lensBody" bgcolor="#FFFFFF" nowrap="true">
                  <xsl:value-of select="VINModelYearName"/>&#160;
                  </td>
                  <td class="lensBody" bgcolor="#FFFFFF" nowrap="true">
                  <xsl:value-of select="VINModelName"/>&#160;
                  </td>
              <td class="lensBody" bgcolor="#FFFFFF" nowrap="true">
                  <xsl:value-of select="count(VINModelName)"/>&#160;
                  </td>
         </tr>
     </xsl:for-each>
   </table>
</xsl:template>

====================================================================== Wendell Piez mailto:wapiez@xxxxxxxxxxxxxxxx Mulberry Technologies, Inc. http://www.mulberrytech.com 17 West Jefferson Street Direct Phone: 301/315-9635 Suite 207 Phone: 301/315-9631 Rockville, MD 20850 Fax: 301/315-8285 ---------------------------------------------------------------------- Mulberry Technologies: A Consultancy Specializing in SGML and XML ======================================================================


XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list



Current Thread