Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Using External XML to decide loop 2

Status
Not open for further replies.

oharab

Programmer
May 21, 2002
2,152
GB
I have an XML file that looks like
Code:
Data.xml
<z:row crime_no='XX/06/1264'
          date_ent='2006-02-02T00:00:00'
          ho_class='28/3'
          division='HD'
          beat='8'
          CrimeType_Code='4' />
<z:row crime_no='XX/06/1265'
          date_ent='2006-02-02T00:00:00'
          ho_class='28/3'
          division='AA'
          beat='2'
          CrimeType_Code='3' />
<z:row crime_no='XX/06/1266'
          date_ent='2006-02-02T00:00:00'
          ho_class='18/9'
          division='CA'
          beat='11'
          CrimeType_Code='1' />
etc.
I want to turn it into a nice html table with division across the top,
CrimeType_Code down the side and the counts as the data.

The list of divisions changes from time to time, so I have this in an
external file
Code:
Divisions.xml
<?xml version="1.0" encoding="utf-8" ?>
<divisions>
   <division>
       <code>AA</code>
       <title>Alpha Division</title>
   </division>
   <division>
       <code>AC</code>
       <title>Bravo Division</title>
   </division>
   <division>
       <code>CA</code>
       <title>Charlie Division</title>
   </division>
</divisions>

I can't get XSL to loop through Divisions.xml and use the code as a
parameter for Data.xml. So far I've got:
Code:
<xsl:variable name="divLook"
    select="document('Divisions.xml')"/>
<!--store divisions-->
and 14 lots of
Code:
<td>Other Violence</td>
   <xsl:for-each select="$divLook/divisions/division/code">
   <xsl:variable name="divF" select="." />
   <td><xsl:value-of
select="count(//xml/rs:data/z:row[@CrimeType_Code='2' and
@division=$divF" /></td>
</xsl:for-each>
but it's not working.
I think it's looping through each element in $divLook, saving the
variable, then trying to find "//xml/rs:data/z:row" in the $divLook variable.

Please help.

Thanks.

Ben

----------------------------------------------
Ben O'Hara
David W. Fenton said:
We could be confused in exactly the same way, but confusion might be like Nulls, and not comparable.
 
I would do something like this (make sure you include the correct namespaces and I've assumed your XML is structure is xml/rs:data/z:row):
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform"[/URL] xmlns:rs="thenamespace" xmlns:z="thenamespace">
  <xsl:output method="xml" indent="yes" encoding="iso-8859-1" doctype-public="-//W3C//DTD XHTML 1.1//EN" doctype-system="[URL unfurl="true"]http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[/URL] omit-xml-declaration="yes"/>
  <xsl:template match="/">
    <xsl:apply-templates select="xml/rs:data"/>
  </xsl:template>
  <xsl:template match="rs:data">
    <html>
      <head>
        <title>Crime table</title>
      </head>
      <body>
        <table cellpadding="3" border="1" style="border-collapse: collapse;">
          <thead>
            <tr>
              <th>Crime Type Code</th>
              <xsl:apply-templates select="z:row[not(@division = preceding-sibling::z:row/@division)]" mode="header"/>
            </tr>
          </thead>
          <tbody>
            <xsl:apply-templates select="z:row[not(@CrimeType_Code = preceding-sibling::z:row/@CrimeType_Code)]" mode="body">
              <xsl:sort select="@CrimeType_Code"/>
            </xsl:apply-templates>
          </tbody>
        </table>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="z:row" mode="header">
    <th>
      <xsl:value-of select="document('Divisions.xml')/divisions/division[code== current()/@division]/title"/>
    </th>
  </xsl:template>
  <xsl:template match="z:row" mode="body">
    <xsl:variable name="CrimeType_Code">
      <xsl:value-of select="@CrimeType_Code"/>
    </xsl:variable>
    <tr>
      <td>
        <xsl:value-of select="$CrimeType_Code"/>
      </td>
      <xsl:for-each select="../z:row[not(@division = preceding-sibling::z:row/@division)]">
        <td>
          <xsl:value-of select="count(../z:row[@CrimeType_Code = $CrimeType_Code][@division = current()/@division])"/>
        </td>
      </xsl:for-each>
    </tr>
  </xsl:template>
</xsl:stylesheet>

Jon

"I don't regret this, but I both rue and lament it.
 
A direct fix to the op's realization is this, with the problem as I see it.

>[tt] xsl:value-of select="count(//xml/rs:data/z:row[@CrimeType_Code='2' and @division=$divF" />[/tt]

It should be replaced by this.

[tt] xsl:value-of select="count(//xml/rs:data/z:row[@CrimeType_Code='2' and @division=[red]string([/red]$divF[red])[/red][highlight]][/highlight]" />[/tt]

There is a typo as highlighted. Main problem is the node in variable has to be converted for comparison. That is what I can come up with. I may miss the point as we see only a portion of the doc and rest is guessing.

As a note aside, I would be very cautious in using xml as a tag name. There is no problem with it as such. Just to remind that if you use xml as a namespace prefix it would not be allowed.
 
Amendment:

There still misses a closing parenthesis! This is a re-take of the main line including angle brackets.
[tt]
<xsl:value-of select="count(//xml/rs:data/z:row[@CrimeType_Code='2' and @division=string($divF)][red])[/red]" />
[/tt]
 
string($divF) won't make a difference, because the variable is already a string.

You'll definitely need the closing parenthesis, but most likely reason is you have not specified the namespaces in the XSL (prefixed nodes won't match without specify the namespace).

