Using SimpleXML to loop through muliple entries - php

Hi Im trying to parse an xml feed using simplexml in php.
The xml feed is laid out as follows:
<Member>
<MemberType>Full</MemberType>
<JoinDate>2010-06-12</JoinDate>
<DataType>A</DataType>
<Data>
<FirstName>Ted</FirstName>
<LasttName>Smith</LasttName>
<Data1>56</Data1>
<Data2>100</Data2>
<Data3>120</Data3>
</Data>
</Member>
<Member>
<MemberType>Full</MemberType>
<JoinDate>2010-06-12</JoinDate>
<DataType>B</DataType>
<Data>
<FirstName>Ted</FirstName>
<LasttName>Smith</LasttName>
<Data1>57</Data1>
<Data2>110</Data2>
<Data3>130</Data3>
</Data>
</Member>
<Member>
<MemberType>Full</MemberType>
<JoinDate>2010-06-12</JoinDate>
<DataType>C</DataType>
<Data>
<FirstName>Ted</FirstName>
<LasttName>Smith</LasttName>
<Data4>58</Data4>
<Data5>115</Data5>
<Data6>230</Data6>
</Data>
</Member>
where the member element loops over and over again in the xml doc, but the data inside it changes. What im trying to do is enter all the data for certain members into an sql database. So ideally i want to enter this all in one line in the db. The xml feed contains different member types, like 'full' or 'associate'.
At the moment i am trying to loop through all the full members, and get all the data for this particular member. The data for each member is broken up into three parts each with a separate member tag, so above Ted Bloggs has data in three member tags, where Datatype is A, B and C
$PLF = simplexml_load_file('../XML/Members.xml');
foreach ($PLF->Root->xpath('//Member') as $member) {
if ($member->MemberType == 'Full') {
echo $member->MemberType.'<br/>';
echo $member->JoinDate.'<br />';
echo $member->DataType.'<br/>';
echo $member->Data->FirstName.'<br/>';
echo $member->Data->LastName.'<br/>';
echo $member->Data->Data1.'<br/>';
echo $member->Data->Data2.'<br/>';
echo '<br />';
}}
the code i have at the moment can only pull data from the first type (type A) in each loop, and i really want to combine all types A, B, and C into the same loop. So i can get all the data for each member like Ted Smith into one line in the DB.

I use simple xml to read some remote files and loop thru them as well. This is my code:
$sUrl = "some url";
$sContent = file_get_contents($sUrl);
$oXml = new SimpleXMLElement($sContent);
$aReturn = array();
foreach ($oXml->children() as $oStation)
{
$iRackId = (int)$oStation->rack_id;
$dLong = (double)str_replace(",", ".", $oStation->longitute);
$dLati = (double)str_replace(",", ".", $oStation->latitude);
$sDescription = (string)$oStation->description;
$aRes = array();
$aRes['rack_id'] = $iRackId;
$aRes['longitute'] = $dLong;
$aRes['latitude'] = $dLati;
$aRes['description'] = utf8_decode($sDescription);
if ($dLong > 0 && $dLati > 0)
$aReturn[$iRackId] = $aRes;
}
What I do is I put the result of the XML file into an array. Later in the code I save that data to the database as well.
Hope this helped you. I don't use xpath... had nothing but problems with it and didn't had the time to sort them out. This seems to work for me though.
Br,
Paul Peelen

Related

Retrieve XML element data with PHP

i'm trying to allow the user to retrieve a specific XML node and print the information. My XML data is as follows:
<people>
<person id="1">
<forename>Jim</forename>
<surname>Morrison</surname>
</person>
<person id="2">
<forename>James</forename>
<surname>Frank</surname>
</person>
</people>
I want to be able to search my XML document with a name or ID, for instance I want to be able to say, 'check that james exists, and if james does, print out his information, otherwise print out an error message'.
I've figured that I first need to include the file with:
$xml=simplexml_load_file("credentials.xml") or die("Error: Cannot create object");
From this point onwards i'm unsure how to proceed, i've looked at SimpleXML and XPATH but i'm unsure on how to use these to acheive this. Thanks if you can help
Let's xpath():
$xml = simplexml_load_string($x); // assume XML in $x
$result = $xml->xpath("/people/person[forename = 'James']")[0];
The above xpath-expression will select any <person> having 'James' as a <forename> and store it as an array in $result.
With the [0] at the end of line 2, we select only the first entry in that array and store it in $result. This requires PHP >= 5.4.
We could also write to get the same result:
$result = $xml->xpath("/people/person[forename = 'James']");
$result = $result[0];
If there were more than 1 James in the XML, we would only get the first.
To get all Jameses, do as in line 1 above.
Then, let's output the <person> selected by our xpath expression:
echo $result->forename . ' ' . $result->surname .' has id ' . $result['id'] . ".";
In case $result contains several Jameses, do:
foreach ($result as $person) {
echo $person->forename . ' ' . $person->surname .' has id ' . $person['id'] . "." . PHP_EOL;
}
see it working: https://eval.in/222195
You should use the manual.
simplexml_load_file returns a SimpleXMLElement object.
From here you can use the objects children method to get the children you want, which would also be more of that SimpleXMLElement objects. Let's say you first want the children called people, and then want person. When you got the person objects, you can get the value of the attribute by the method attributes to get the names and the values etc. Because SimpleXMLElement implements Traversable, you can use a foreach loop to loop through the lists/arrays you get in return.
Figured out how to mostly solve the problem with this:
$xml=simplexml_load_file("credentials.xml") or die("Error: Cannot create object");
foreach($xml->children() as $people){
if ($people->forename == "james" ){echo $people->forename;}
}
This may be inefficient so if anyone else has a better way please do specify :)

