PHP - Two same nodes in xml - php

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);

Related

PHP - Access Item from XML with xpath()

I want to get the ISBN number of a book from an Amazon XML File. I already checked other posts and tried to solve the problem with them but without any success. The $xml looks like:
<itemlookupresponse>
<items>
<item>
<itemattributes>
<studio>Pottermore from J.K. Rowling</studio>
<eisbn>9781781100769</eisbn>
</itemattributes>
</item>
<item>
<itemattributes>
<studio>Carlsen Verlag GmbH</studio>
<isbn>3551551677</isbn>
</itemattributes>
</item>
<item>
<itemattributes>
<studio>Carlsen</studio>
<isbn>3551551677</isbn>
</itemattributes>
</item>
</items>
</itemlookupresponse>
I want to get the items, where the ISBN equals 3551551677. For that reason, I used the following command, which unfortunately returns an empty array.
$item = $xml->xpath('//items/item[itemattributes/isbn=3551551677]');
I would be glad, if someone could help me and explain, what I made wrong.
XML and XPath are case-sensitive. Amazon's XML is not all lowercase, despite what you've posted in your question. Adjust your XPath to match the exact case used in Amazon's actual XML.
Note also that if there are namespaces involved in the actual XML, those too must be accounted for in your XPath. See How does XPath deal with XML namespaces?
You didn't show something important (namespaces?) because this code works for me:
$string = <<<XML
<itemlookupresponse>
<items>
<item>
<itemattributes>
<studio>Pottermore from J.K. Rowling</studio>
<eisbn>9781781100769</eisbn>
</itemattributes>
</item>
<item>
<itemattributes>
<studio>Carlsen Verlag GmbH</studio>
<isbn>3551551677</isbn>
</itemattributes>
</item>
<item>
<itemattributes>
<studio>Carlsen</studio>
<isbn>3551551677</isbn>
</itemattributes>
</item>
</items>
</itemlookupresponse>
XML;
$xml = new SimpleXMLElement($string);
/* Поиск <a><b><c> */
$items = $xml->xpath('//items/item[ ./itemattributes/isbn[.="3551551677"] ]');
//$items = $xml->xpath('//items/item[itemattributes/isbn=3551551677]');
echo "Result size: " . count($items) . "\n";
foreach ( $items as $item) {
echo $item->asXML() . "\n";
}

PHP DOMDocument : How to parse custom XML/RSS tag names with COLONS?

I have the below RSS to parse, something like:
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:x-wr="http://www.w3.org/2002/12/cal/prod/Apple_Comp_628d9d8459c556fa#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:x-example="http://www.example.com/rss/x-example" xmlns:x-microsoft="http://schemas.microsoft.com/x-microsoft" xmlns:xCal="urn:ietf:params:xml:ns:xcal" version="2.0">
<channel>
<item>
<title>About Apples</title>
<author>David K. Lowie</title>
<description>Some description about apples</description>
<xCal:description>This is the full description about apples</xCal:description>
</item>
<item>
<title>About Oranges</title>
<author>Marry L. Jones</title>
<description>Some description about oranges</description>
<xCal:description>This is the full description about oranges</xCal:description>
</item>
</channel>
</rss>
In PHP, i parse it something like:
$rss = new DOMDocument();
$rss->load( "http://www.example.com/books.rss" );
foreach( $rss->getElementsByTagName("item") as $node ) {
echo $node->getElementsByTagName("title")->item(0)->nodeValue,
echo $node->getElementsByTagName("author")->item(0)->nodeValue,
echo $node->getElementsByTagName("description")->item(0)->nodeValue,
echo $node->getElementsByTagName("xCal:description")->item(0)->nodeValue,
}
I can read everything except the xCal:description node there. (The node names are exactly like that: description and the xCal:description.)
How to parse (read) the nodes like xCal:description
Is it because of the similar node names, like: description and the xCal:description ?
(I can't change the RSS source since it's not under my control.)
Please kindly help.
Use getElementsByTagNameNS():
$node->getElementsByTagNameNS("urn:ietf:params:xml:ns:xcal", "description")->item(0)->nodeValue
While using the namespace aware variants of the DOM methods is a correct answer, you might want to take a look at Xpath. It is a much more comfortable way to fetch data from a DOM.
For the Xpath expression, you can register own prefixes for the namespaces as needed.
$rss = new DOMDocument();
$rss->load("http://www.example.com/books.rss");
$xpath = new DOMXpath($rss);
$xpath->registerNamespace('xc', 'urn:ietf:params:xml:ns:xcal');
foreach($xpath->evaluate("//item") as $item) {
echo $xpath->evaluate('string(title)', $item), "\n";
echo $xpath->evaluate('string(author)', $item), "\n";
echo $xpath->evaluate('string(description)', $item), "\n";
echo $xpath->evaluate('string(xc:description)', $item), "\n";
}
Output:
About Apples
David K. Lowie
Some description about apples
This is the full description about apples
About Oranges
Marry L. Jones
Some description about oranges
This is the full description about oranges