Jon

"I don't regret this, but I both rue and lament it.
 
>string($divF) won't make a difference, because the variable is already a string.
Are you sure? Are we going to have another debat for that?
 
I eventually think there is a mixed up on the context of nodes where count() would conduct.

Let me illustrate this with the xsl which will successfully count. The major device is to make another variable pointing to the original data xml document.

The xsl sheet.
[tt]
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl=" xmlns:rs="uri-oharab-rs" xmlns:z="uri-oharab-z">
<xsl:eek:utput method="text" />
<xsl:variable name="divLook" select="document('Divisions.xml')"/>
<xsl:template match="/">
[blue]<xsl:variable name="orgdoc" select="." />[/blue]
<xsl:for-each select="$divLook/divisions/division/code">
<xsl:variable name="divF" select="." />
<xsl:text>[count:</xsl:text>
<xsl:value-of select="count([red]$orgdoc[/red]/[blue]root[/blue]/rs:data/z:row[@division = $divF])" />
<xsl:text>]</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
[/tt]
[II] The data xml file (Data.xml). I have added some ancestor, in particular, I use root instead of xml that I tried to avoid.
[tt]
<?xml version="1.0"?>
<root xmlns:rs="uri-oharab-rs" xmlns:z="uri-oharab-z">
<rs:data>
<z:row crime_no='XX/06/1264'
date_ent='2006-02-02T00:00:00'
ho_class='28/3'
division='HD'
beat='8'
CrimeType_Code='4' />
<z:row crime_no='XX/06/1265'
date_ent='2006-02-02T00:00:00'
ho_class='28/3'
division='AA'
beat='2'
CrimeType_Code='3' />
<z:row crime_no='XX/06/1266'
date_ent='2006-02-02T00:00:00'
ho_class='18/9'
division='CA'
beat='11'
CrimeType_Code='1' />
</rs:data>
</root>
[/tt]
[III] Divisions.xml - Nothing change needed.

With these setting, the correct output will show you the count is conducting correctly (respectively AA, AC and CA).

[blue][tt][count:1][count:0][count:1][/tt][/blue]

That is the main thing one needed to make sure count() function.
 
You don't like being contradicted do you?

"string($divF) won't make a difference, because the variable is already a string."

No debate needed. It's a fact.

Jon

"I don't regret this, but I both rue and lament it.
 
