Home > OS >  Sorting an XML file using php
Sorting an XML file using php

Time:10-15

There's an xml file with multiple entries and i'm trying to sort it based on two children. I'm trying to sort it first by a:DESIGN and then by a:COLOR. i've made various attempts but it wont sort no matter what i tried. Any help is highly appreciated.

$xml = file_get_contents("http://79.129.20.64:8580/GlxConnector/GetItemsForXml");
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$list = iterator_to_array($xpath->evaluate('/*/*/*'));
uasort(
  $list,
  function($one, $two) use ($xpath) {
    return strcasecmp(
      $xpath->evaluate('string(a:DESIGN)', $one), 
      $xpath->evaluate('string(a:COLOR)', $two)
    );
  } 
);
$document->saveXML();
//var_dump($list);
$document->save('test1.xml');

Sample XML(a few entries only for demonstration. Uses real keys/values):

<GetItemsForXmlResponse xmlns="http://tempuri.org/">
    <GetItemsForXmlResult xmlns:a="http://schemas.datacontract.org/2004/07/e_ShopConnector.Entities" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:ItemForXML>
            <a:AVAILABILITY>false</a:AVAILABILITY>
            <a:CODE>127-0-368-32118-995</a:CODE>
            <a:COLOR>995</a:COLOR>
            <a:DESIGN>32118</a:DESIGN>
            <a:DIMENSION>160X230</a:DIMENSION>
            <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE>
            <a:PRICE>121.000000000000</a:PRICE>
            <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT>
            <a:SERIES>BOHEME</a:SERIES>
        </a:ItemForXML>
        <a:ItemForXML>
            <a:AVAILABILITY>true</a:AVAILABILITY>
            <a:CODE>126-0-500-22570-795</a:CODE>
            <a:COLOR>795</a:COLOR>
            <a:DESIGN>22570</a:DESIGN>
            <a:DIMENSION>200X250</a:DIMENSION>
            <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE>
            <a:PRICE>145.000000000000</a:PRICE>
            <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT>
            <a:SERIES>SYDNEY</a:SERIES>
        </a:ItemForXML>
        <a:ItemForXML>
            <a:AVAILABILITY>true</a:AVAILABILITY>
            <a:CODE>162-0-120-32118-995</a:CODE>
            <a:COLOR>995</a:COLOR>
            <a:DESIGN>32118</a:DESIGN>
            <a:DIMENSION>080X150</a:DIMENSION>
            <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE>
            <a:PRICE>40.000000000000</a:PRICE>
            <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT>
            <a:SERIES>DELUXE</a:SERIES>
        </a:ItemForXML>
        <a:ItemForXML>
            <a:AVAILABILITY>true</a:AVAILABILITY>
            <a:CODE>136-0-252-75002-022</a:CODE>
            <a:COLOR>022</a:COLOR>
            <a:DESIGN>75002</a:DESIGN>
            <a:DIMENSION>140X180</a:DIMENSION>
            <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE>
            <a:PRICE>76.000000000000</a:PRICE>
            <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT>
            <a:SERIES>PANAMA</a:SERIES>
        </a:ItemForXML>
        <a:ItemForXML>
            <a:AVAILABILITY>true</a:AVAILABILITY>
            <a:CODE>136-0-252-32118-995</a:CODE>
            <a:COLOR>995</a:COLOR>
            <a:DESIGN>32118</a:DESIGN>
            <a:DIMENSION>140X180</a:DIMENSION>
            <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE>
            <a:PRICE>76.000000000000</a:PRICE>
            <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT>
            <a:SERIES>PANAMA</a:SERIES>
        </a:ItemForXML>
    </GetItemsForXmlResponse>
</GetItemsForXmlResult>

expected output(sorted by design and color):

