Here's the problem:
I have a given XML structure, similar to this one:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<programs>
<program>data_one</program>
<program>data_two</program>
<program>data_three</program>
</programs>
<periods>
<period date="2015-10-31T00:00:00+08:00">
<data_one>12.33</data_one>
<data_two>5.11</data_two>
<data_three>1544.22</data_three>
</period>
<period date="2015-12-31T00:00:00+08:00">
<data_one>19.33</data_one>
<data_two>123.44</data_two>
<data_three>999.12</data_three>
</period>
<period date="2015-11-30T00:00:00+08:00">
<data_one>1.01</data_one>
<data_two>19.23</data_two>
<data_three>234.11</data_three>
</period>
<period date="2016-01-31T00:00:00+08:00">
<data_one>69.33</data_one>
<data_two>80.12</data_two>
<data_three>65.87</data_three>
</period>
</periods>
</root>
I then used this little trick to convert part of the XML to an array (thanks to the answers I found here):
// convert periods from XML to array
$data = simplexml_load_string($this->xml->periods->asXML(), null, LIBXML_NOCDATA);
$json = json_encode($data);
$xml_as_array = json_decode($json, true);
That was done, because I needed to sort the the 'period' by date. This was quit simple done like this:
// create the sort-keys
foreach ($xml_as_array['period'] as $key => $row) {
$dates[$key] = $row['@attributes']['date'];
}
// sort by date
array_multisort($dates, SORT_ASC, $xml_as_array['period']);
A check with var_dump() shows, that the array is correctly sorted:
array(1) {
["period"]=>
array(4) {
[0]=>
array(4) {
["@attributes"]=>
array(1) {
["date"]=>
string(25) "2015-10-31T00:00:00+08:00"
}
["data_one"]=>
string(5) "12.33"
["data_two"]=>
string(4) "5.11"
["data_three"]=>
string(7) "1544.22"
}
[1]=>
array(4) {
["@attributes"]=>
array(1) {
["date"]=>
string(25) "2015-11-30T00:00:00+08:00"
}
["data_one"]=>
string(4) "1.01"
["data_two"]=>
string(5) "19.23"
["data_three"]=>
string(6) "234.11"
}
[2]=>
array(4) {
["@attributes"]=>
array(1) {
["date"]=>
string(25) "2015-12-31T00:00:00+08:00"
}
["data_one"]=>
string(5) "19.33"
["data_two"]=>
string(6) "123.44"
["data_three"]=>
string(6) "999.12"
}
[3]=>
array(4) {
["@attributes"]=>
array(1) {
["date"]=>
string(25) "2016-01-31T00:00:00+08:00"
}
["data_one"]=>
string(5) "69.33"
["data_two"]=>
string(5) "80.12"
["data_three"]=>
string(5) "65.87"
}
}
}
Now we come to the real problem: converting the array back to an XML structure similar as the original one (and as a bonus to replace the original '' with it). I tried the usual recursive approach, but badly failed on creating the '' nodes with the 'date' attribute. There are some related questions here on stackoverflow addressing the array to xml conversion issue, but none of them worked...(therefor I'm not going to post them. Maybe its easier to come up with something fresh instead of trying to make my really bad code somehow to work ;-) For any solutions, ideas or tipps I'd be very thankful.
Cheers, hopfi2k
Consider using XSLT which you could bypass the PHP array sort altogether. As information, XSLT is a special-purpose declarative language (same type as SQL, sadly not as popular) used primarily to restructure/re-format/re-design XML files which your sort is exactly a native need as xslt maintains the <xslt:sort>
method. Also, PHP like other general purpose languages (C#, Java, Python) maintains an XSLT 1.0 processor and so can run the transform to even overwrite the original XML file:
XSLT Script (save .xsl or .xslt file)
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<!-- Identity Transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Sort periods by individual period's date attribute -->
<xsl:template match="periods">
<xsl:copy>
<xsl:apply-templates select="period">
<xsl:sort select="@date" order="ascending"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:transform>
PHP Script
// LOAD FILES
$doc = new DOMDocument();
$doc->load('Input.xml');
$xsl = new DOMDocument;
$xsl->load('XSLTfile.xsl');
// CONFIGURE TRANSFORMER
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
// TRANSFORM XML SOURCE
$newXml = $proc->transformToXML($doc);
// SAVE OUTPUT TO FILE
$xmlfile ='Output.xml'; // USE SAME NAME TO OVERWRITE ORIGINAL
file_put_contents($xmlfile, $newXml);
Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<programs>
<program>data_one</program>
<program>data_two</program>
<program>data_three</program>
</programs>
<periods>
<period date="2015-10-31T00:00:00+08:00">
<data_one>12.33</data_one>
<data_two>5.11</data_two>
<data_three>1544.22</data_three>
</period>
<period date="2015-11-30T00:00:00+08:00">
<data_one>1.01</data_one>
<data_two>19.23</data_two>
<data_three>234.11</data_three>
</period>
<period date="2015-12-31T00:00:00+08:00">
<data_one>19.33</data_one>
<data_two>123.44</data_two>
<data_three>999.12</data_three>
</period>
<period date="2016-01-31T00:00:00+08:00">
<data_one>69.33</data_one>
<data_two>80.12</data_two>
<data_three>65.87</data_three>
</period>
</periods>
</root>