Getting information from more than one large XML file - php

I am trying to get information from external large xml files; from file 1 (vehicleList.xml) and file 2 (CheckVehicles.xml) into a PHP file. All values in XML file 2 are in XML file 1. I would like to display only values in file 1 that are in XML file 2.
My foreach loop code can bring results for up to 130 items (that is if I reduce the items in XML file 2 to 130 items/nodes). However if I remove the if statement, I am able to get all the 3340 items/vehicles from XML file 1.
Where am I going wrong? I tried arrays but failed.
Here is my code:
//XML FILE 1 with 1300 items
$myXML = new SimpleXMLElement('CheckVehicles.xml', NULL, TRUE);//
foreach($myXML->root->item as $item){
$listArrayNew[(int)$item->value] = (int)$item->value;
}
//XML FILE 2 with 3340 vehicles
$parser = new SimpleXMLElement('vehicleList.xml', NULL, TRUE);
foreach ($parser->GetVehiclesListResponse->GetVehiclesListResult->Vehicle as $Vehicle) {
if($listArrayNew[(int)$Vehicle->ID] == (int)$Vehicle->ID){
$vehicle = $Vehicle->Description;
$regNumber = $Vehicle->RegistrationNumber;
$siteID = $Vehicle->SiteID;
$row .= "<tr>
<td>".$vehicle."</td>
<td>".$regNumber."</td>
<td>".$siteId."</td>
</tr>";
}
}
Here are the XML files:
XML file 1: vehicleList.xml
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope>
<GetVehiclesListResponse>
<GetVehiclesListResult>
<Vehicle>
<ID>153</ID>
<SiteID>11</SiteID>
<GroupID>3</GroupID>
<Description>A.O Basid KAR 459 E</Description>
<RegistrationNumber>KAR 459 E</RegistrationNumber>
</Vehicle>
..............................
<Vehicle>
<ID>3340</ID>
<SiteID>25</SiteID>
<GroupID>4</GroupID>
<Description>UAR 712B White Nissan Tiida (Deus Mubangizi)</Description>
<RegistrationNumber>UAR 712B</RegistrationNumber>
</Vehicle>
</GetVehiclesListResult>
</GetVehiclesListResponse>
</soap:Envelope>
XML file 2: CheckVehicles.xml
<?xml version="1.0" encoding="utf-8"?>
<Result>
<root>
<item>
<index>0</index>
<value>153</value>
</item>
...................
<item>
<index>1300</index>
<value>128</value>
</item>
</root>
</Result>

I don't know where you go wrong in your case. However if you want to select elements from the second file based on a criteria (e.g. an ID / unique Number) from the first file I suggest you make use of xpath in your case:
Obtain the numbers from the first file that are the criteria (e.g. /*/root/item/value)
Select all elements from the second file that match the criteria (e.g. ID in /*/GetVehiclesListResponse/GetVehiclesListResult/Vehicle).
The later point can best be achieved by using the technique outlined in Is there anything for XPATH like SQL “IN”? which is creating a comma separated list of the numbers to select and then compare this against each elements number.
Example:
Consider there 2 500 out of 10 000 elements in a first file and in a second file there are 10 000 elements. Each element can be uniquely identified by it's ID.
The first file has this layout:
<?xml version="1.0"?>
<root>
<item>
<index>0</index>
<id>604</id>
</item>
<item>
<index>1</index>
<id>2753</id>
</item>
...
</root>
And the second file has this layout.
<?xml version="1.0"?>
<list>
<item>
<id>1</id>
<some>Number: 33</some>
</item>
<item>
<id>2</id>
<some>Number: 35</some>
</item>
...
</list>
The xpath query to get all IDs from the first file therefore is:
//item/id
And the query for the second file can be expressed with SimpleXML in PHP as:
$ids = implode(',', $file1->xpath('//item/id'));
$query = '//item[contains(",' . $ids . ',", concat(",", id, ","))]';
You can find example code of that example here: http://eval.in/6370

Related

Remove white spaces between tag values in xml with php