<GetItemsForXmlResponse xmlns="http://tempuri.org/">
    <GetItemsForXmlResult xmlns:a="http://schemas.datacontract.org/2004/07/e_ShopConnector.Entities" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:ItemForXML>
            <a:AVAILABILITY>true</a:AVAILABILITY>
            <a:CODE>162-0-120-32118-995</a:CODE>
            <a:COLOR>995</a:COLOR>
            <a:DESIGN>32118</a:DESIGN>
            <a:DIMENSION>080X150</a:DIMENSION>
            <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE>
            <a:PRICE>40.000000000000</a:PRICE>
            <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT>
            <a:SERIES>DELUXE</a:SERIES>
        </a:ItemForXML>
        <a:ItemForXML>
            <a:AVAILABILITY>true</a:AVAILABILITY>
            <a:CODE>136-0-252-32118-995</a:CODE>
            <a:COLOR>995</a:COLOR>
            <a:DESIGN>32118</a:DESIGN>
            <a:DIMENSION>140X180</a:DIMENSION>
            <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE>
            <a:PRICE>76.000000000000</a:PRICE>
            <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT>
            <a:SERIES>PANAMA</a:SERIES>
        </a:ItemForXML>
        <a:ItemForXML>
            <a:AVAILABILITY>false</a:AVAILABILITY>
            <a:CODE>127-0-368-32118-995</a:CODE>
            <a:COLOR>995</a:COLOR>
            <a:DESIGN>32118</a:DESIGN>
            <a:DIMENSION>160X230</a:DIMENSION>
            <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE>
            <a:PRICE>121.000000000000</a:PRICE>
            <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT>
            <a:SERIES>BOHEME</a:SERIES>
        </a:ItemForXML>
        <a:ItemForXML>
            <a:AVAILABILITY>true</a:AVAILABILITY>
            <a:CODE>126-0-500-22570-795</a:CODE>
            <a:COLOR>795</a:COLOR>
            <a:DESIGN>22570</a:DESIGN>
            <a:DIMENSION>200X250</a:DIMENSION>
            <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE>
            <a:PRICE>145.000000000000</a:PRICE>
            <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT>
            <a:SERIES>SYDNEY</a:SERIES>
        </a:ItemForXML>
        <a:ItemForXML>
            <a:AVAILABILITY>true</a:AVAILABILITY>
            <a:CODE>136-0-252-75002-022</a:CODE>
            <a:COLOR>022</a:COLOR>
            <a:DESIGN>75002</a:DESIGN>
            <a:DIMENSION>140X180</a:DIMENSION>
            <a:DISCOUNT_PERCENTAGE>0.000000000000</a:DISCOUNT_PERCENTAGE>
            <a:PRICE>76.000000000000</a:PRICE>
            <a:PRICE_AFTER_DISCOUNT>0.000000000000</a:PRICE_AFTER_DISCOUNT>
            <a:SERIES>PANAMA</a:SERIES>
        </a:ItemForXML>
    </GetItemsForXmlResponse>
</GetItemsForXmlResult>

CodePudding user response:

It's possible - but it's pretty convoluted. I'm afraid I don't have the time to fully annotate the code, but it basically involves creating a new skeleton document, searching the old document (using xpath) for your two keys, sorting them and then importing nodes from the old document, in that sorted order, into the new document. On top of that, you also have to handle namespaces...

#first - on the old document
$xpath->registerNamespace("a", "http://schemas.datacontract.org/2004/07/e_ShopConnector.Entities");
$designs = $xpath->query('//a:DESIGN/text()');

#create your skeleton new document
$xmlstring = '<GetItemsForXmlResponse xmlns="http://tempuri.org/">
    <GetItemsForXmlResult xmlns:a="http://schemas.datacontract.org/2004/07/e_ShopConnector.Entities" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    </GetItemsForXmlResult>
  </GetItemsForXmlResponse>';
  
$newDoc = new DOMDocument();
$newDoc->loadXML($xmlstring);
$xpath2 = new DOMXPath($newDoc);
$destination = $xpath2->query('//*[local-name()="GetItemsForXmlResult"]');

#and the heavy lifting
foreach($designs as $design) {
    $values[] = $design->nodeValue;
}
$u_des = array_unique(($values));
sort($u_des);
foreach ($u_des as $des){
    $targets = $xpath->query("//a:ItemForXML[.//a:DESIGN/text()= {$des}]");
    foreach ($targets as $target){
        $cols = $xpath->query('.//a:COLOR/text()',$target);
         $col_ar[] = $cols[0]->nodeValue;
          }
          sort($col_ar);
          foreach ($col_ar as $color){
              $finals = $xpath->query("//a:ItemForXML[.//a:DESIGN/text()= {$des}][.//a:COLOR/text()= {$color}]");
              foreach($finals as $final){
                    $node = $newDoc->importNode($final, true);
                    $destination[0]->appendChild($node);
              }
      unset($col_ar);
    }
}
echo $newDoc->saveXML();

Try this on your actual xml and see if it works. If not, it should work with some tinkering.

CodePudding user response:

You can use a powershell

using assembly System
using assembly System.Collections
using assembly System.Xml.Linq

$inputFilename = "c:\temp\test.xml"
$outputFilename = "c:\temp\test1.xml"

$doc = [System.Xml.Linq.XDocument]::Load($inputFilename)

$GetItemsForXmlResult = $doc.Descendants().Where( {$_.Name.LocalName -eq "GetItemsForXmlResult"})
$GetItemsForXmlResult = [System.Linq.Enumerable]::First($GetItemsForXmlResult)

$GetItemsForXmlResult = [System.Xml.Linq.XNode]$GetItemsForXmlResult


$nsA = $GetItemsForXmlResult.GetNamespaceOfPrefix("a")
$xname = [System.Xml.Linq.XName]::Get("ItemForXML",$nsA.NamespaceName)
$sorteditems = $GetItemsForXmlResult.Elements($xname)

$sorteditems = [System.Linq.Enumerable]::ToList($sorteditems)

$sortedtems = $sortedtems | Sort-Object -Property @{ Expression = { $_.Element($nsA   "DESIGN").Value}}, `
                                                  @{ Expression = { $_.Element($nsA   "COLOR").Value}}
$GetItemsForXmlResult.RemoveNodes()
$GetItemsForXmlResult.Add($sorteditems)
$doc.Save($outputFilename)
  • Related