I want to loop through an xml file using simplephp.
My code for accessing is something like this:
// get the path of the file
$file = "xml/".$item_name . "_cfg.xml";
if( ! $xml = simplexml_load_file($file) ){
echo 'unable to load XML file';
} else {
$item_array = array();
foreach($xml->config->exported->item as $items)
{
$item_name = (string) $items->attributes()->name;
echo "item name: " . $item_name . "<br />";
}
That will echo out the name of all the item names in this xml, this isnt the actual xml as some of the data is sensitive but its basically the same with different data.
So it will show as the following based on the xml below:
yellow
blue
orange
red
black
here is the xml
<?xml version="1.0"?>
<main>
<config>
<exported>
<items>
<item name="yellow"></item>
<item name="blue"></item>
<New_Line />
<item name="orange"></item>
<item name="red"></item>
<New_Line />
<item name="black"></item>
</items>
</exported>
</config>
<main>
That is good but what i need to display is:
yellow
blue
--------
orange
red
--------
black
If you notice in the xml there is this line in between some of the stats
<New_Line />
And when i encounter that i want to echo a few dashes but i am not really sure how you do it as I'm not familiar with simplexml
Arguably this is a poor choice of structure in the XML, since what is presumably actually meant is that there are multiple sets of items, which should therefore have some parent to represent each individual group. Nonetheless, what you want to do is pretty easy using SimpleXML.
The trick is to use the ->children() method to iterate over all child nodes in order, regardless of their name. Then within that loop, you can examine the name of each node using ->getName() and decide how to act.
Here's an example (and a live demo of it in action); note that I've added the ->items to match the sample XML you gave, and used the shorter $node['name'] rather than $node->attributes()->name.
foreach($xml->config->exported->items->children() as $node)
{
switch ( $node->getName() )
{
case 'item':
$item_name = (string)$node['name'];
echo "item name: " . $item_name . "<br />";
break;
case 'New_Line':
echo '<hr />';
break;
}
}
Related
I am making a PHP project for a Pizza Shop [This is project-0 in David Malan's course CS-S75 Building Dynamic Websites]. And the Code that I have to write must be eXtensible. That is, if the pizza shop's owner wants to add a new category, he should be able to do that pretty easily and my PHP code must accommodate those changes in the XML file without writing any new code.
For my code to be extensible though, I need some methods for filtering the XML data.
For instance inside the root node <menu>, I have child nodes item that have attributes like
<item name="Pizzas">
<category name="Onions">
</category>
</item>
<item name="Salads">
<category name = "Garden">
</category>
</item>
and there are ten item tags in total.
What I want to do is this: if the user wants to purchase the salads, I would want to filter the XML DOM tree the following way:
// $_POST['selected'] has a value of 'Salads' stored in it
$selected = $_POST['selected']
$dom = simple_xml_loadfile("menu.xml")
foreach ($dom -> xpath("menu/item[#name = $selected ]" as $item))
{
echo $item -> category['name'].'<br />';
}
And it should print Garden and any other item that is subsequently added to the Salads category.The problem occurs with the menu/item[#name = $selected ] because this is probably not a proper method for comparing the attribute (Note that attribute comparison like this in XML requires single equal sign and not double equal).And obviously menu/item[#name = $_POST['selected']] doesn't work either.
What works is #name = "Salads" and of course this kills the whole purpose of the extensiblity of XML and dynamism of PHP.
Please help!
Let's get all category nodes that belong to a parent node that has a name attribute of your choosing:
Also note that the function name is simplexml_load_file and not simple_xml_loadfile
foreach ($dom->xpath('item[#name="' . $selected . '"]/category') as $item)
{
echo $item->attributes()->{'name'}. PHP_EOL;
}
Also note the usage of single vs. double quotes to enclose the attribute value.
For reference, this is the xml structure I used for testing:
<menu>
<item name="Pizzas">
<category name="Onions"></category>
</item>
<item name="Salads">
<category name = "Garden"></category>
<category name = "Cesar"></category>
<category name = "Onion and Tomato"></category>
</item>
</menu>
I'm using XML to insert products into my db with PHP. I can access / read the xml feed with the following code:
$xml=simplexml_load_file('testfeed.xml') or die("Error: Cannot create object");
foreach($xml->children() as $product) {
$pname = $product->name;
$pdescr = $product->description;
echo $pname;
echo $pdescr;
}
Below is my example XML:
<product ID="9">
<name>Product X</name>
<properties>
<property name="categoryPath">
<value>path-to-category</value>
</property>
<property name="stock">
<value>1</value>
</property>
</properties>
</product>
It's easy to get the values for name, but how do I get the value of the categorypath, since this one is inside properties->property->value and declared in the <property name="categoryPath">?
Thanks!
The easiest way without looping too much through the structures will be using XPath:
$values = $xml->xpath('//property[#name="categoryPath"]/value');
Just loop through the returned array and cast each result to string when needed, and you're done.
$xml=simplexml_load_file('./testfeed.xml') or die("Error: Cannot create object");
foreach($xml->children() as $product) {
$pname = $product->name;
$pdescr = $product->properties->property["name"];
$pdvalue = (string)$product->properties->property->value;
echo "Prdo Name: $pname\n";
echo "Prod Desc: $pdescr\n";
echo "Prod Value: $pdvalue\n";
}
Output:
Prdo Name: Product X
Prod Desc: categoryPath
Prod Value: path-to-category
Update:
While Jossif's answer is very good at getting all the nodes in bulk I'm including an edit for updated xml structure without using xpath:
$property = $product->properties->property;
$propertyAttribute = $property->attributes()->{'name'};
$catPath = ($propertyAttribute=="categoryPath")? $property->value : "value for missing paths";
print $catPath . PHP_EOL;
Since you don't seem to have any other similar xml nodes, you don't need the attribute to separate them.
You should be able to safely use this
$catPath = $product->properties->property->value;
print $catPath;
to get the string from the value node.
In all the examples of simplexml I have seen the structure of the xml is like:
<examples>
<example>
</example>
<example>
</example>
<example>
</example>
</examples>
However I am dealing with xml in the form:
<examples>
<example>
</example>
<example>
</example>
<example>
</example>
</examples>
<app>
<appdata>
<error>
<Details>
<ErrorCode>101</ErrorCode>
<ErrorDescription>Invalid Username and Password</ErrorDescription>
<ErrorSeverity>3</ErrorSeverity>
<ErrorSource />
<ErrorDetails />
</Details>
</error>
<items>
<item>
</item>
<item>
</item>
</items>
</appdata>
</app>
I would like to skip the examples stuff, and go straight to the app tag and check if the error errorcode exists and if it doesn't, go to the items array and loop through it.
My current way of handling this is:
$items = new SimpleXMLElement($xml_response);
foreach($items as $item){
//in here I check the presence of the object properties
}
Is there a better way? The problem is the xml structure sometimes changes order so I want to be able to go straight to particular parts of the xml.
This kind of thing is very easy using XPath, and handily, SimpleXML has an xpath function built into it! XPaths allow you to select nodes in a graph based on their ancestors, descendants, attributes, values, and so on.
Here is an example of using SimpleXML's xpath function to extract data from your XML. Note that I added an extra parent element to the sample you posted so that the XML would validate.
$sxo = new SimpleXMLElement($xml);
# this selects all 'error' elements with parent 'appdata', which has parent 'app'
$error = $sxo->xpath('//app/appdata/error');
if ($error) {
# go through the error elements...
while(list( , $node) = each($error)) {
# get the error details
echo "Found an error!" . PHP_EOL;
echo $node->Details->ErrorCode
. ", severity " . $node->Details->ErrorSeverity
. ": " . $node->Details->ErrorDescription . PHP_EOL;
}
}
Output:
Found an error!
101, severity 3: Invalid Username and Password
Here's another example -- I edited the XML excerpt slightly to show the results better here:
// edited <items> section of the XML you posted:
<items>
<item>Item One
</item>
<item>Item Two
</item>
</items>
# this selects all 'item' elements under appdata/items:
$items = $sxo->xpath('//appdata/items/item');
foreach ($items as $i) {
echo "Found item; value: " . $i . PHP_EOL;
}
Output:
Found item; value: Item One
Found item; value: Item Two
There's more information in the SimpleXML XPath documentation, and try the zvon.org XPath tutorials -- they give a good grounding in XPath 1.0 syntax.
I currently have the following XML structure:
<root>
<maininfo>
<node>
<tournament_id>3100423</tournament_id>
<games>
<a_0>
<id>23523636</id>
<type>
<choice_4>
<choice_id>345</choice_id>
<choice_4>
<choice_9>
<choice_id>345</choice_id>
<choice_9>
... etc
</type>
</a_0>
<a_1></a_1>
<a_2></a_2>
...etc
</games>
</info>
</node>
</root>
I can easily get the id of the first node element "a_0" by just doing:
maininfo[0]->a_3130432[0]->games[0]->a_1[0]->id;
My issue is:
How do I automatically iterate (with a foreach) through all a_0, a_1, a_2 and get the values of each of these node elements and all of their children like "345" in <choice_id>345</choice_id>?
The ending numbers of a_0, a_1 + the children of choice_4, choice_9, are dynamically created and there are no logic in the _[number] counting up with +1 for each next element.
As it has been outlined previously on Stackoverflow (for example in Read XML dynamic PHP) and as well generally in the PHP manual (for example in Basic SimpleXML usage), you can iterate over all child elements by using foreach.
For example to go over all a_* elements, it's just
foreach ($xml->maininfo->node->games[0] as $name => $a) {
echo $name, "\n";
}
Output:
a_0
a_1
a_2
You then want to iterate over these their ->type children again. This is possible in pure PHP by putting one foreach into a another:
foreach ($xml->maininfo->node->games[0] as $name => $a) {
echo $name, "\n";
if (!$a->type[0]) {
continue;
}
foreach ($a->type[0] as $name => $choice) {
echo ' +- ', $name, "\n";
}
}
This now outputs:
a_0
+- choice_4
+- choice_9
a_1
a_2
This starts to get a bit complicated. As you can imagine since XML is famous for it's tree structures, you're not the first one running into this problem. Therefore a query-language to get elements from an XML document has been invented: Xpath.
With Xpath you can access XML data as if it was a file-system. As I know that each a_* element is a child of games and each choice_* element a child of type, it's pretty straight forward:
/*/maininfo/node/games/*/type/*
^ ^ ^
| | choice_*
root |
a_*
In PHP Simplexml this looks like:
$choices = $xml->xpath('/*/maininfo/node/games/*/type/*');
foreach ($choices as $choice) {
echo $choice->getName(), ': ', $choice->choice_id, "\n";
}
Output:
choice_4: 345
choice_9: 345
As this example shows, the data is now retrieved with a single foreach.
If you as well need access to the <a_*> elements, you need to have multiple foreach's or your own iteration but that is even a more advanced topic which I'd say would extend over the limits of your question.
I hope this is helpful so far. See as well SimpleXMLElement::children() which also gives all children (like ->games[0] in the first example). All example codes are as well available as a working, interactive online-demo.
If I understand it well, you can do something like:
for($i = 0; $i < $max; ++$i){
$a = $parentNode->{'a_'.$i};
}
You can do this very easily using SimpleXML :
<?php
$xmlStr = "<?xml version='1.0' standalone='yes'?>
<root>
<maininfo>
<node>
<tournament_id>3100423</tournament_id>
<games>
<a_0>
<id>23523636</id>
<type>
<choice_4>
<choice_id>345</choice_id>
</choice_4>
<choice_9>
<choice_id>345</choice_id>
</choice_9>
</type>
</a_0>
<a_1></a_1>
<a_2></a_2>
</games>
</node>
</maininfo>
</root>";
$xmlRoot = new SimpleXMLElement($xmlStr);
$i = 0;
foreach($xmlRoot->maininfo[0]->node[0]->games[0] as $a_x)
{
echo $i++ . " - " . htmlentities($a_x->asXML()) . "<br/>";
}
?>
I have modified some parts of your XML string to make it syntactically correct. You can view the results at http://phpfiddle.org/main/code/56q-san
I've successfully integrated the LinkedIn API with my website, but I'm struggling to extract information from the XML. At the moment I'm just trying to print it out so I can proceed to use the user's information once they have logged in and given permission.
Below is the format of the XML, and further down is the code I am using to extract the information. The "first name", "last name" and "headline" calls work perfectly, but where an element has sub-headings, nothing is printed out. I've tried using
echo 'Positions: ' . $xml->{'positions:(title)'};
but it doesn't work.
Here is the XML:
<person>
<id>
<first-name />
<last-name />
<headline>
<location>
<name>
<country>
<code>
</country>
</location>
<industry>
<summary/>
<positions total="">
<position>
<id>
<title>
<summary>
<start-date>
<year>
<month>
</start-date>
<is-current>
<company>
<name>
</company>
</position>
</person>
This is the code I've been using to try to extract the information. I know I have to include the sub-heading somehow but I just don't know how!
echo 'First Name: ' . $xml->{'first-name'};
echo '<br/>';
echo 'Last Name: ' . $xml->{'last-name'};
echo '<br/>';
echo 'Headline: ' . $xml->{'headline'};
echo '<br/>';
echo 'Positions: ' . $xml->{'positions'};
Any help would be greatly appreciated, thanks for reading!
Using SimpleXML, you'd access the LinkedIn XML data properties as follows:
Anything with a dash in the property gets {}, so first-name becomes:
$xml->{'first-name'}
Anything without a dash such as headline, is referenced like:
$xml->headline
Anything that is a collection, such as positions, is referenced like:
foreach($xml-positions as $position) {
echo $position->title;
echo $position->{'is-current'};
}
Your XML is not valid, its not well formed. Anyway here's a sample XML and how to use it.
$v = <<<ABC
<vitrine>
<canal>Hotwords</canal>
<product id="0">
<descricao>MP3 Apple iPod Class...</descricao>
<loja>ApetreXo.com</loja>
<preco>à vista R$765,22</preco>
<urlImagem>http://im</urlImagem>
<urlProduto>http://</urlProduto>
</product>
</vitrine>
ABC;
$xml = simplexml_load_string($v);
foreach ($xml->product as $c){
echo $c->loja; //echoing out value of 'loja'
}
Try to use PHP's XML Parser instead:
http://www.php.net/manual/en/function.xml-parse.php
Tried Paul's answer above for:
foreach($xml-positions as $position) {
echo $position->title;
echo $position->{'is-current'};
}
didn't work for me - so I used this - not as elegant but works
for($position_num = 0; $position_num < 10;$position_num++){
echo $xml->positions->position[$position_num]->company->name;
}
Your XML is not well-formed... there are several elements without close tags. So we have no way to know for sure the structure of your XML. (You can't do that in XML like you can in HTML.)
That being said, assuming that <person> is the context node, you can probably get the content of the <title> element using an XPath expression, as in
$xml->xpath('positions/position/title');
I'm assuming $xml is a SimpleXMLElement object.