I been searching information how to remove white spaces between tag values leaved by a PHP code when I export it to XML, I will explain detailed, first I load and XML then I do a search on the file with xPath, then I remove some elements that do not match some brands and finally I reexport it as a new XML, the problem is that this new XML is full of white spaces leaved by the code. I tried trim it but it doesn't seems to work correctly.
Here is my code:
<?php
$sXML = simplexml_load_file('file.xml'); //First load the XML
$brands = $sXML->xPath('//brand'); //I do a search for the <brand> tag
function filter(string $input) { //Then I give it a list of variables
switch ($input) {
case 'BRAND 3':
case 'BRAND 4':
return false;
default:
return true;
}
}
array_walk($brands, function($brand) { //I remove all elements do not match my list
$content = (string) $brand;
if (filter($content)) {
$item = $brand->xPath('..')[0];
unset($item[0]);
}
});
$sXML->asXML('filtred.xml'); // And finally export a new xml
?>
This one is the original XML:
<?xml version="1.0" encoding="utf-8"?>
<products>
<item>
<reference>00001</reference>
<other_string>PRODUCT 1</other_string>
<brand>BRAND 1</brand>
</item>
<item>
<reference>00002</reference>
<other_string>PRODUCT 2</other_string>
<brand>BRAND 2</brand>
</item>
<item>
<reference>00003</reference>
<other_string>PRODUCT 3</other_string>
<brand>BRAND 3</brand>
</item>
<item>
<reference>00004</reference>
<other_string>PRODUCT 4</other_string>
<brand>BRAND 4</brand>
</item>
<item>
<reference>00005</reference>
<other_string>PRODUCT 5</other_string>
<brand>BRAND 5</brand>
</item>
</products>
And the output of the script sends this:
<?xml version="1.0" encoding="utf-8"?>
<products>
<item>
<reference>00001</reference>
<other_string>PRODUCT 1</other_string>
<brand>BRAND 1</brand>
</item>
<item>
<reference>00002</reference>
<other_string>PRODUCT 2</other_string>
<brand>BRAND 2</brand>
</item>
<item>
<reference>00005</reference>
<other_string>PRODUCT 5</other_string>
<brand>BRAND 5</brand>
</item>
</products>
As you can see on the output, there is a white space between product 2 and product 5 and that I need to remove it. Any help will be appreciate.
You can force SimpleXML to trim all whitespace when it reads the file, by passing the LIBXML_NOBLANKS option to simplexml_load_file:
$sXML = simplexml_load_file('file.xml', null, LIBXML_NOBLANKS);
Then when you call ->asXML(), all the whitespace will be removed, and you'll get XML all on one line, like this:
<?xml version="1.0" encoding="utf-8"?>
<products><item><reference>00003</reference><other_string>PRODUCT 3</other_string><brand>BRAND 3</brand></item><item><reference>00004</reference><other_string>PRODUCT 4</other_string><brand>BRAND 4</brand></item></products>
To re-generate whitespace based on the remaining structure, you'll need to use DOM rather than SimpleXML - but that's easy to do without changing any of your existing code, because dom_import_simplexml simply "rewraps" the XML without reparsing it.
Then you can use the DOMDocument formatOutput property and save() method to "pretty-print" the document:
$sXML = simplexml_load_file('file.xml', null, LIBXML_NOBLANKS);
// ...
// process $sXML as before
// ...
$domDocument = dom_import_simplexml($sXML)->ownerDocument;
$domDocument->formatOutput = true;
echo $domDocument->save('filtered.xml');
Another possibility is to use preg_replace:
// Get simpleXml as string
$xmlAsString = $yourSimpleXmlObject->asXML();
// Remove newlines
$xmlAsString = preg_replace("/\n/", "", $xmlAsString);
// Remove spaces between tags
$xmlAsString = preg_replace("/>\s*</", "><", $xmlAsString);
var_dump($xmlAsString);
Now you get your XML as string in one line (including the XML declaration).

xpath finds nothing - PHP

