0

I need to sort the following XML (foreach ProgramList) based on the value of it's child MajorDescription

<ArrayOfProgramList xmlns="http://schemas.datacontract.org/2004/07/Taca.Resources" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ProgramList>
    <AreaOfInterests xmlns:a="http://schemas.datacontract.org/2004/07/Taca">
        <a:AreaOfInterest>
            <a:Interest>ABORIGINAL STUDIES</a:Interest>
        </a:AreaOfInterest>
    </AreaOfInterests>
    <Coop>true</Coop>
    <MajorDescription>ABORIGINAL COMMUNITY AND SOCIAL DEVELOPMENT</MajorDescription>
    <Program>ACSD</Program>
    <ProgramLocations>
        <ProgramLocation>
            <Campus>Barrie</Campus>
        </ProgramLocation>
    </ProgramLocations>
    <Term>201210</Term>
</ProgramList>
<ProgramList>
    <AreaOfInterests xmlns:a="http://schemas.datacontract.org/2004/07/Taca">
        <a:AreaOfInterest>
            <a:Interest>GRADUATE CERTIFICATE STUDIES</a:Interest>
        </a:AreaOfInterest>
        <a:AreaOfInterest>
            <a:Interest>HEALTH AND WELLNESS STUDIES</a:Interest>
        </a:AreaOfInterest>
    </AreaOfInterests>
    <Coop>false</Coop>
    <MajorDescription>ADVANCED CARE PARAMEDIC</MajorDescription>
    <Program>PARM</Program>
    <ProgramLocations>
        <ProgramLocation>
            <Campus>Barrie</Campus>
        </ProgramLocation>
    </ProgramLocations>
    <Term>201210</Term>
</ProgramList>
</ArrayOfProgramList>

I'm trying to do it with SimpleDOM as I've read thats the easiest way to sort XML on other SO questions.

I've tried using:

foreach($listxml->sortedXPath('//ArrayOfProgramList/ProgramList','//ArrayOfProgramList/ProgramList/MajorDescription') as $program){ ... }

and various other similar 'sort' values such as '@MajorDescription', '/MajorDescription' and '.' as suggested here How does one use SimpleDOM sortedXPath to sort on node value? but everything returns an empty array when I check it with var_dump()

I think the problem is that I need to sort on the value of a child node - is this possible? The foreach needs to be on ProgramList as I need to output the values of all the child nodes within ProgramList on each iteration.

Any suggestions? I don't have to use SimpleDOM, I'm open to any method that works - currently I'm iterating through an array of A-Z, and for each letter, iterating the ProgramList, comparing the first letter of MajorDescription to the current letter and outputting if it matches - this is obviously not ideal and only sorts the first letter...

Community
  • 1
  • 1
tsdexter
  • 2,911
  • 4
  • 36
  • 59

2 Answers2

1

You can try to put all the ProgramList elements into an array and sort it according to a custom function. The code should look like this:

function cmp($a, $b)
{
  return strcmp($a->MajorDescription[0],$b->MajorDescription[0])
}

$arr = $listxml->xpath("//ArrayOfProgramList/ProgramList");
usort($arr,"cmp");
Mircea
  • 915
  • 6
  • 12
  • thanks - your answer inspired me to look back at usort - I couldn't get it working exactly with your code but it is working via usort using the answer from this question http://stackoverflow.com/questions/8274193/sort-xml-div-by-child-node-php-simplexml – tsdexter Jan 10 '13 at 19:23
0

There are two problems with your original code. The first is that your XML uses a default namespace, and by design, XPath doesn't support default namespaces so you have to look for namespaced node (e.g. //foo:bar, not //bar) to find them. If you cannot register a prefix for this namespace (for example, if you cannot modify the source XML) you can match namespaced nodes using the wildcard //* combined with a predicate that matches the node's namespace and/or local name.

$nsPredicate = '[namespace-uri() = "http://schemas.datacontract.org/2004/07/Taca.Resources"]';

$query = '//*[local-name() = "ArrayOfProgramList"]' . $nsPredicate
       . '/*[local-name() = "ProgramList"]' . $nsPredicate;

$orderBy = '*[local-name() = "MajorDescription"]' . $nsPredicate;

foreach ($listxml->sortedXPath($query, $orderBy) as $program)
{
    echo $program->asXML(),"\n";
}

The other problem is with your sort criterion. It should be written from the target node's context.

Josh Davis
  • 28,400
  • 5
  • 52
  • 67