Page 1 of 1

sorting my XML

Posted: Sun Aug 19, 2007 12:23 pm
by mrivorey1
feyd | Please use

Code: Select all

,

Code: Select all

and [syntax="..."] tags where appropriate when posting code. Your post has been edited to reflect how we'd like it posted. Please read:  [url=http://forums.devnetwork.net/viewtopic.php?t=21171]Posting Code in the Forums[/url] to learn how to do it too.[/color]


I have an XML file of members in a costuming organization.  I'd like to be able to sort this data variably sometimes on based on last name (with secondary sorting on first name), and other times based on member number (TKID, not USERID below).  The XML looks like this.

[syntax="xml"]

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="member_test.xsl"?>
<garrison>
     <members>
          <member>
              <first>John</first>
              <last>Doe</last>
              <tkid>100</tkid>
              <costume>TK</costume>
              <costume>BH</costume>
              <userid>2545</userid>
          </member>
          <member>
              <first>Joe</first>
              <last>Blow</last>
              <tkid>148</tkid>
              <costume>BH</costume>
              <costume>TK</costume>
              <costume>TB</costume>
              <userid>2612</userid>
          </member>
          <member>
              <first>Jane</first>
              <last>Doe</last>
              <tkid>101</tkid>
              <costume>TK</costume>
              <userid>2612</userid>
          </member>
   </members>
</garrison>


I'm using PHP4. Here is the code I'm using to parse.[/syntax]

Code: Select all

<?php
class xml_member{
    var $first, $last, $tkid, $userid;
    var $costume = array();
}
// this function handles each opening XML tag.  example: <costume>
function startTag($parser, $data){
    global $current_tag;
    $current_tag .= "*$data";
}
// this function handles each closing XML tag.  example: </costume>
function endTag($parser, $data){
    global $current_tag;
    $tag_key = strrpos($current_tag, '*');
    $current_tag = substr($current_tag, 0, $tag_key);
}
// this function handles the data between opening and closing tags.
function contents($parser, $data){
    global $current_tag, $xml_first_key, $xml_last_key, $xml_tkid_key, $xml_userid_key, $xml_costume_key, $counter, $member_array;
    switch($current_tag){
        case $xml_first_key:
            $member_array[$counter] = new xml_member();
            $member_array[$counter]->first = $data;
            break;
        case $xml_last_key:
            $member_array[$counter]->last = $data;
            break;
        case $xml_tkid_key:
            $member_array[$counter]->tkid = $data;
            break;
        case $xml_costume_key:
            $member_array[$counter]->costume[] = $data;
            break;
        case $xml_userid_key:
            $member_array[$counter]->userid = $data;
            $counter++;
            break;
    }
}


As you can see, the relevant data for, lets say the last name, end up in $member_array[$counter]->last


Can someone reply with code that will alternately sort $member_array based on last name (with secondary sort on first), and on TKID?


PS: Please forgive me if my code is crap, I'm still learning, and without proper instruction.


feyd | Please use

Code: Select all

,

Code: Select all

and [syntax="..."] tags where appropriate when posting code. Your post has been edited to reflect how we'd like it posted. Please read:  [url=http://forums.devnetwork.net/viewtopic.php?t=21171]Posting Code in the Forums[/url] to learn how to do it too.[/color]

Posted: Sun Aug 19, 2007 3:20 pm
by volka
You can use xsl(t) to sort the xml data.
e.g. with php5's xls extension

Code: Select all

<?php
$xsl = new XSLTProcessor();
$xsl->importStyleSheet( DOMDocument::load('sort.xsl') );

$doc = DOMDocument::load('data.xml');
$xsl->setParameter('', 'sortBy', 'last');
echo $xsl->transformToXML($doc);

Code: Select all

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="sortBy" select="'userid'"/>

<xsl:template match="* | @*">
  <xsl:copy><xsl:copy-of select="@*"/><xsl:apply-templates/></xsl:copy>
</xsl:template>

<xsl:template match="members">
  <xsl:apply-templates select="member">
    <xsl:sort select="*[name()=$sortBy]" order="ascending" />
  </xsl:apply-templates>
</xsl:template>

</xsl:stylesheet>
There's a similar extension for php4 called xslt
But the lifetime of php4 is nearing its end.