Introduction
I have some codes in SQL. I go code by code in while loop in php and search these codes in XML feed.
Programm code
I have the following programm code
$x_search = $xml->xpath("//Item[#Sort='$sort']");
if(!$x_search){
$x_Id = $x_search[0]->attributes()->Id;
echo $sort." - ".$x_Id."<BR />";
}
Problem
It is possible, that some code is not in SQL. So I get this error message:
Undefined offset: 0 in
How to do something like if you find it in XML, $x_Id = $x_search[0]->attributes()->Id;?
I have tried already:
$x_search = $xml->xpath("//*[#Sort='$sort']");
if(!empty($x_search)){
if(isset($x_search)){
Example XML:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Item Id="12860" IdP="-2147483648" Sort="0001KC" Name="Computers">
<StoItem />
</Item>
</Root>
Examples for $sort:
00004M
12860
12859
12859
12861
12861
12862
12862
12863
12863
12864
Thank you
SimpleXMLElement::xpath() always returns an array of SimpleXMLElement objects. The array is empty, if nothing is matched. The result can equal false, if the expression is invalid (programming error). So if (!empty($x_search)) ... or if ($x_search) ... can be used as condition to check the result. false is an empty value and an empty array equals false. Both conditions will only be true if the result from the expression is an array with at least a single element.
$xmlString = <<<'XML'
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Item Id="12860" IdP="-2147483648" Sort="0001KC" Name="Computers">
<StoItem />
</Item>
</Root>
XML;
$xml = new SimpleXmlElement($xmlString);
$sort = '0001KC';
$x_search = $xml->xpath("//Item[#Sort='$sort']");
if (!empty($x_search)) {
$x_Id = $x_search[0]->attributes()->Id;
echo $sort." - ".$x_Id."<BR />";
}
Output: https://eval.in/406640
0001KC - 12860<BR />
Most of your example values for $sort look like Id attribute values. The second one, is the Id attribute value in the example XML.
If you want to match the Id attribute, the Xpath expression would be:
//Item[#Id='$sort']
It is even possible to match both attributes:
//Item[#Id='$sort' or #Sort='$sort']
This is an example of the XML. A I wrote it is possible, that some code is not in SQL. So that's why I need to solve this problem. Something like write me an echo only if you find this sort in XML.
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Item Id="12860" IdP="-2147483648" Sort="0001KC" Name="Computers">
<StoItem />
</Item>
</Root>
The begin of the list of codes that enter invariable $sort
00004M
12860
12859
12859
12861
12861
12862
12862
12863
12863
12864

PHP - Two same nodes in xml

I have the following code:
<?php
$str = '<?xml version="1.0" encoding="utf-8"?>
<ROOT>
<ITEM>
<TITLE>Title1</TITLE>
<CATEGORY>Books</CATEGORY>
<CATEGORY>Books | Novel</CATEGORY>
</ITEM>
<ITEM>
<TITLE>Title2</TITLE>
<CATEGORY>Books</CATEGORY>
<CATEGORY>Books | Sci-fi</CATEGORY>
</ITEM>
</ROOT>';
$xml = simplexml_load_string($str);
$s_xml = $xml->xpath("/ROOT/ITEM");
foreach($s_xml as $s_cat){
$cat_group = htmlspecialchars($s_cat->CATEGORY);
echo $cat_group."<BR />";
}
?>
I can't edit the XML so I need to solve the folowing problem. How to say to PHP that I need to show the second node called CATEGORY and not the first one. In my example I have the output
Books
Books
And I need:
Books | Novel
Books | Sci-fi
Thank you!
This is what you are looking for (note the [1]):
$cat_group = htmlspecialchars($s_cat->CATEGORY[1]);
It takes the second item in the array of category elements
You can always look at your elements like this, to figure out how the structure looks:
print_r($s_cat->CATEGORY);

Adding Nodes to Existing XML

The problem i was having is the Root XML was being produced every time it writes to the XML.
The Main issue was setting up Child and Defining the Root. From the help of Łza
I now understand the Root XML Node is ignored.
So then you setup and create a Child and then add your content, And example of the correct format is.
$xml = simplexml_load_file('FILENAME.xml'); // Load XML File Need to add IF Statment to create if does not exist
$result = $xml->addchild('Result'); // Ignore Root NODE and Add Child Results
$result->addChild('Time', gmdate('D-M-Y -H:i:s')); // Rest of the below adds Child to Result and outputs results
$result->addChild('Channel', $Site);
$result->addChild('Type', '**');
$result->addChild('Process', $Status);
$result->addChild('SKU', $code->SKU);
$result->addChild('item', $item);
$result->addChild('Status', '$Feedback');
$result->addChild('ErrorID', '$Error');
$result->addChild('Message', '$Message');
$xml->asXml('FILENAME.xml'); //Write to file would be
// All of the above Code is using variables from another part of the script
The output would be
<Root>
<Result>
<Time>Fri-May-2013 -09:15:22</Time>
<Channel>20</Channel>
<Type>**</Type>
<Process>Update</Process>
<SKU>98746524765</SKU>
<Item/>
<Status>Problem</Status>
<ErrorID>999-Error</ErrorID>
<Message>Unknown file format support</Message>
</Result>
<Result>
<Time>Fri-May-2013 -09:15:22</Time>
<Channel>20</Channel>
<Type>**</Type>
<Process>Update</Process>
<SKU>5412254785</SKU>
<Item/>
<Status>Problem</Status>
<ErrorID>123-Error</ErrorID>
<Message>Invalid Item</Message>
</Result>
</Root>
Thanks
Try to use SimpleXMLElement library instead hardcoded xml creation. This is maybe more complicate to use at the begining, but much more safe (I mean avoid possible errors in xml structure when you hardcode the xml) and easy to use when you just get start to use it.
And easy to add/remove nodes, childnodes.
This is an example for your code:
$xml = new SimpleXMLElement('<xml/>');
$data = $xml->addChild('data');
$result = $data->addChild('Result');
$result->addChild('Time', gmdate('D-M-Y -H:i:s'));
$result->addChild('Channel', $SiteID);
// ... and the same way create all your xml nodes.
// if you want add next <result> node witch all elements repeat the code, (or put it in loop if you want more <result> elements):
$result = $data->addChild('Result');
$result->addChild('Time', gmdate('D-M-Y -H:i:s'));
$result->addChild('Channel', $SiteID);
// and after create all nodes save the file:
$xml->asXml('DHError.xml');
above code will create xml:
<xml>
<data>
<Result>
<Time>Fri-May-2013 -12:14:39</Time>
<Channel>data</Channel>
</Result>
<Result>
<Time>Fri-May-2013 -12:14:39</Time>
<Channel>data</Channel>
</Result>
</data>
</xml>
Thats it. Then if you need to load and process the xml it would be easy:
To load the File simply use:
$xml2 = simplexml_load_file('DHError.xml');
// to add new node <Result>:
$resultNext = $xml2->data->addchild('Result');
$resultNext->addChild('Time', gmdate('D-M-Y -H:i:s'));
$resultNext->addChild('Channel', $SiteID);
//and save file
$xml2->asXml('DHError.xml');
this create a xml:
<?xml version="1.0" ?>
<xml>
<data>
<Result>
<Time>Fri-May-2013 -12:27:24</Time>
<Channel>data</Channel>
</Result>
<Result>
<Time>Fri-May-2013 -12:27:24</Time>
<Channel>data</Channel>
</Result>
<Result>
<Time>Fri-May-2013 -12:27:24</Time>
<Channel>data</Channel>
</Result>
</data>
</xml>

SimpleXML add element before

I'm using SimpleXML to generate xml based invoice.
The structure has to be like this (heavily simplified):
<invoice>
<total>
<price>100</price>
</total>
<items>
<item>...</item>
</items>
</invoice>
But if first loop my items and add totals together, and then insert <total>:
<invoice>
<items>...</items>
<total>...</total>
</invoice>
But CUSTOM XSD says it invalid. This probably will not cause an error in applications, but I'd like it to be valid.
So can I insert <total> tag before <items> tag?
Note: <items> tag is not the first element in <invoice>.
Jquery equivalent of the function in need is .insertBefore()
Cheers!
You can do something like this:
$domelement = dom_import_simplexml($items);
$new = $dom->insertBefore(
$dom->ownerDocument->createElement("total"),
$dom->firstChild
);
$newsxml = simplexml_import_dom($new);
then add the items into total node.

Categories