I want to load XML file and then remove all <Charge> where <DispositionDate> is bigger/older then 7 years. Date format is YYYY-MM-DD.
XML example:
<BackgroundReports userId="" password="" account="" >
<BackgroundReportPackage>
<Screenings>
<Screening type="criminal" qualifier="">
<CriminalReport>
<CriminalCase>
<AgencyReference type="Docket">
<IdValue>CR-0870120-09</IdValue>
</AgencyReference>
<Charge>
<ChargeId>
<IdValue>1</IdValue>
</ChargeId>
<ChargeOrComplaint>DUI: HIGHEST RTE OF ALC (BAC .16+) 1ST OFF</ChargeOrComplaint>
<ChargeTypeClassification>unknown</ChargeTypeClassification>
<DispositionDate>2009-04-07</DispositionDate>
</Charge>
<Charge>
<ChargeId>
<IdValue>2</IdValue>
</ChargeId>
<ChargeOrComplaint>CARELESS DRIVING</ChargeOrComplaint>
<ChargeTypeClassification>unknown</ChargeTypeClassification>
<DispositionDate>2010-08-02</DispositionDate>
</Charge>
<Charge>
<ChargeId>
<IdValue>3</IdValue>
</ChargeId>
<ChargeOrComplaint>STATUTE: 475 PC</ChargeOrComplaint>
<ChargeTypeClassification>misdemeanor</ChargeTypeClassification>
<OffenseDate>1988-11-05</OffenseDate>
<Disposition>CONVICTED</Disposition>
<DispositionDate>1988-11-09</DispositionDate>
<DispositionDate>1988-11-05</DispositionDate>
<DispositionDate>1988-11-09</DispositionDate>
</Charge>
</CriminalCase>
</CriminalReport>
</Screening>
</Screenings>
</BackgroundReportPackage>
</BackgroundReports>
I know how to open and close/save file using PHP, I don't know how to delete the part i don't want... If anyone would help me with that I would be extremly thankfull!
You can either use SimpleXML, DOM or XSL for it.
Example XML (shortened for brevity (from Revision 1 of your question)):
$xml = <<< XML
<CriminalCase>
<Charge>
<DispositionDate>1995-12-21</DispositionDate>
</Charge>
<Charge>
<DispositionDate>2010-12-21</DispositionDate>
</Charge>
</CriminalCase>
XML;
With SimpleXml
$sevenYearsAgo = new DateTime('-7 years');
$CriminalCase = new SimpleXmlElement($xml);
for ($i = 0; $i < $CriminalCase->Charge->count(); $i++) {
$dispositionDate = new DateTime($CriminalCase->Charge->DispositionDate);
if ($dispositionDate < $sevenYearsAgo) {
unset($CriminalCase->Charge[$i]);
}
}
echo $CriminalCase->asXml();
With DOM
$dom = new DOMDocument;
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
$oldCases = $xpath->query(
sprintf(
'//Charge[substring-before(DispositionDate, "-") < %d]',
date('Y', strtotime('-7 years'))
)
);
foreach ($oldCases as $oldCase) {
$oldCase->parentNode->removeChild($oldCase);
}
echo $dom->saveXml();
With XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:date="http://exslt.org/dates-and-times"
extension-element-prefixes="date">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="/">
<CriminalCase>
<xsl:apply-templates />
</CriminalCase>
</xsl:template>
<xsl:template match="Charge">
<xsl:if test="date:year(DispositionDate) > date:year() - 7">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
and then use this PHP Code to transform it
$doc = new DOMDocument();
$xsl = new XSLTProcessor();
$doc->loadXml($xsl);
$xsl->importStyleSheet($doc);
$doc->loadXml($xml);
echo $xsl->transformToXML($doc);
Here are some tips on how to get started:
You need to parse the XML to something a little easier to work with. PHP has a library called SimpleXML.
Loop through the data and remove the objects which are older than 7 years. To compare dates you have to first convert the dates you got from the XML to something PHP can process as a date. Take a look at strtotime which gives you the timestamp (seconds since 1970, actually 1901 for version > 5.1.0) or DateTime which supports dates before 1970.
To check if the fetched date is older than 7 years ago, you need to (one way) subtract the timestamp with the current timestamp, and see if that value is greater than 7 years in seconds. Or if you use DateTime, you can take a look at DateTime::diff. Remove the objects that you iterate over that are older than 7 years (unset).
To save as XML again, take a look at SimpleXMLElement::asXML
Hope that helps!
Related
I am a beginner on PHP and XML.
I have a XML file as below (partly):
<combination>
<id_combination>2289</id_combination>
<quantity>4</quantity>
<unit_price_impact>0.000000</unit_price_impact>
<reference>K10100.1B</reference>
<group_name>Color</group_name>
<attribute_name>Blue</attribute_name>
</combination>
<combination>
<id_combination>2289</id_combination>
<quantity>4</quantity>
<unit_price_impact>0.000000</unit_price_impact>
<reference>K10100.1B</reference>
<group_name>Size</group_name>
<attribute_name>1</attribute_name>
</combination>
<combination>
<id_combination>2290</id_combination>
<quantity>20</quantity>
<unit_price_impact>0.000000</unit_price_impact>
<reference>K10100.2B</reference>
<group_name>Color</group_name>
<attribute_name>Blue</attribute_name>
</combination>
<combination>
<id_combination>2290</id_combination>
<quantity>20</quantity>
<unit_price_impact>0.000000</unit_price_impact>
<reference>K10100.2B</reference>
<group_name>Size</group_name>
<attribute_name>2</attribute_name>
</combination>
And i desire to get an array as described below:
$id_combination => 2289
$reference => K10100.1B
$combination_name => Color: Blue / Size: 1
$quantity => 4
$id_combination => 2290
$reference => K10100.2B
$combination_name => Color: Blue / Size: 2
$quantity => 20
I want to join data with the same 'id_combination' nodes and then process it in PHP.
I tried to use "foreach" loop, "array_unique", "implode" and etc.. but wasn't able to get any success.
I would appreciate anyone would help me straight to result with the suggested code.
i just did the following changes to your xml and have append new code and look and worked on my xampp server
$xml='<?xml version="1.0" encoding="UTF8"?>
<combinations>
<combination>
<id_combination>2289</id_combination>
<quantity>4</quantity>
<unit_price_impact>0.000000</unit_price_impact>
<reference>K10100.1B</reference>
<group_name>Color</group_name>
<attribute_name>Blue</attribute_name>
</combination>
<combination>
<id_combination>2289</id_combination>
<quantity>4</quantity>
<unit_price_impact>0.000000</unit_price_impact>
<reference>K10100.1B</reference>
<group_name>Size</group_name>
<attribute_name>1</attribute_name>
</combination>
<combination>
<id_combination>2290</id_combination>
<quantity>20</quantity>
<unit_price_impact>0.000000</unit_price_impact>
<reference>K10100.2B</reference>
<group_name>Color</group_name>
<attribute_name>Blue</attribute_name>
</combination>
<combination>
<id_combination>2290</id_combination>
<quantity>20</quantity>
<unit_price_impact>0.000000</unit_price_impact>
<reference>K10100.2B</reference>
<group_name>Size</group_name>
<attribute_name>2</attribute_name>
</combination>
</combinations>';
$xmlobject = simplexml_load_string($xml);
echo '<pre>';
// this print an array of objects
print_r($xmlobject);
// this print the associative array
print_r((array)$xmlobject);
Consider an XSLT solution as this is a typical need for the Muenchian Method in XSLT 1.0 used to group nodes by various keys. As information, XSLT is a special-purpose language designed to transform XML files and like most general purpose languages, PHP maintains an XSLT processor.
XSLT Script (save as .xsl to be loaded in script below)
<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="*"/>
<xsl:key name="idkey" match="combination" use="id_combination" />
<xsl:template match="combinations">
<xsl:copy>
<xsl:apply-templates select="combination[generate-id() =
generate-id(key('idkey',id_combination)[1])]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="combination[generate-id() =
generate-id(key('idkey',id_combination)[1])]">
<xsl:copy>
<xsl:copy-of select="id_combination|quantity|unit_price_impact|reference"/>
<combination_name>
<xsl:for-each select="key('idkey',id_combination)">
<xsl:value-of select="concat(group_name, ': ', attribute_name)" />
<xsl:if test="position() != last()">
<xsl:text> / </xsl:text>
</xsl:if>
</xsl:for-each>
</combination_name>
</xsl:copy>
</xsl:template>
</xsl:transform>
PHP Script
// LOAD XML AND XSL FILES
$xml = new DOMDocument('1.0', 'UTF-8');
$xml->load('Input.xml');
$xslfile = new DOMDocument('1.0', 'UTF-8');
$xslfile->load('XSLTScript.xsl');
// TRANSFORM XML with XSLT
$proc = new XSLTProcessor;
$proc->importStyleSheet($xslfile);
$newXml = $proc->transformToXML($xml);
// ECHO OUTPUT STRING
echo $newXml;
$xml = new SimpleXMLElement($newXml);
$xpath = $xml->xpath('//combination');
$array = [];
foreach($xpath as $result){
$inner = [];
foreach ($result as $node => $item) {
$inner[$node] = (string)$item;
}
$array[] = $inner;
}
var_dump($array);
Transformed XML
<?xml version="1.0" encoding="UTF-8"?>
<combinations>
<combination>
<id_combination>2289</id_combination>
<quantity>4</quantity>
<unit_price_impact>0.000000</unit_price_impact>
<reference>K10100.1B</reference>
<combination_name>Color: Blue / Size: 1</combination_name>
</combination>
<combination>
<id_combination>2290</id_combination>
<quantity>20</quantity>
<unit_price_impact>0.000000</unit_price_impact>
<reference>K10100.2B</reference>
<combination_name>Color: Blue / Size: 2</combination_name>
</combination>
</combinations>
Array Output
array(2) {
[0]=>
array(5) {
["id_combination"]=>
string(4) "2289"
["quantity"]=>
string(1) "4"
["unit_price_impact"]=>
string(8) "0.000000"
["reference"]=>
string(9) "K10100.1B"
["combination_name"]=>
string(21) "Color: Blue / Size: 1"
}
[1]=>
array(5) {
["id_combination"]=>
string(4) "2290"
["quantity"]=>
string(2) "20"
["unit_price_impact"]=>
string(8) "0.000000"
["reference"]=>
string(9) "K10100.2B"
["combination_name"]=>
string(21) "Color: Blue / Size: 2"
}
}
I have sudo string in a sort of xml format which i want to convert to xml in php. Which I then want to grab the imaware which contains date string in different formats and convert to appropriate date.
sudo string
<IMAware>
09-03-2016 05:28
</IMAware>
<NextUpdate>
</NextUpdate>
<ETR>
</ETR>
<SMS>
text
</SMS>
<Summary>
text
</Summary>
<Impact>
text
</Impact>
<Plan>
</Plan>
<Resolution>
text
</Resolution>
<Complete>
text
</Complete>
<Timeline>
text
</Timeline>
<Crisis>
</Crisis>
So far I have the following
for ($i = 0; $i < count($dbData); $i++) {
try {
print_r(trim($dbData[$i]['INCIDENT_NOTES']));
$xml = simplexml_load_string (trim($dbData[$i]['INCIDENT_NOTES']));
print_r($xml);
} catch (Exception $e) {
echo $e;
}
/*$items = $xml->xpath(item);
foreach($items as $item) {
echo $item['title'], ': ', $item['description'], "\n";
}*/
}
This fails with
Warning: simplexml_load_string(): Entity: line 5: parser error : Extra content at the end of the document in /var/SP/oiadm/docroot/dev/maskella/common/api/Api.class.php on line 1444
Do I need to enclose with<?xml version='1.0'?> <document></document>?
Date formats which I have are
21-02-2016 20:14
Date/Time: 09/02 - 15:40
Date: 08/02 - 11:50
Yes, the xml doc will need the header, and will also need a root element which encapsulates the elements from your sudo example.
See What is the correct format to use for Date/Time in an XML file for appropriate date formats in XML.
Does anyone knows of an alternative to convert a SimpleXmlElement into a string?
The standard string casting is very slow:
$myString = (string)$simpleXmlElement->someNode;
I needed to know which one is faster for finding an element with a specific text-value: XPath or walking the nodes... So I wrote a simple script which would measure the duration of 1000 iterations for both ways.
The first results were that XPath was much slower, but then I found out that I forgot the string cast in the node-walking part. When I fixed that, the node-walking was much much slower.
So, only the cast-to-string flipped the entire outcome.
Please review the following code to understand the issue at hand:
<pre>
<?php
//---------------------------------------------------------------------------
date_default_timezone_set('Europe/Amsterdam');
error_reporting(E_ALL);
//---------------------------------------------------------------------------
$data = <<<'EOD'
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<children>
<child><test>ads</test></child>
<child><test>sdf</test></child>
<child><test>dfg</test></child>
<child><test>fgh</test></child>
<child><test>ghj</test></child>
<child><test>hjk</test></child>
<child><test>jkl</test></child>
<child><test>ads</test></child>
<child><test>sdf</test></child>
<child><test>dfg</test></child>
<child><test>fgh</test></child>
<child><test>ghj</test></child>
<child><test>hjk</test></child>
<child><test>jkl</test></child>
<child><test>123</test></child>
<child><test>234</test></child>
<child><test>345</test></child>
<child><test>456</test></child>
<child><test>567</test></child>
<child><test>678</test></child>
<child><test>789</test></child>
<child><test>890</test></child>
<child><test>90-</test></child>
<child><test>0-=</test></child>
<child><test>qwe</test></child>
</children>
</root>
EOD;
$xml = new SimpleXMLElement($data);
$values = array('123', '234', '345', '456', '567', '678', '789', '890', '90-', '0-=', 'qwe');
$valCount = count($values);
$tries = 1000;
//---------------------------------------------------------------------------
echo("Running XPath... ");
$startTime = microtime(true);
for ($idx=0; $idx<$tries; $idx++)
$xml->xpath('/root/children/child[test="'.$values[($idx % $valCount)].'"]');
$duration = microtime(true) - $startTime;
echo("Finished in: $duration\r\n");
//---------------------------------------------------------------------------
echo("Running NodeWalk... ");
$startTime = microtime(true);
for ($idx=0; $idx<$tries; $idx++)
{
$nodes = $xml->children->child;
foreach ($nodes as $node)
if ((string)$node->test == $values[($idx % $valCount)])
break;
}
$duration = microtime(true) - $startTime;
echo("Finished in: $duration\r\n");
//---------------------------------------------------------------------------
?>
</pre>
When altering the line:
if ((string)$node->test == $values[($idx % $valCount)])
to:
if ($node->test == $values[($idx % $valCount)])
The code even looks at more nodes, but it's still a lot faster. So, it looks to me that the string cast here is very slow.
Does anyone have a faster alternative for the string cast?
Amazingly so, as #Gordon pointed out, there is actually not so much difference in performance... on Linux.
As the original tests were done only on Windows, I retested it on Linux, and what do you know... The difference between the XPath-way and the node-walking-way is gone.
For me that's enough, because I'm actually writing this for Linux (prod platform). But is still something to notice when building for Windows.
Whether the 25 digits are decimal and integers or just integers, DOMDocument::schemaValidate() fires a warning, return false, and libxml_get_errors(); captures the next errors:
PHP snippet:
$DD = new DOMDocument('1.0', 'ISO-8859-1');
$DD -> loadXML('<?xml version ="1.0" encoding="ISO-8859-1"?><a></a>');
libxml_use_internal_errors(true);
$old_libxml_disable_entity_loader = libxml_disable_entity_loader(false);
$DD -> schemaValidate(__DIR__ . '/schemas/schema.xsd'); // WARNING
libxml_disable_entity_loader($old_libxml_disable_entity_loader);
$errors = libxml_get_errors();
foreach ($errors as $error) { // PRINT ERRORS
echo $error -> code . '<br>';
echo $error -> message . '<br>';
}
DOMDocument::schemaValidate() Generated Errors:
Error 1824:
Element '{http://www.w3.org/2001/XMLSchema}maxInclusive':
'9999999999999999999999999' is not a valid value of the
atomic type 'xs:decimal'. in /path/schema.xsd on line X
Error 1717:
Element '{http://www.w3.org/2001/XMLSchema}maxInclusive': The value
'9999999999999999999999999' of the facet does not validate
against the base type '{http://www.w3.org/2001/XMLSchema}decimal'. in
/path/schema.xsd on line X
Valid schema (invalid XML only):
<?xml version="1.0" encoding="ISO-8859-1"?>
<xs:schema
targetNamespace="http://www.lala.com/la"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:la="http://www.lala.com/la"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:simpleType name="AmountType">
<xs:restriction base="xs:decimal">
<xs:totalDigits value="100"/>
<xs:fractionDigits value="20"/>
<xs:maxInclusive value="999999999999999999999999"/><!-- 24 DIGITS -->
</xs:restriction>
</xs:simpleType>
</xs:schema>
Invalid schema: WARNING + Libxml internal errors of invalid schema
<?xml version="1.0" encoding="ISO-8859-1"?>
<xs:schema
targetNamespace="http://www.lala.com/la"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:la="http://www.lala.com/la"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:simpleType name="AmountType">
<xs:restriction base="xs:decimal">
<xs:totalDigits value="100"/>
<xs:fractionDigits value="20"/>
<xs:maxInclusive value="9999999999999999999999999"/><!-- 25 DIGITS -->
</xs:restriction>
</xs:simpleType>
</xs:schema>
PHP version: 5.5.20
Libxml version: 2.9.2
According to W3C XML Schema Part 2: Datatypes Second Edition, libxml2 can limit the range of maxInclusive because it is allowed to limit the range of the value space of xs:decimal...
4.3.7 maxInclusive:
[Definition:] maxInclusive is the ·inclusive upper bound· of the
·value space· for a datatype with the ·ordered· property. The value of
maxInclusive ·must· be in the ·value space· of the ·base type·.
3.2.3 decimal
Note: All ·minimally conforming· processors ·must· support decimal
numbers with a minimum of 18 decimal digits (i.e., with a
·totalDigits· of 18). However, ·minimally conforming· processors ·may·
set an application-defined limit on the maximum number of decimal
digits they are prepared to support, in which case that
application-defined maximum number ·must· be clearly documented.
My xml tag format is
<transcript>
<messages>
<message>
<to>user1#localhost.local</to>
<from>user2#localhost.local</from>
<body>hello</body>
<date>2014-09-09 14:14:17.652 IST</date>
</message>
<message>
<to>user2#localhost.local</to>
<from>user1#localhost.local</from>
<body>hi dear</body>
<date>2014-09-10 14:14:17.652 IST</date>
</message>
</messages>
</transcript>
I want to load This xml file and display result in format like
2014-09-09 - Tuesday
(2:14 PM ) user1 : hello
2014-09-10 - Wednesday
(2:14 PM )user2 : hi dear
I have tried this PHP code
$xml_file_path='filename_with_path';
$XMLReader->open($xml_file_path);
while ($XMLReader->read() && $XMLReader->name !== "transcript");
while ($XMLReader->read() && $XMLReader->name !== "messages");
while ($XMLReader->read() && $XMLReader->name !== "message");
while ($XMLReader->name === "message") {
$node = new SimpleXMLElement($XMLReader->readOuterXML());
}
but $node give me empty result,How can I fix it or where I am wrong.
I personally hate xml, I know it's a mature standard, but I hate working with non-native data stores. Even databases irk me.
If I may refactor your code a bit...
//This turns your xml into a php object, which can be manipulated more directly
if (file_exists('text.xml')) {
$xml = simplexml_load_file('text.xml');
}
then you can output pretty simply
$totalMessages = count( $xml->messages->message );
for($i = 0; $i < $totalMessages; $i++){
echo "{$xml->messages->message[$i]->date}<br>
{$xml->messages->message[$i]->from}: {$xml->messages->message[$i]->body}<br><br>";
}
This outputs
2014-09-09 14:14:17.652 IST
user2#localhost.local: hello
2014-09-10 14:14:17.652 IST
user1#localhost.local: hi dear
I leave it up to you to slice and dice your strings for prettiness.