>[tt]You don't like being contradicted do you?[/tt]
That's not a problem. I know I have a fair chance to be wrong. Only problem is you don't see all the facts and I don't see all the facts. It is not certain that divF is itself not something else. In this case, after looking more at the data exposed in the original post, may be you are right. Okay? I kept the op's approach as closely as I can and salvage whatever can. You post something else to your liking. That's the problem.
 
Hi,
I've had a go at it and to be honest it's gonna take a bit before it sinks in fully!
tsuji,
Your original suggestion of string($divF) didn't work as it's not the variable type that was the problem, it was the way I was doing the looping. I was looping through the external document and trying to plug results of that into the internal document.
I've not had chance to try your next one, but it seems to have potential, I'll let you know how that works out.
Jon,
Your XSL does exactly what was needed, kinda, but there may not be every CrimeType_code in the data file, and those that are missing need to show zeroes, so I need to understand your code a bit better before I can integrate it with my full solution.
Thanks both for your ideas, it's given me a few more techniques in my arsenal.

Ben
(I'll send stars as soon as my PC is fixed. Popups aren't working along with a load of other stuff!)

----------------------------------------------
Ben O'Hara
David W. Fenton said:
We could be confused in exactly the same way, but confusion might be like Nulls, and not comparable.
 
OK tsuji, no probs. Its good to get different opinions.

I post something else to show there are different ways to approach a problem, and I try to make the code as concise, easy to read and elegant as possible. In this case, its better to use grouping than have 14 lots of code doing the same thing. Plus I try to encourage web standards and semantics.

Ben,

If there's anything you don't understand, just ask. The grouping:
Code:
<xsl:apply-templates select="z:row[not(@division = preceding-sibling::z:row/@division)]"/>
is explained in:


If you need to loop for every crimetype_code up to a certain number, you'll need to use iteration. As XSL is essentially a functional language, you have to do iteration via recursion (which is alien to most people who come from an imperative language background):
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform"[/URL] xmlns:rs="thenamespace" xmlns:z="thenamespace">
  <xsl:output method="xml" indent="yes" encoding="iso-8859-1" doctype-public="-//W3C//DTD XHTML 1.1//EN" doctype-system="[URL unfurl="true"]http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"[/URL] omit-xml-declaration="yes"/>
  <xsl:template match="/">
    <xsl:apply-templates select="xml/rs:data"/>
  </xsl:template>
  <xsl:template match="rs:data">
    <html>
      <head>
        <title>Crime table</title>
      </head>
      <body>
        <table cellpadding="3" border="1" style="border-collapse: collapse;">
          <thead>
            <tr>
              <th>Crime Type Code</th>
              <xsl:apply-templates select="z:row[not(@division = preceding-sibling::z:row/@division)]" mode="header"/>
            </tr>
          </thead>
          <tbody>
            <xsl:call-template name="CrimeType_Codes">
              <xsl:with-param name="currentcode" select="1"/>
              <xsl:with-param name="lastcode" select="14"/>
            </xsl:call-template>
          </tbody>
        </table>
      </body>
    </html>
  </xsl:template>
  <xsl:template match="z:row" mode="header">
    <th>
      <xsl:value-of select="document('Divisions.xml')/divisions/division[code== current()/@division]/title"/>
    </th>
  </xsl:template>
  <xsl:template name="CrimeType_Codes">
    <xsl:param name="currentcode"/>
    <xsl:param name="lastcode"/>
    <tr>
      <td>
        <xsl:value-of select="$currentcode"/>
      </td>
      <xsl:for-each select="z:row[not(@division = preceding-sibling::z:row/@division)]">
        <td>
          <xsl:value-of select="count(../z:row[@CrimeType_Code = $currentcode][@division = current()/@division])"/>
        </td>
      </xsl:for-each>
    </tr>
    <xsl:if test="$currentcode &lt; $lastcode">
      <xsl:call-template name="CrimeType_Codes">
        <xsl:with-param name="currentcode" select="$currentcode + 1"/>
        <xsl:with-param name="lastcode" select="$lastcode"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Jon

"I don't regret this, but I both rue and lament it.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top