Posted: Sun Aug 19, 2007 3:24 pm
by mrivorey1
volka wrote:There's a similar extension for php4 called xslt
But the lifetime of php4 is nearing its end.
Well the good news is, I just discovered my server has PHP5 as well. It's just not the default.

I'll give this a try.

Posted: Sun Aug 19, 2007 5:15 pm
by mrivorey1
feyd | Please use

Code: Select all

,

Code: Select all

and [syntax="..."] tags where appropriate when posting code. Your post has been edited to reflect how we'd like it posted. Please read:  [url=http://forums.devnetwork.net/viewtopic.php?t=21171]Posting Code in the Forums[/url] to learn how to do it too.[/color]


[quote="volka"][syntax="xsl"]<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="sortBy" select="'userid'"/>

<xsl:template match="* | @*">
  <xsl:copy><xsl:copy-of select="@*"/><xsl:apply-templates/></xsl:copy>
</xsl:template>

<xsl:template match="members">
  <xsl:apply-templates select="member">
    <xsl:sort select="*[name()=$sortBy]" order="ascending" />
  </xsl:apply-templates>
</xsl:template>

</xsl:stylesheet>
[/quote]

Well, it's getting me closer, but the XLS leaves out the MEMBERS element (not to be confused with MEMBER) in the transformed XML. And apparently I don't know enough about XPath to fix it.


I got this:

Code: Select all

<garrison>
     <member>
              <first>John</first>
              <last>Adams</last>
              <tkid>8000</tkid>
              <costume>TK</costume>
              <userid>7326</userid>
          </member><member>
              <first>Beverly</first>
              <last>Adams</last>
              <tkid>8001</tkid>
              <costume>ID</costume>
              <userid>7461</userid>
          </member><member>
              <first>Joe</first>
              <last>Aginon</last>
              <tkid>7000</tkid>
              <costume>TD</costume>
              <costume>TC</costume>
              <userid>4234</userid>
          </member>
</garrison>


I wanted this:

Code: Select all

<garrison>
     <members>
          <member>
              <first>John</first>
              <last>Adams</last>
              <tkid>8000</tkid>
              <costume>TK</costume>
              <userid>7326</userid>
          </member><member>
              <first>Beverly</first>
              <last>Adams</last>
              <tkid>8001</tkid>
              <costume>ID</costume>
              <userid>7461</userid>
          </member><member>
              <first>Joe</first>
              <last>Aginon</last>
              <tkid>7000</tkid>
              <costume>TD</costume>
              <costume>TC</costume>
              <userid>4234</userid>
          </member>
     </members>
</garrison>



feyd | Please use[/syntax]

Code: Select all

,

Code: Select all

and [syntax="..."] tags where appropriate when posting code. Your post has been edited to reflect how we'd like it posted. Please read:  [url=http://forums.devnetwork.net/viewtopic.php?t=21171]Posting Code in the Forums[/url] to learn how to do it too.[/color]

Posted: Sun Aug 19, 2007 6:29 pm
by volka
fortunately that's easy to fix ;)

Code: Select all

<xsl:template match="members">
  <members>
    <xsl:apply-templates select="member">
      <xsl:sort select="*[name()=$sortBy]" order="ascending" />
    </xsl:apply-templates>
  </members>
</xsl:template>

Posted: Sun Aug 19, 2007 6:58 pm
by mrivorey1
SWEET!!! It worked!!! I just don't have enough experience with XSL yet. The obvious fix didn't even occur to me.

Now I just need to figure out how to do a secondary sort on first names (to sort people in the same family).

Posted: Sun Aug 19, 2007 7:23 pm
by volka
http://www.w3.org/TR/xslt#element-sort wrote:Sorting is specified by adding xsl:sort elements as children of an xsl:apply-templates or xsl:for-each element. The first xsl:sort child specifies the primary sort key, the second xsl:sort child specifies the secondary sort key and so on. When an xsl:apply-templates or xsl:for-each element has one or more xsl:sort children, then instead of processing the selected nodes in document order, it sorts the nodes according to the specified sort keys and then processes them in sorted order.

Code: Select all

<xsl:template match="members">
  <members>
    <xsl:apply-templates select="member">
      <xsl:sort select="*[name()='last']" order="ascending" />
      <xsl:sort select="*[name()='first']" order="ascending" />
    </xsl:apply-templates>
  </members>
</xsl:template>