Two dimensional array

i've tried to find this out by myself before asking but cant really figure it out.
What I have is a loop, it's actually a loop which reads XML data with simplexml_load_file
Now this XML file has data which I want to read and put into an array.. a two dimensional array actually..
So the XML file has a child called Tag and has a child called Amount.
The amount is always differnt, but the Tag is usually the same, but can change sometimes too.
What I am trying to do now is:
Example:
This is the XML example:
<?xml version="1.0"?>
<Data>
<Items>
<Item Amount="9,21" Tag="tag1"/>
<Item Amount="4,21" Tag="tag1"/>
<Item Amount="6,21" Tag="tag2"/>
<Item Amount="1,21" Tag="tag1"/>
<Item Amount="6,21" Tag="tag2"/>
</Data>
</Items>
Now i have a loop which reads this, sees what tag it is and adds up the amounts.
It works with 2 loops and two different array, and I would like to have it all in one array in single loop.
I tried something like this:
$tags = array();
for($k = 0; $k < sizeof($tags); $k++)
{
if (strcmp($tags[$k], $child['Tag']) == 0)
{
$foundTAG = true;
break;
}
else
$foundTAG = false;
}
if (!$foundTAG)
{
$tags[] = $child['Tag'];
}
and then somewhere in the code i tried different variations of adding to the array ($counter is what counts the Amounts together):
$tags[$child['Tag']][$k] = $counter;
$tags[$child['Tag']][] = $counter;
$tags[][] = $counter;
i tried few other combinations which i already deleted since it didnt work..
Ok this might be a really noob question, but i started with PHP yesterday and have no idea how multidimensional arrays work :)
Thank you
this is how you can iterate over the returned object from simple xml:
$xml=simplexml_load_file("/home/chris/tmp/data.xml");
foreach($xml->Items->Item as $obj){
foreach($obj->Attributes() as $key=>$val){
// php will automatically cast each of these to a string for the echo
echo "$key = $val\n";
}
}
so, to build an array with totals for each tag:
$xml=simplexml_load_file("/home/chris/tmp/data.xml");
$tagarray=array();
// iterate over the xml object
foreach($xml->Items->Item as $obj){
// reset the attr vars.
$tag="";
$amount=0;
// iterate over the attributes setting
// the correct vars as you go
foreach($obj->Attributes() as $key=>$val){
if($key=="Tag"){
// if you don't cast this to a
// string php (helpfully) gives you
// a psuedo simplexml_element object
$tag=(string)$val[0];
}
if($key=="Amount"){
// same as for the string above
// but cast to a float
$amount=(float)$val[0];
}
// when we have both the tag and the amount
// we can store them in the array
if(strlen($tag) && $amount>0){
$tagarray[$tag]+=$amount;
}
}
}
print_r($tagarray);
print "\n";
This will break horribly should the schema change or you decide to wear blue socks (xml is extremely colour sensitive). As you can see dealing with the problem child that is xml is tedious - yet another design decision taken in a committee room :-)

Most efficient method of parsing non-standard XML document in PHP

In PHP, what is the quickest, most efficient way to parse an XML document formatted like so:
<data>
<rowdata>
<fieldname>products_id</fieldname>
<value><![CDATA[1]]></value>
<fieldname>products_image</fieldname>
<value><![CDATA[image_one.jpg]]></value>
<fieldname>products_name</fieldname>
<value>Product One</value>
</rowdata>
<rowdata>
<fieldname>products_id</fieldname>
<value><![CDATA[2]]></value>
<fieldname>products_image</fieldname>
<value><![CDATA[image_two.jpg]]></value>
<fieldname>products_name</fieldname>
<value>Product Two</value>
</rowdata>
</data>
This is the format I've been given by a till system company to import products in to a database. I have no idea why they decided to have <fieldname> and <value> tags instead of just <products_id>1</products_id>
At the moment the only way I can think of doing is writing up some crude loop which sets a boolean each time a <value> tag is found and resets
Seems pretty straightforward. There is some logic - for each <fieldname> there is a <value> - I assume that each fieldname correspond to a column in a table.
Using simplexml :
$xml='<data>
<rowdata>
<fieldname>products_id</fieldname>
<value><![CDATA[1]]></value>
<fieldname>products_image</fieldname>
<value><![CDATA[image_one.jpg]]></value>
<fieldname>products_name</fieldname>
<value>Product One</value>
</rowdata>
<rowdata>
<fieldname>products_id</fieldname>
<value><![CDATA[2]]></value>
<fieldname>products_image</fieldname>
<value><![CDATA[image_two.jpg]]></value>
<fieldname>products_name</fieldname>
<value>Product Two</value>
</rowdata>
</data>';
$data = simplexml_load_string($xml);
$baseSQL='INSERT into TABLE set ';
foreach($data as $rowdata) {
$SQL='';
$count=0;
foreach ($rowdata->fieldname as $fieldname) {
if ($SQL!='') $SQL.=',';
$SQL.=$fieldname.'="'.$rowdata->value[$count].'"';
$count++;
}
echo $baseSQL.$SQL.'<br>';
}
produces :
INSERT into TABLE set products_id="1",products_image="image_one.jpg",products_name="Product One"
INSERT into TABLE set products_id="2",products_image="image_two.jpg",products_name="Product Two"

