I'm trying to read an XML file with PHP but I'm only getting the first result and can't figure out why.
XML structure:
<main>
<data>
<record>
<column name="title">Some title here</column>
</record>
<record>
<column name="title">Some second title here</column>
</record>
</data>
</main>
Pretty basic. This is the code I'm using to getting the results:
foreach($XML->data->record as $product) {
$title = mysql_real_escape_string($product->title);
}
But this only gives an empty string so I guess I'm looking at the wrong path. The code below does work, but only gives me the first record and not the other records in the XML file.
foreach($XML->data as $product) {
$title = mysql_real_escape_string($product->record->title);
}
The answer is probably easy, but I can't figure it out. Hope someone is willing to help :)
title is the value of the name attribute of the <column> node.
You can't access it the way you do it.
To access the <column> node:
$xml = simplexml_load_string($x); // assume XML in $x
foreach ($xml->data->record as $product) {
echo $product->column;
}
Output:
Some title here
Some second title here
see it working: https://eval.in/506519
If there are many columns under each record...
<record>
<column name="title">Some title here</column>
<column name="car">BMW</column>
<column name="color">red</column>
</record>
... and you want to list all, you need a second loop:
foreach ($xml->data->record as $product) { // same as above
foreach ($product->column as $column) { // iterate all column
echo $column['name'] . ": " . $column . PHP_EOL;
}
}
Output:
title: Some title here
car: BMW
color: red
see it working: https://eval.in/506521
In case you want only <column> nodes with name = "title", check with if:
foreach ($xml->data->record as $product) { // same as above
foreach ($product->column as $column) { // iterate all column
if ($column['name'] == "title") echo $column . PHP_EOL;
}
}
see it working: https://eval.in/506523
And be aware that there is a different way to get all <column> with name = "title" with xpath:
$titles = $xml->xpath("//column[#name = 'title']");
foreach ($titles as $title)
echo $title . PHP_EOL;
see it in action: https://eval.in/506531
For an explanation of the xpath syntax, look at Using xPath to access values of simpleXML and zillion others.
and BTW: as said in comments, don't use mysql_...
Not tested but looks ok. Load the xml file / string into DOMDocument and get all the column nodes and iterate through them
$dom=new DOMDocument;
$dom->loadXML( $strxml );
/*
or
--
$dom->load( $xmlfile );
*/
$col=$dom->getElementsByTagName('column');
foreach( $col as $node ){
echo $node->tagName.' '.$node->getAttribute('title').' '.$node->nodeValue;
}
Related
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.
I have a bookCatalog.xml file as below
<bookCatalog>
<book id='1'>
<title>html</title>
</book>
<book id='2'>
<title>java</title>
</book>
<book id='3'>
<title>php</title>
</book>
</bookCatalog>
I want to programmatically get the title value of a book node by using variable $id of book node, and i used the following code:
$doc=new DOMDocument();
$doc->load('bookCatalog.xml');
$xpath= new DOMXPath($doc);
$findBookNode=$xpath->query("//book[#id='$id']")->item(0);
foreach ($findBookNode as $child) {
if ($child->nodeName === 'title') {
$bookTitle = $child->nodeValue;
}
}
But it turned out that the result is not what i want.
If I replace the variable $id to '1' , I can get the title value of the book node whose id=1;
$findBookNode=$xpath->query("//book[#id='1']")->item(0);
I just found the problem in my code:
The problem is that the variable $id is assigned by $_POST['id'] from a form in another code segment.
$id=$_POST['id'];
Then the value of variable $id has trailing space, for example
$id='1'
become
$id='1 ' // one space after number 1
I need some assistance with the following issue. I have an xml file with the following structure:
<attributes>
<record ProductId="103" Compatibility="iPhone"/>
<record ProductId="103" Color="Black"/>
<record ProductId="103" Material="Leather"/>
<record ProductId="103" Collection="iPhone"/>
</attributes>
The problem is that I want to generate only one record per ProductId because it makes it easier for me to handle the information. The record should look like:
<record ProductId="103" Compatibility="iPhone" Color="Black" Material="Leather" Collection="iPhone"/>
I believe that using xpath is the way to go, but I just can't get the synthax right. I'm using a php script to parse the xml file.
Any assistance will be greatly appreciated.
Alternative you could just create a new one, but of course, get the necessary attributes first, gather them then apply them to the new one. Like this sample:
$xml_string = '<attributes><record ProductId="103" Compatibility="iPhone"/><record ProductId="103" Color="Black"/><record ProductId="103" Material="Leather"/><record ProductId="103" Collection="iPhone"/></attributes>';
$xml = simplexml_load_string($xml_string);
$record = new SimpleXMLElement('<attributes><record /></attributes>'); // create empty
$data = array();
foreach($xml->record as $entry) {
$attributes = $entry->attributes(); // get attributes
foreach($attributes as $key => $attribute) {
$data[$key] = (string) $attribute; // put it inside a container temporarily
}
}
// add the gathered attributes
foreach($data as $key => $value) {
$record->record->addAttribute($key, $value);
}
echo $record->asXML();
// echo htmlentities($record->asXML());
// <?xml version="1.0"?> <attributes><record ProductId="103" Compatibility="iPhone" Color="Black" Material="Leather" Collection="iPhone"/></attributes>
I have an XML document with the following structure:
<posts>
<user id="1222334">
<post>
<message>hello</message>
<client>client</client>
<time>time</time>
</post>
<post>
<message>hello client how can I help?</message>
<client>operator</client>
<time>time</time>
</post>
</user>
<user id="2333343">
<post>
<message>good morning</message>
<client>client</client>
<time>time</time>
</post>
<post>
<message>good morning how can I help?</message>
<client>operator</client>
<time>time</time>
</post>
</user>
</posts>
I am able to create the parser and print out the whole document, the problem is however that I want to print only the (user) node and children with a specific attribute (id).
my PHP code is:
if( !empty($_GET['id']) ){
$id = $_GET['id'];
$parser=xml_parser_create();
function start($parser,$element_name,$element_attrs)
{
switch($element_name)
{
case "USER": echo "-- User --<br>";
break;
case "CLIENT": echo "Name: ";
break;
case "MESSAGE": echo "Message: ";
break;
case "TIME": echo "Time: ";
break;
case "POST": echo "--Post<br> ";
}
}
function stop($parser,$element_name){ echo "<br>"; }
function char($parser,$data){ echo $data; }
xml_set_element_handler($parser,"start","stop");
xml_set_character_data_handler($parser,"char");
$file = "test.xml";
$fp = fopen($file, "r");
while ($data=fread($fp, filesize($file)))
{
xml_parse($parser,$data,feof($fp)) or
die (sprintf("XML Error: %s at line %d",
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser)));
}
xml_parser_free($parser);
}
using this in the start() function can select the right node but it doesn't have any effect on the reading process:
if(($element_name == "USER") && $element_attrs["ID"] && ($element_attrs["ID"] == "$id"))
any help would be appreciated
UPDATE:
XMLReader works but when using if statement it stops working:
foreach ($filteredUsers as $user) {
echo "<table border='1'>";
foreach ($user->getChildElements('post') as $index => $post) {
if( $post->getChildElements('client') == "operator" ){
printf("<tr><td class='blue'>%s</td><td class='grey'>%s</td></tr>", $post->getChildElements('message'), $post->getChildElements('time'));
}else{
printf("<tr><td class='green'>%s</td><td class='grey'>%s</td></tr>", $post->getChildElements('message'), $post->getChildElements('time'));
}
}
echo "</table>";
}
As suggested in a comment earlier, you can alternatively use the XMLReaderDocs.
The XMLReader extension is an XML Pull parser. The reader acts as a cursor going forward on the document stream and stopping at each node on the way.
It is a class (with the same name: XMLReader) which can open a file. By default you use next() to move to the next node. You would then check if the current position is at an element and then if the element has the name you're looking for and then you could process it, for example by reading the outer XML of the element XMLReader::readOuterXml()Docs.
Compared with the callbacks in the Expat parser, this is a little burdensome. To gain more flexibility with XMLReader I normally create myself iterators that are able to work on the XMLReader object and provide the steps I need.
They allow to iterate over the concrete elements directly with foreach. Here is such an example:
require('xmlreader-iterators.php'); // https://gist.github.com/hakre/5147685
$xmlFile = '../data/posts.xml';
$ids = array(3, 8);
$reader = new XMLReader();
$reader->open($xmlFile);
/* #var $users XMLReaderNode[] - iterate over all <user> elements */
$users = new XMLElementIterator($reader, 'user');
/* #var $filteredUsers XMLReaderNode[] - iterate over elements with id="3" or id="8" */
$filteredUsers = new XMLAttributeFilter($users, 'id', $ids);
foreach ($filteredUsers as $user) {
printf("---------------\nUser with ID %d:\n", $user->getAttribute('id'));
echo $user->readOuterXml(), "\n";
}
I have create an XML file that contains some more posts like in your question, numbered in the id attribute from one and up:
$xmlFile = '../data/posts.xml';
Then I created an array with two ID values of the user interested in:
$ids = array(3, 8);
It will be used in the filter-condition later. Then the XMLReader is created and the XML file is opened by it:
$reader = new XMLReader();
$reader->open($xmlFile);
The next step creates an iterator over all <user> elements of that reader:
$users = new XMLElementIterator($reader, 'user');
Which are then filtered for the id attribute values stored into the array earlier:
$filteredUsers = new XMLAttributeFilter($users, 'id', $ids);
The rest is iterating with foreach now as all conditions have been formulated:
foreach ($filteredUsers as $user) {
printf("---------------\nUser with ID %d:\n", $user->getAttribute('id'));
echo $user->readOuterXml(), "\n";
}
which will return the XML of the users with the IDs 3 and 8:
---------------
User with ID 3:
<user id="3">
<post>
<message>message</message>
<client>client</client>
<time>time</time>
</post>
</user>
---------------
User with ID 8:
<user id="8">
<post>
<message>message 8.1</message>
<client>client</client>
<time>time</time>
</post>
<post>
<message>message 8.2</message>
<client>client</client>
<time>time</time>
</post>
<post>
<message>message 8.3</message>
<client>client</client>
<time>time</time>
</post>
</user>
The XMLReaderNode which is part of the XMLReader iterators does also provide a SimpleXMLElementDocs in case you want to easily read values inside of the <user> element.
The following example shows how to get the count of <post> elements inside the <user> element:
foreach ($filteredUsers as $user) {
printf("---------------\nUser with ID %d:\n", $user->getAttribute('id'));
echo $user->readOuterXml(), "\n";
echo "Number of posts: ", $user->asSimpleXML()->post->count(), "\n";
}
This would then display Number of posts: 1 for the user ID 3 and Number of posts: 3 for the user ID 8.
However, if that outer XML is large, you don't want to do that and you want to continue to iterate inside that element:
// rewind
$reader->open($xmlFile);
foreach ($filteredUsers as $user) {
printf("---------------\nUser with ID %d:\n", $user->getAttribute('id'));
foreach ($user->getChildElements('post') as $index => $post) {
printf(" * #%d: %s\n", ++$index, $post->getChildElements('message'));
}
echo "Number of posts: ", $index, "\n";
}
Which produces the following output:
---------------
User with ID 3:
* #1: message 3
Number of posts: 1
---------------
User with ID 8:
* #1: message 8.1
* #2: message 8.2
* #3: message 8.3
Number of posts: 3
This example shows: depending on how large the nested children are, you can traverse further with the iterators available via getChildElements() or you can use as well the common XML parser like SimpleXML or even DOMDocument on a subset of the XML.
You can use PHP SimpleDomHTML (A HTML DOM parser written in PHP5+ let you manipulate HTML in a very easy way!) You can query your data like the way you work with jQuery. It support HTML so for sure it well support for XML document.
You can download and view the document here: http://simplehtmldom.sourceforge.net/
I am have two xml files.. I first get one and loop through it then I need to take an id from the first xml file and find it in the second one and echo out the results associated with that id. If I were to do this with SQL I would simply do this:
$query = (SELECT * FROM HotelSummary WHERE roomTypeCode = '$id') or die();
while($row=mysql_fetch_array($query)){
$name = $row['Name'];
}
echo $name;
How can I do this is in xml and php??
I recommend you to read the DOMDocument documentation.
It's quite heavy but also powerful (not always clear what happens, but the Internet shold always give you a solution)
You can simply walk through your first document, finding your Id and then find your DOMElement via an XPath.
<?php
$dom = new DOMDocument();
$dom->load('1.xml');
foreach ($dom->getElementsByTagName('article') as $node) {
// your conditions to find out the id
$id = $node->getAttribute('id');
}
$dom = new DOMDocument();
$dom->load('2.xml');
$xpath = new DOMXPath($dom);
$element = $xpath->query("//*[#id='".$id."']")->item(0);
// would echo "top_2" based on my example files
echo $element->getAttribute('name');
Based on following test files:
1.xml
<?xml version="1.0" encoding="UTF-8"?>
<articles>
<article id="foo_1">
<title>abc</title>
</article>
<article id="foo_2">
<title>def</title>
</article>
</articles>
2.xml
<?xml version="1.0" encoding="UTF-8"?>
<tests>
<test id="foo_1" name="top_1">
</test>
<test id="foo_2" name="top_2">
</test>
</tests>
Use SimpleXML to create an object representation of the file. You can then loop through the elements of the Simple XML object.
Depending on the format of the XML file:
Assuming it is:
<xml>
<roomTypeCode>
<stuff>stuff</stuff>
<name>Skunkman</name>
</roomTypeCode>
<roomTypeCode>
<stuff>other stuff</stuff>
<name>Someone Else</name>
</roomTypeCode>
</xml>
It would be something like this:
$xml = simplexml_load_file('xmlfile.xml');
for($i = 0; $i < count($xml->roomTypeCode); $i++)
{
if($xml->roomTypeCode[$i]->stuff == "stuff")
{
$name = $xml->roomTypeCode[$i]->name;
}
}
That connects to the XML file, finds how many roomTypeCode entries there are, searches for the value of "stuff" within and when it matches it correctly, you can access anything having to do with that XML entry.