Forum Moderators: coopster

Message Too Old, No Replies

PHP XML Parsing?

Help Plz

         

sess4561

2:34 pm on Feb 25, 2010 (gmt 0)

10+ Year Member



Hi guy.

Always come on here with my PHP problems as you guys are by far the best! I hope someone can help me out on this one.

I have a client who is receiving information via an xml source. What they want to do is display this information on their site, in a design of their choice, so that as new items are added to the xml, their site is updated.

The problem I am having is with the xml file.

BELOW IS A SAMPLE OF THE XML:-

<?xml version="1.0"?>
<FeedData>
<ReadyMadeDeals>
<RMD ref="11085">
<FullAddress>10 Test Street, NE3 33S</FullAddress>
<PropertyInfo>A 2 Bed property in excellent condition with a large rear garden and parking for 2 cars. UPVC windows throughout.</PropertyInfo>
</RMD>
</ReadyMadeDeals>
</FeedData>

BELOW IS MY PHP:

<?php


$xml_file = "xmlfile.xml";


$xml_headline_key = "*FEEDDATA*READYMADEDEALS*RMD";
$xml_description_key = "*FEEDDATA*READYMADEDEALS*PROPERTYINFO";

$story_array = array();

$counter = 0;
class xml_story{
var $fulladdress, $propertyinfo;
}

function startTag($parser, $data){
global $current_tag;
$current_tag .= "*$data";
}

function endTag($parser, $data){
global $current_tag;
$tag_key = strrpos($current_tag, '*');
$current_tag = substr($current_tag, 0, $tag_key);
}

function contents($parser, $data){
global $current_tag, $xml_headline_key, $xml_description_key, $counter, $story_array;
switch($current_tag){
case $xml_headline_key:
$story_array[$counter] = new xml_story();
$story_array[$counter]->fulladdress = $data;
break;
case $xml_description_key:
$story_array[$counter]->propertyinfo = $data;
$counter++;
break;
}
}

$xml_parser = xml_parser_create();

xml_set_element_handler($xml_parser, "startTag", "endTag");

xml_set_character_data_handler($xml_parser, "contents");

$fp = fopen($xml_file, "r") or die("Could not open file");

$data = fread($fp, filesize($xml_file)) or die("Could not read file");

if(!(xml_parse($xml_parser, $data, feof($fp)))){
die("Error on line " . xml_get_current_line_number($xml_parser));
}

xml_parser_free($xml_parser);

fclose($fp);

?>

<html>
<head>
<title>CNT HEADLINE NEWS</title>
</head>
<body bgcolor="#FFFFFF">
<?

for($x=0;$x<count($story_array);$x++){
echo "\t<h2>" . $story_array[$x]->fulladdress . "</h2>\n";
echo "\t\t<br />\n";
echo "\t<i>" . $story_array[$x]->propertyinfo . "</i>\n";
}
?>

// END


This all works great when I don't include (RMD) in the script, but for some reason when I add this, no information shows.

Is this because they are using "ref="11085"> ?

Once again, all help is extremely appreciated!

Cheers

astupidname

12:10 pm on Feb 26, 2010 (gmt 0)

10+ Year Member



Is there any particular reason you don't use DOMDocument instead of xml_parser? I usually use DOMDocument, it just feels to me a lot less clunky than xml_parser, and DOMDocument is much more readable as it treats the xml as what it is - a DOM Document (duh! :) ) with nodes and childNodes and so on... So I guess if it were me, I would just do it like so:

<html>
<head>
<title>PHP'S DOMDocument, So Easy A Caveman Can Do It!</title>
</head>
<body>
<?php

$xml_file = "xmlfile.xml";

$xmlDoc = new DOMDocument();
$xmlDoc->preserveWhiteSpace = false; //ignore useless childNodes which are just blank space
$xmlDoc->load($xml_file);
$readyMadeDeal_nodes = $xmlDoc->getElementsByTagName('ReadyMadeDeals'); //all the nodes by name of 'ReadyMadeDeals'

//each $item in the loop will be one of the 'ReadyMadeDeal' nodes:
foreach ($readyMadeDeal_nodes as $item) {
foreach ($item->childNodes as $RMDnode) { //each $RMDnode will be one of the 'RMD' nodes
//get the 'ref' attribute of the RMD node, if desired
echo '<small>property listing id #'.$RMDnode->getAttribute('ref').'</small><br>';
//get the 'FullAddress' and 'PropertyInfo' nodes from the 'RMD' nodes' childNode's:
foreach ($RMDnode->childNodes as $RMDchild) {
switch($RMDchild->nodeName) {
case 'FullAddress':
echo '<h2 style="margin:0;">'.$RMDchild->nodeValue.'</h2>';
break;
case 'PropertyInfo':
echo '<p style="margin-top:0;"><i>'.$RMDchild->nodeValue.'</i></p>';
break;
}
}
}
}