Update or add node to simplexml if type query

OK sorry about this, but getting knickers in twist with what is really a very simple task. I have a simple xml structure:
<site>
<page>
<pagename>page1</pagename>
<title>title1</title>
<id>abc
<plot>
this is text
</plot>
</id>
</page>
<page>
<pagename>page2</pagename>
<title>titlee2</title>
<id>xyz
<plot>
this is text
</plot>
</id>
</page>
</site>
I pass 3 variables by ajax post from a form the variables are pagename, id and plot.
What I cannot seem to do is write the right query that checks the pagename node against the pagename variable, then checks for the id node in that page. If the id node exists then I update the plot node, if the id does not exist I create the id node and the plot node.
I can write/update the nodes, somebody did help find the right page but then I get lost. So in my example xml above if I pass the variables pagename=page1 and id=abc then I just update the plot, but if pagename is page1 and id=def (which does not exist) the I need to create the id and the plot. as said can do the addChild, update bit, just cannot get the query right to check that the id exists for that page - note $id = $site->xpath('//id[text()="'.$posted_variable.'"]'); doesn't work for me because the id might be present in more than one page node, but it cannot be duplicated in the same page node.
Many thanks for help
OK I have it working, may not be pretty but it does work -
$xml = simplexml_load_file('xml.xml');
$site = new SimpleXMLElement($xml->asXML());
$paget = "index";
$text = $_POST['tosave'];
$text = htmlentities($text, ENT_QUOTES, 'UTF-8');
// EDIT / UPDATE //
$pages = $site->xpath('//page/pagename[text()="'.$paget.'"]/..');
if($pages){
$a = array();
foreach( $pages[0]->id as $ID ){
array_push($a,$ID);
}
if(in_array($_POST['containerid'],$a)){
foreach ($a as $key => $value) {
if($value==$_POST['containerid']){
$pages[0]->id[$key]->plot = trim($text) ;
}
}
} else {
$pages[0]->addChild('id',$_POST['containerid'])->addChild('plot',trim($text));
}
}
$site->asXML("xml.xml");
exit;

PHP XML XPath - Stop duplicate nodes

Im trying to parse an XML document using the XPath element in simple XML. However this script below (When searching for the entry "U2" in the last.fm API) returns:
Passengers Passengers Bono Passengers Bono U2 and Green Day Passengers
Bono U2 and Green Day R.E.M. Passengers Bono U2 and Green Day R.E.M.
INXS
As you can see there are repeating nodes. Is there a way that I can stop duplicate/repeating nodes from being shown?
(PHP Code)
$xmlmusic = new SimpleXMLElement($result);
$releases = $xmlmusic->xpath('artist/similar/artist');
foreach ($releases as $artist) {
$artistResult .= $artist->name . PHP_EOL;
echo $artistResult;}
(XML Document)
<?xml version="1.0" encoding="utf-8"?>
<lfm status="ok">
<artist>
<name>U2</name>
<mbid>704acdbb-1415-4782-b0b6-0596b8c55e46</mbid>
<url>http://www.last.fm/music/U2</url>
<image size="small">http://userserve-ak.last.fm/serve/34/107345.jpg</image>
<streamable>1</streamable>
<stats>
<listeners>2613654</listeners>
<playcount>96947986</playcount>
</stats>
<similar>
<artist>
<name>Passengers</name>
<url>http://www.last.fm/music/Passengers</url>
<image size="small">http://userserve-ak.last.fm/serve/34/4826014.jpg</image>
</artist>
I'm assuming that your XML snippet is only a small sample of the full results, so...
you need to modify your foreach loop to check if the artist currently being processed has been seen before. The easiest way is to use an array:
$seen = array();
foreach ($releases as $artist) {
if (!isset($seen[$artist->name])) {
$seen[$artist->name] = true;
}
}
Once the loop's done, you've got a nice array with each artist being a key in $seen. To replicate your simple string concatenation, you simply implode/echo:
echo implode(array_keys(', ', $seen));

Categories