Getting information from more than one large XML file

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

PHP : how to parse XML with nested xpath elements

Here is the XML that I am working on :
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:noo="http://www.myscheme.com/schema">
<channel>
<item>
<title>A Simple Title</title>
<noo:subcategory>the sub category</noo:subcategory>
<noo:relatedInfos>
<noo:teams>
<noo:team id="3">New York</noo:team>
<noo:team id="4">Las Vegas</noo:team>
</noo:teams>
</noo:relatedInfos>
</item>
</channel>
</rss>
I am doing this php code to get the two "team" but it does not work ($xml has the previous content) :
$xml_datas = simplexml_load_string($xml);
foreach($xml_datas->channel->item as $item){
$noo = $item->children('noo');
echo $noo->team;
}
Do you have any idea why it is not working ?
Thanks
See if this helps:
<?php // RAY_temp_userco.php
error_reporting(E_ALL);
$xml = <<<ENDXML
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:noo="http://www.myscheme.com/schema">
<channel>
<item>
<title>A Simple Title</title>
<noo:subcategory>the sub category</noo:subcategory>
<noo:relatedInfos>
<noo:teams>
<noo:team id="3">New York</noo:team>
<noo:team id="4">Las Vegas</noo:team>
</noo:teams>
</noo:relatedInfos>
</item>
</channel>
</rss>
ENDXML;
$obj = simplexml_load_string($xml);
$ns = $obj->getNamespaces(TRUE);
foreach($obj->channel->item as $item){
$noo = $item->children($ns['noo']);
var_dump($noo);
}
"noo" is just a local alias for that namespace, and the ->children() method (and most XML handling functions) want to know its actual global identifier, which is the URI in the xmlns attribute.
You need to either specify the full identifier of the namespace (i.e. ->children('http://www.myscheme.com/schema')) or set the optional second parameter to tell SimpleXML to look up the prefix (->children('noo', true). The second may be more readable, but it will break if a future document has the same schema, but gives the namespace a different local alias.
Additionally, the team nodes aren't directly under the item node, so you need to traverse further to get them:
// Give the namespace a readable name that won't change
define('NS_NOO', 'http://www.myscheme.com/schema');
$xml_datas = simplexml_load_string($xml);
foreach($xml_datas->channel->item as $item){
$teams = $item->children(NS_NOO)->relatedInfo->teams;
echo $teams->team[0];
}

PHP+XML changing node order

I've made a simple news script that saves articles to rss which then gets used on the Character Generator Newsticker on TV, the problem is that the CG plays the nodes starting from the top of the rss file.
now the xml looks like this:
<?xml version="1.0" ?>
<rss version="2.0">
<channel>
<title>News</title>
<link>website.com</link>
<description>News</description>
<language>ar-sa</language>
<item>
<title>Headline 1</title>
<description>Headline one the news this hour</description>
</item>
<item>
<title>Headline 2</title>
<description>Fire here flooding over there</description>
</item>
<item>
<title>Headline 3</title>
<description>Fire here flooding over there</description>
</item>
</channel>
</rss>
What i would like todo is have an option to move articles up and down the xml file, so instead of having "Headline 3" third in the list i would like to move it up to be first.
I know with C# you can do this using:
XElement node = ...get the element...
//Move up
if (node.PreviousNode != null) {
node.PreviousNode.AddBeforeSelf(node);
node.Remove();
}
//Move down
if (node.NextNode != null) {
node.NextNode.AddAfterSelf(node);
node.Remove();
Anyone have an idea how i can do this in PHP?
Thanks!
You can have a look at this answer XML reforming with DOM where they use the DOM-parser to rearrange the XML

Categories