?>
</body>
</html>


Hope that helps!

sess4561

2:41 pm on Mar 3, 2010 (gmt 0)

10+ Year Member



Thanks mate, that was a big help!

How would I from there go about storing that information into my MySql Database?

I want to simply update/insert the information directly from the xml file?

INSERT INTO properties (ref, FullAddress, PropertyInfo) VALUES (
(387629,'$RMD','This event cannot be adequately described');

Hope someone can help on that one!

Thanks in advance.

sess4561

2:41 pm on Mar 3, 2010 (gmt 0)

10+ Year Member



Thanks mate, that was a big help!

How would I from there go about storing that information into my MySql Database?

I want to simply update/insert the information directly from the xml file?

INSERT INTO properties (ref, FullAddress, PropertyInfo) VALUES (
(387629,'$RMD','This event cannot be adequately described');

Hope someone can help on that one!

Thanks in advance.

astupidname

5:01 pm on Mar 3, 2010 (gmt 0)

10+ Year Member



Thanks

Yeah, you are welcome, glad you liked.
How would I from there go about storing that information into my MySql Database?

Based on my earlier example, and assuming you already have a database setup? And also assuming you wish to use this as an 'administration' page of sorts to do the inserting (you won't want to re-insert every time the page is viewed or in production, rather meant to be a 'run once' admin page?), I guess it would go something like this:

<?php
//make your connection to mysql
$sql = mysql_connect("localhost", "username", "password") || die('Unable To Connect! '.mysql_error());
//set your database name
mysql_select_db("database_name_here", $sql);

$xml_file = "xmlfile.xml";

$xmlDoc = new DOMDocument();
$xmlDoc->preserveWhiteSpace = false; //ignore useless childNodes which are just blank space
$xmlDoc->load($xml_file);
$readyMadeDeal_nodes = $xmlDoc->getElementsByTagName('ReadyMadeDeals'); //all the nodes by name of 'ReadyMadeDeals'

//each $item in the loop will be one of the 'ReadyMadeDeal' nodes:
foreach ($readyMadeDeal_nodes as $item) {
foreach ($item->childNodes as $RMDnode) { //each $RMDnode will be one of the 'RMD' nodes
//get the 'ref' attribute of the RMD node, if desired
$ref = mysql_real_escape_string($RMDnode->getAttribute('ref'), $sql);
echo '<small>property listing id #'.$RMDnode->getAttribute('ref').'</small><br>';
//set a couple of 'clean' holders to re-use for insertion later:
$fullAddress = '';
$propertyInfo = '';
//get the 'FullAddress' and 'PropertyInfo' nodes from the 'RMD' nodes' childNode's:
foreach ($RMDnode->childNodes as $RMDchild) {
switch($RMDchild->nodeName) {
case 'FullAddress':
$fullAddress = mysql_real_escape_string($RMDchild->nodeValue, $sql);
echo '<h2 style="margin:0;">'.$RMDchild->nodeValue.'</h2>';
break;
case 'PropertyInfo':
$propertyInfo = mysql_real_escape_string($RMDchild->nodeValue, $sql);
echo '<p style="margin-top:0;"><i>'.$RMDchild->nodeValue.'</i></p>';
break;
}
}
$query = mysql_query("INSERT INTO properties (ref, FullAddress, PropertyInfo) VALUES ($ref, $fullAddress, $propertyInfo)") || die('Failed Query');
}
}

?>

Should be pretty close to what you need, if not, holler!

sess4561

8:20 pm on Mar 3, 2010 (gmt 0)

10+ Year Member



hi again,

now i'm getting the following errors returned.

Warning: mysql_select_db(): supplied argument is not a valid MySQL-Link resource in /home/sites/#*$!x/public_html/xml.php on line 9

Fatal error: Call to undefined method DOMText::getAttribute() in /home/sites/#*$!x/public_html/xml.php on line 22

any suggestions?

thanks

astupidname

12:52 am on Mar 4, 2010 (gmt 0)

10+ Year Member



Warning: mysql_select_db(): supplied argument is not a valid MySQL-Link resource in /home/sites/#*$!x/public_html/xml.php on line 9

Are you sure you set your username and password correctly when making the mysql_connect(), and have the name of your database (assuming you have already created a database?) set correctly in the call to mysql_select_db() ?
Fatal error: Call to undefined method DOMText::getAttribute() in /home/sites/#*$!x/public_html/xml.php on line 22

Not sure exactly why you got that, perhaps the xml is not exactly as you have shown? Or perhaps it's somewhat botched or something? At any rate, I added some filtering in below to check for nodeType which should help perhaps. If not, verify for me the format of your xml. The program is not currently set up to process TextNode's which may be in the $readyMadeDeal_nodes childNodes (as opposed to just element nodes as shown in your sample), which sounds like what may have happened there, but are not shown in your example, but it would just be a matter of some more "if'ing" it out. I have no clue what you would want to do with them anyhow.

I tested with a database and got it working on my end with the below, made a few other small changes (darn mysql syntax always trips me up, on the rare occassions I use it), note the addition of single quotes in the INSERT VALUES on the items which are strings:
<html>
<head>
<title>PHP'S DOMDocument, And Inserting Values To Database From XML.</title>
</head>
<body>
<?php
//>

$sql = mysql_connect("localhost", "usernameHere", "passwordHere"); //adjust username, password as needed...
if (!$sql) {
die('Unable To Connect! '.mysql_error());
}

mysql_select_db("RealEstateListings", $sql); //set to name of your database

$xml_file = "xmlfile.xml";

$xmlDoc = new DOMDocument();
$xmlDoc->preserveWhiteSpace = false; //ignore useless childNodes which are just blank space
$xmlDoc->load($xml_file);
$readyMadeDeal_nodes = $xmlDoc->getElementsByTagName('ReadyMadeDeals'); //all the nodes by name of 'ReadyMadeDeals'

//each $item in the loop will be one of the 'ReadyMadeDeal' nodes:
foreach ($readyMadeDeal_nodes as $item) {
foreach ($item->childNodes as $RMDnode) { //each $RMDnode will be one of the 'RMD' nodes
if ($RMDnode->nodeType === 1) {
//get the 'ref' attribute of the RMD node, if desired
$ref = $RMDnode->getAttribute('ref'); //not using mysql_real_escape_string here, assuming will be int, will check later
echo '<small>property listing id #'.$ref.'</small><br>';
//set a couple of 'clean' holders to re-use for insertion later:
$address = '';
$info = '';
//get the 'FullAddress' and 'PropertyInfo' nodes from the 'RMD' nodes' childNode's:
foreach ($RMDnode->childNodes as $RMDchild) {
if ($RMDchild->nodeType === 1) {
switch($RMDchild->nodeName) {
case 'FullAddress':
$addr = $RMDchild->nodeValue;
$address = mysql_real_escape_string($addr, $sql); //prevent mysql injection, database ready
echo '<h2 style="margin:0;">'.$addr.'</h2>';
break;
case 'PropertyInfo':
$inf = $RMDchild->nodeValue;
$info = mysql_real_escape_string($inf, $sql); //database ready
echo '<p style="margin-top:0;"><i>'.$inf.'</i></p>';
break;
}
}
}
if (is_numeric($ref) && strlen($address) && strlen($inf)) { //we have valid data to work with
$ref = (int)$ref; //cast as integer
//in setting up this sample, I'm assuming the use of a UNIQUE constraint
//on the ref column of the database table, to avoid erroneous duplicate entries.
//needs the quotes on $address & $info, was tripping me up otherwise.
$query = mysql_query("INSERT INTO properties (ref, FullAddress, PropertyInfo) VALUES ($ref,'$address','$info')", $sql);
if (!$query) {
$err = mysql_error();
if (preg_match("/Duplicate Entry/i", $err)) { //this will show if you try re-running with the same xml file:
echo '<p style="color:red;">'.$err.' , query was aborted</p>';
} else {
die('<p style="color:red;">Failed Query!<br>'.$err.'</p>');
}
}
} else {
echo '<p style="color:red;">not is_numeric and all that jazz, query not attempted</p>';
}
}//end if $RMDnode->nodeType
}
}

?>
</body>
</html>


Just FYI, in case it helps, from my localhost on WAMP I used the following to create the 'RealEstateListings' database with 'properties' table:

<?php
//>

$sql = mysql_connect("localhost", "root");
if (!$sql) {
die('Unable To Connect! '.mysql_error());
}
if (mysql_query("CREATE DATABASE RealEstateListings DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci",$sql)) {
echo '<p>Database created</p>';
mysql_select_db("RealEstateListings", $sql);
$query = "CREATE TABLE properties (id int NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), ref int NOT NULL, UNIQUE(ref), FullAddress varchar(255), PropertyInfo TEXT)";
$created = mysql_query($query,$sql);
if ($created) {
echo '<p>Successfully created database with table properties</p>';
} else {
die('Failed Query: '.mysql_error());
}
mysql_close($sql);
} else {
die('Error creating database: '.mysql_error());
}

?>


Of course it don't work to create databases that way on my remote server, am on shared server and have to do through Control Panel there. Just mentioning in case you need help setting up on your remote server.
Also, for posterity, the sample xml file I used to mimic yours:
<?xml version="1.0"?>
<FeedData>
<ReadyMadeDeals>
<RMD ref="11085">
<FullAddress>10 Test Street, NE3 33S</FullAddress>
<PropertyInfo>A 2 Bed property in excellent condition with a large rear garden and parking for 2 cars. UPVC windows throughout.</PropertyInfo>
</RMD>
</ReadyMadeDeals>
<ReadyMadeDeals>
<RMD ref="22099">
<FullAddress>22 Test2 Street, SW3 33S</FullAddress>
<PropertyInfo>A 4 Bed property in excellent condition with parking for 2.2 cars. Includes windows, walls, and a roof!</PropertyInfo>
</RMD>
</ReadyMadeDeals>
</FeedData>

sess4561

8:32 am on Mar 4, 2010 (gmt 0)

10+ Year Member



works absolutely great now. 100% what i was after. thanks ever so much. your a life saver!

sess4561

9:47 am on Mar 4, 2010 (gmt 0)

10+ Year Member



just a quick question again, sorry for being a pain!

I've got so far down the xml file, and its working great, but I have come to a part which has:-

<Financial>
<PriceToBuy>103950</PriceToBuy>
<Valuation>135000</Valuation>
<Equity>31050</Equity>
<ValuationRentPCM>478</ValuationRentPCM>
</Financial>

How do I reference something that is inside of <Financial>?

I so far have referenced everything with

case 'PropertyInfo':
$inf = $RMDchild->nodeValue;
$info = mysql_real_escape_string($inf, $sql); //database ready
echo '<p style="margin-top:0;"><i>'.$inf.'</i></p>';
break;


etc, etc, but if I try to reference 'Equity' for example(as shown below) it doesnt work:-

case 'Equity':
$eqt = $RMDchild->nodeValue;
$equity = mysql_real_escape_string($eqt, $sql); //database ready
echo '<p style="margin-top:0;"><i>'.$eqt.'</i></p>';
break;

Also later on in the xml there is a <photos> section. These only have values, and vary in amount of photos! CONFUSING, CONFUSING!

<Photos>
<Values id="main" image="http://#*$!.com/WebDocs1/Photos/FILE_EXTN111193-73804565.jpg" thumbnail="http://#*$!.com/WebDocs1/Photos/thb/FILE_EXTN111193-73804565.jpg" />
<Values image="http://#*$!.com/WebDocs1/Photos/FILE_KITCH11193-98107295.jpg" thumbnail="http://#*$!.com/WebDocs1/Photos/thb/FILE_KITCH11193-98107295.jpg" />
<Values image="http://#*$!.com/WebDocs1/Photos/FILE_BATHR11193-52689696.jpg" thumbnail="http://#*$!.com/WebDocs1/Photos/thb/FILE_BATHR11193-52689696.jpg" />
<Values image="http://#*$!.com/WebDocs1/Photos/FILE_OTHER11193-28990032.jpg" thumbnail="http://#*$!.com/WebDocs1/Photos/thb/FILE_OTHER11193-28990032.jpg" />
<Values image="http://#*$!.com/WebDocs1/Photos/FILE_OTHR211193-66753531.jpg" thumbnail="http://#*$!.com/WebDocs1/Photos/thb/FILE_OTHR211193-66753531.jpg" />
<Values image="http://#*$!.com/WebDocs1/Photos/FILE_OTHR311193-35691211.jpg" thumbnail="http://#*$!.com/WebDocs1/Photos/thb/FILE_OTHR311193-35691211.jpg" />
</Photos>

Sorry if im being such a NOOB!

astupidname

7:27 pm on Mar 4, 2010 (gmt 0)

10+ Year Member



You will want to loop through the <financial> element's childNodes, much like we are already doing, in order to get to the 'Equity' element node or the 'PriceToBuy' element node etc.. etc.. and grab their nodeValue's. As for the 'Photos', If they are just contained in the 'FeedData' block by themselves you could just grab them the way we are grabbing the 'ReadyMadeDeal' elements and loop through them. I guess I don't understand their correlation to anything else in the file? How do you know what image is for what? This is turning out to be a pretty complicated ("CONFUSING, CONFUSING!" -agreed :) xml file, it would seem.

sess4561

7:51 pm on Mar 4, 2010 (gmt 0)

10+ Year Member



if you give me your email address, i'll give you a link to the live feed if you like. might make things a bit clearer!

So for looping through the <financial> elements, i would do something like follows:-


case 'Equity':
$eqt = $Financechild->nodeValue;
$equity = mysql_real_escape_string($eqt, $sql); //database ready
echo '<p style="margin-top:0;"><i>'.$eqt.'</i></p>';
break;

But where would the loop be placed?

My email is <snip> if you want to send a blank email, i'll reply with the live feed.

Cheers

[edited by: jatar_k at 8:33 pm (utc) on Mar 4, 2010]
[edit reason] no email addresses thanks [/edit]