using XSL to count elements
Posted: Fri Oct 15, 2021 7:19 am
Hello! I've been trying for several weeks to learn XSL and use it to manipulate some data we receive in XML format, but I'm not finding a whole lot of resources for this language on the Internet (so if you've seen this same question posted somewhere else, I apologize for the duplication). Here's an example of the data files we receive (scrubbed of confidential information), the XSL code I came up with to manipulate it, and the resulting output:
Sample-Input.xml
Sample-Code.xsl
Sample-Output.html
(I know I have more problems with this code than just the one I asked about here, but I have a feeling that once I understand what I'm doing wrong with this problem, the other pieces will fall into place.)
The answers I'm expecting to get in the count column should be 3, 1, 0, and 3; but it seems to be counting not just descendants, but all the matching data points from the current position to the end - and recounting them every time the position changes.
My question is: Why is it that the "following-sibling" key word that I'm using is finding not only siblings (nodes having the same parent node), but also "cousins" (nodes at the same level, regardless of which node is the parent)? And, of course, how do I fix it?
Sample-Input.xml
Code: Select all
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Entry1</key>
<dict>
<key>EntryCollectionA</key>
<dict>
<key>ECA Identifier</key>
<dict>
<key>ECADetail1</key>
<string>The quick brown fox</string>
<key>ECADetail2</key>
<integer>1112</integer>
</dict>
</dict>
<key>EntryCollectionB</key>
<dict>
<key>Identifier for the first ECB</key>
<dict>
<key>ECBMinorDetail1</key>
<false/>
<key>ECBMinorDetail2</key>
<dict>
<key>EBC2's name</key>
<string>jumps over the lazy dog.</string>
</dict>
<key>ECBImportantDetail3</key>
<string>three</string>
<key>ECBImportantDetail4</key>
<string>four</string>
<key>ECBImportantDetail5</key>
<string>five</string>
<key>ECBIrrelevantToMe</key>
<data>
yadda+yadda+yadda/yadda+yaddayaddayaddayadda
</data>
<key>ECBIrrelevantToo</key>
<integer>987654321</integer>
</dict>
<key>second ECB for Entry1 has a different title</key>
<dict>
<key>ECBMinorDetail1</key>
<false/>
<key>ECBMinorDetail2</key>
<dict>
<key>a name for Ecb2 goes here</key>
<string>vaults above the recalcitrant canine.</string>
</dict>
<key>ECBImportantDetail3</key>
<string>is the number of the count,</string>
<key>ECBImportantDetail4</key>
<string>and what the number of the count shall be</string>
<key>ECBImportantDetail5</key>
<string>is right out.</string>
<key>ECBIrrelevantToMe</key>
<data>
MORE/inde+cipher+able/and+IRReleVaNTinbase64
</data>
<key>ECBIrrelevantToo</key>
<integer>19283746</integer>
</dict>
<key>Entry1 third ECB</key>
<dict>
<key>ECBMinorDetail1</key>
<true/>
<key>ECBMinorDetail2</key>
<dict>
<key>Ecb3 has a name</key>
<string>is intimidated by the sleeping dragon.</string>
</dict>
<key>ECBImportantDetail3</key>
<string>is a magic number</string>
<key>ECBImportantDetail4</key>
<string>is the legs in a zoo</string>
<key>ECBImportantDetail5</key>
<string>ready or not, here I come!</string>
<key>ECBIrrelevantToMe</key>
<data>
WiLlY0USt1lLFe3dMe/WhEnImSiXtYfOuR/60plUsF0R
</data>
<key>ECBIrrelevantToo</key>
<integer>6406406566</integer>
</dict>
</dict>
<key>ImportantDatum6</key>
<string>what this data pertains to</string>
<key>ImportantDatum7</key>
<string>reference code</string>
<key>ImportantDatum8</key>
<string>one,two</string>
<key>whoCares</key>
<integer>1</integer>
<key>iDo-aboutThisOne</key>
<string>999</string>
<key>ANDagain</key>
<string>eeny</string>
<key>moreSillyData</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
<key>bucketObits</key>
<string>more characters</string>
<key>bitObuckets</key>
<string>textity textext</string>
<key>amount</key>
<integer>66645714</integer>
<key>code</key>
<integer>765432109</integer>
<key>subcode</key>
<integer>828474104</integer>
</dict>
<key>second entry</key>
<dict>
<key>EntryCollectionA</key>
<dict>
<key>5564738291</key>
<dict>
<key>wardrobe</key>
<string>lionWitch</string>
<key>buuleeun</key>
<false/>
</dict>
</dict>
<key>EntryCollectionB</key>
<dict>
<key>2's ECB name</key>
<dict>
<key>tuuleeun</key>
<false/>
<key>batchCodes</key>
<dict>
<key>5564738291</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
</dict>
<key>ImportantDatum7</key>
<string>sequence identifier</string>
<key>ImportantDatum8</key>
<string>three,four</string>
<key>iDo-aboutThisOne</key>
<string>thuh-ree</string>
<key>moreSillyData</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
<key>subcode</key>
<integer>841391812</integer>
</dict>
</dict>
<key>ImportantDatum6</key>
<string>another collection of data</string>
<key>ImportantDatum7</key>
<string>identifier string</string>
<key>ImportantDatum8</key>
<string>buckle my shoe</string>
<key>whoCares</key>
<integer>1</integer>
<key>iDo-aboutThisOne</key>
<string>again</string>
<key>ANDagain</key>
<string>meeny</string>
<key>moreSillyData</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
<key>bucketObits</key>
<string>say what you want</string>
<key>amount</key>
<integer>51736576</integer>
<key>code</key>
<integer>456789012</integer>
<key>subcode</key>
<integer>841391812</integer>
</dict>
<key>another main entry</key>
<dict>
<key>EntryCollectionA</key>
<dict>
<key>5564738291</key>
<dict>
<key>wardrobe</key>
<string>lionWitch</string>
<key>buuleeun</key>
<false/>
</dict>
</dict>
<key>ImportantDatum6</key>
<string>my third data set</string>
<key>ImportantDatum7</key>
<string>ID code</string>
<key>ImportantDatum8</key>
<string>five,six</string>
<key>whoCares</key>
<integer>1</integer>
<key>iDo-aboutThisOne</key>
<string>tenAgain</string>
<key>ANDagain</key>
<string>miney</string>
<key>moreSillyData</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
<key>bucketObits</key>
<string>more to say</string>
<key>amount</key>
<integer>1576960</integer>
<key>code</key>
<integer>1248163264</integer>
<key>subcode</key>
<integer>842184218</integer>
</dict>
<key>final sample</key>
<dict>
<key>EntryCollectionA</key>
<dict>
<key>5564738291</key>
<dict>
<key>wardrobe</key>
<string>lionWitch</string>
<key>buuleeun</key>
<false/>
</dict>
</dict>
<key>EntryCollectionB</key>
<dict>
<key>ECB (but for #4?)</key>
<dict>
<key>tuuleeun</key>
<false/>
<key>batchCodes</key>
<dict>
<key>5564738291</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
</dict>
<key>ImportantDatum7</key>
<string>name, of sorts</string>
<key>ImportantDatum8</key>
<string>nine,ten</string>
<key>iDo-aboutThisOne</key>
<string>tenAgain</string>
<key>moreSillyData</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
<key>subcode</key>
<integer>84218421</integer>
</dict>
<key>four's second ECB</key>
<dict>
<key>tuuleeun</key>
<false/>
<key>batchCodes</key>
<dict>
<key>5564738291</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
</dict>
<key>ImportantDatum7</key>
<string>three is also 7?</string>
<key>ImportantDatum8</key>
<string>eight is also four</string>
<key>iDo-aboutThisOne</key>
<string>cra-zee</string>
<key>moreSillyData</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
<key>subcode</key>
<integer>474828131</integer>
</dict>
<key>369,121518</key>
<dict>
<key>tuuleeun</key>
<false/>
<key>batchCodes</key>
<dict>
<key>5564738291</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
</dict>
<key>ImportantDatum7</key>
<string>code name for this item</string>
<key>ImportantDatum8</key>
<string>close the door</string>
<key>iDo-aboutThisOne</key>
<string>heaven</string>
<key>moreSillyData</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
<key>subcode</key>
<integer>963852741</integer>
</dict>
</dict>
<key>ImportantDatum6</key>
<string>and here's another group of data</string>
<key>ImportantDatum7</key>
<string>code name for this item</string>
<key>ImportantDatum8</key>
<string>do it again</string>
<key>whoCares</key>
<integer>1</integer>
<key>iDo-aboutThisOne</key>
<string>heaven</string>
<key>ANDagain</key>
<string>moe</string>
<key>moreSillyData</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
</data>
<key>bucketObits</key>
<string>piece of text</string>
<key>amount</key>
<integer>19189760</integer>
<key>code</key>
<integer>567890123</integer>
<key>supportedDeviceTypes</key>
<integer>7</integer>
<key>subcode</key>
<integer>303030303</integer>
</dict>
</dict>
</plist>
Code: Select all
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>Heading and preliminary stuff</h2>
<table>
<tr>
<th bgcolor="#336699">Main</th>
<th>count</th>
<th bgcolor="#3366cc">EntryCollectionB</th>
<th bgcolor="#339966">EntryCollectionB/<br/>
ECBImportantDetail3</th>
<th bgcolor="#3399cc">EntryCollectionB/<br/>
ECBImportantDetail4</th>
<th bgcolor="#33cc66">EntryCollectionB/<br/>
ECBImportantDetail5</th>
<th bgcolor="#33cc99">ImportantDatum6</th>
<th bgcolor="#663399">ImportantDatum7</th>
<th bgcolor="#6633cc">ImportantDatum8</th>
<th bgcolor="#669933">iDo-aboutThisOne</th>
<th bgcolor="#6699cc">ANDagain</th>
<th bgcolor="#66cc33">amount</th>
</tr>
<xsl:for-each select="plist/dict/key">
<tr>
<td bgcolor="#66cc99"><xsl:value-of select="." /></td>
<td><xsl:value-of select="count(following-sibling::dict/key[text()='EntryCollectionB']/
following-sibling::dict/key)" /></td>
<td bgcolor="#993366"><xsl:value-of select="following-sibling::dict/key[text()='EntryCollectionB']/
following-sibling::dict/key" /></td>
<td bgcolor="#9933cc"><xsl:value-of select="following-sibling::dict/key[text()='EntryCollectionB']/
following-sibling::dict/key/
following-sibling::dict/key[text()='ECBImportantDetail3']/
following-sibling::string" /></td>
<td bgcolor="#996633"><xsl:value-of select="following-sibling::dict/key[text()='EntryCollectionB']/
following-sibling::dict/key/
following-sibling::dict/key[text()='ECBImportantDetail4']/
following-sibling::string" /></td>
<td bgcolor="#9966cc"><xsl:value-of select="following-sibling::dict/key[text()='EntryCollectionB']/
following-sibling::dict/key/
following-sibling::dict/key[text()='ECBImportantDetail5']/
following-sibling::string" /></td>
<td bgcolor="#99cc33"><xsl:value-of select="following-sibling::dict/key[text()='ImportantDatum6']/
following-sibling::string" /></td>
<td bgcolor="#99cc66"><xsl:value-of select="following-sibling::dict/key[text()='ImportantDatum7']/
following-sibling::string" /></td>
<td bgcolor="#cc3366"><xsl:value-of select="following-sibling::dict/key[text()='ImportantDatum8']/
following-sibling::string" /></td>
<td bgcolor="#cc3399"><xsl:value-of select="following-sibling::dict/key[text()='iDo-aboutThisOne']/
following-sibling::string" /></td>
<td bgcolor="#cc6633"><xsl:value-of select="following-sibling::dict/key[text()='ANDagain']/
following-sibling::string" /></td>
<td bgcolor="#cc6699"><xsl:value-of select="following-sibling::dict/key[text()='amount']/
following-sibling::integer" /></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Code: Select all
<!DOCTYPE HTML><html>
<body>
<h2>Heading and preliminary stuff</h2>
<table>
<tr>
<th bgcolor="#336699">Main</th>
<th>count</th>
<th bgcolor="#3366cc">EntryCollectionB</th>
<th bgcolor="#339966">EntryCollectionB/<br>
ECBImportantDetail3</th>
<th bgcolor="#3399cc">EntryCollectionB/<br>
ECBImportantDetail4</th>
<th bgcolor="#33cc66">EntryCollectionB/<br>
ECBImportantDetail5</th>
<th bgcolor="#33cc99">ImportantDatum6</th>
<th bgcolor="#663399">ImportantDatum7</th>
<th bgcolor="#6633cc">ImportantDatum8</th>
<th bgcolor="#669933">iDo-aboutThisOne</th>
<th bgcolor="#6699cc">ANDagain</th>
<th bgcolor="#66cc33">amount</th>
</tr>
<tr>
<td bgcolor="#66cc99">Entry1</td>
<td>7</td>
<td bgcolor="#993366">Identifier for the first ECB</td>
<td bgcolor="#9933cc">three</td>
<td bgcolor="#996633">four</td>
<td bgcolor="#9966cc">five</td>
<td bgcolor="#99cc33">what this data pertains to</td>
<td bgcolor="#99cc66">reference code</td>
<td bgcolor="#cc3366">one,two</td>
<td bgcolor="#cc3399">999</td>
<td bgcolor="#cc6633">eeny</td>
<td bgcolor="#cc6699">66645714</td>
</tr>
<tr>
<td bgcolor="#66cc99">second entry</td>
<td>4</td>
<td bgcolor="#993366">2's ECB name</td>
<td bgcolor="#9933cc"></td>
<td bgcolor="#996633"></td>
<td bgcolor="#9966cc"></td>
<td bgcolor="#99cc33">another collection of data</td>
<td bgcolor="#99cc66">identifier string</td>
<td bgcolor="#cc3366">buckle my shoe</td>
<td bgcolor="#cc3399">again</td>
<td bgcolor="#cc6633">meeny</td>
<td bgcolor="#cc6699">51736576</td>
</tr>
<tr>
<td bgcolor="#66cc99">another main entry</td>
<td>3</td>
<td bgcolor="#993366">ECB (but for #4?)</td>
<td bgcolor="#9933cc"></td>
<td bgcolor="#996633"></td>
<td bgcolor="#9966cc"></td>
<td bgcolor="#99cc33">my third data set</td>
<td bgcolor="#99cc66">ID code</td>
<td bgcolor="#cc3366">five,six</td>
<td bgcolor="#cc3399">tenAgain</td>
<td bgcolor="#cc6633">miney</td>
<td bgcolor="#cc6699">1576960</td>
</tr>
<tr>
<td bgcolor="#66cc99">final sample</td>
<td>3</td>
<td bgcolor="#993366">ECB (but for #4?)</td>
<td bgcolor="#9933cc"></td>
<td bgcolor="#996633"></td>
<td bgcolor="#9966cc"></td>
<td bgcolor="#99cc33">and here's another group of data</td>
<td bgcolor="#99cc66">code name for this item</td>
<td bgcolor="#cc3366">do it again</td>
<td bgcolor="#cc3399">heaven</td>
<td bgcolor="#cc6633">moe</td>
<td bgcolor="#cc6699">19189760</td>
</tr>
</table>
</body>
</html>
The answers I'm expecting to get in the count column should be 3, 1, 0, and 3; but it seems to be counting not just descendants, but all the matching data points from the current position to the end - and recounting them every time the position changes.
My question is: Why is it that the "following-sibling" key word that I'm using is finding not only siblings (nodes having the same parent node), but also "cousins" (nodes at the same level, regardless of which node is the parent)? And, of course, how do I fix it?