SimpleXML XMLIterator fails on encountering empty nodes - php

I'm attempting to use SimpleXML to read an XML File into in array, however the moment the script encounters an empty node (such as <NodeName />) it throws an error saying the XML is 'invalid' and quits.
The XML in question is entirely valid - I would search/replace through the entire thing and remove the empty nodes, but it's far too large to do this efficiently.
The following is a facsimile of the code I am using, how can this be re-factored to ignore, or simply return a null value to the array when it encounters an empty element?
$iter = 0;
$xmlIterator = new SimpleXMLIterator($url, 0, true);
for( $xmlIterator->rewind(); $xmlIterator->valid(); $xmlIterator->next() ) {
$rows[$iter] = array();
foreach($xmlIterator->getChildren() as $column => $data) {
$rows[$iter][$column] = $data;
$data = null;
}
$iter++;
}

Related

Handling simplexml to array elegantly

I have an xml structure:
<node1><node2><child_1/><child_2/><child_3/></node2></node1>
And i would want to get an array like this:
['child_1', 'child_2', 'child_3']
But to make my method for creating this handle errors elegantly and return an empty array when nothing found i am having to do this:
public function testXmlParse()
{
$config = new SimpleXMLElement("<node1><node2><child_1/><child_2/><child_3/></node2></node1>");
$result = $config->xpath('/node1/node2');
if (! count($result)) {
return [];
}
$result = $result[0]->children();
}
But i have even more code to write to check for arrays and valid etc.
Is there an elegantly way to get the correct result and return 0 on nothing finding?
The code you have written won't return an array anyway - the result of ->children() is an iterable SimpleXMLElement object. However, you can take advantage of the fact that a zero-element object is still iterable with foreach, and will simply go round zero times.
Since you are always looking for the first match, your example can also use SimpleXML access instead of XPath, to avoid the extra logic there.
$config = new SimpleXMLElement("<node1><node2><child_1/><child_2/><child_3/></node2></node1>");
// Start with an empty array; if no children are found, it will stay empty
$results = [];
// Note: $config represents the <node1> element, not the document
foreach ( $config->node2->children() as $name => $element ) {
$results[] = $name;
}
If <node2> is not always present, you may need to add an extra if ( isset($config->node2) ) around the loop, to avoid PHP throwing you warnings.

Check if XML element is existing in loop

For a website i'm making i need to get data from an external XML file.
I load the data like this:
$doc = new DOMDocument();
$url = 'http://myurl/results/xml/12345';
if (!$doc->load($url))
{
echo json_encode(array('error'=> 'error'));
exit;
}
$xpath = new DOMXPath($doc);
$program_date = $xpath->query('//game/date');
Then i use a foreach loop to get all the data
if($program_date){
foreach($program_date as $node){
$programArray['program_date'][] = $node->nodeValue;
}
}
The problem i'm having is that sometimes a certain game doesn't have a date.
So when a game doesn't have a date, i just want it to put "-", instead of the date from the XML file. My problem is that i don't know how to check if a date is present in the data.
I used a lot of ways like isset, !isset, else, !empty, empty
$teamArray['program_kind'][] = "-";
but noting works...
Can someone help me with this problem?
Thanks in advance
You need to iterate the game elements, use them as a context and fetch the data with additional XPath expressions.
But one thing first. Use DOMXPath::evaluate(). DOMXPath::query() only supports location paths. It can only return a node list. But XPath expressions can return scalar values, too.
$xpath = new DOMXPath($doc);
$games = $xpath->evaluate('//game');
The result of //game will always be a DOMNodeList object. It can be an empty list, but you can directly iterate it. A condition like if ($games) will always be true.
foreach ($games as $game) {
Now that you have the game element node, you can use it as an context to fetch other data.
$date = $xpath->evaluate('string(date)', $game);
string() casts the first node of the location path into a string. If it can not match a node, it will return an empty string. Check normalize-space() if you want to remove whitespaces at the same time.
You can validate if the game element has a date node using count().
$hasDate = $xpath->evaluate('count(date) > 0', $game);
The result of this XPath expression is always a boolean.

php simple xml, loop hrough object, put string in array

I am getting an XML error for some reason, even though this(beginner) code does what i want it to do.
It fetches strings into an array.
Line 11 results in "Notice: Trying to get property of non-object in D:\pam\w\www\mp\p\lasxml.php on line 11"
<?php
$xml = new DOMDocument( "1.0", "ISO-8859-1" );
$xml = simplexml_load_file('http://www.myepisodes.com/rss.php?feed=mylist&showignored=0&sort=asc&uid=Demerzel&pwdmd5=c6ed54b98a82b1----ac58147cedbde5');
$allaFullStringfranxml = array();
$i = 0;
do{
$allaFullStringfranxml[] = $xml->channel->item[$i]->title;
++$i;
}while(($xml->channel->item[$i]->title) != null);
I'm not sure why you are using do-while loop, I think in this case foreach will be better - do while loop is running at least once (even if there is no item). Also you are incrementing $i variable and then checking if title title of item with $i index is null - you shouldn't do that without checking if that item really exists. That should work better for you:
foreach ($xml->channel->item as $item) {
$allaFullStringfranxml[] = (string)$item->title;
}
You can also notice here that I'm doing (string)$item->title - that will convert title node to string, in other case you'll store node object.

Array to XML: Howto import HTML as object to DOMDocument? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to insert HTML to PHP DOMNode?
PHP and DOMDocument
I am trying to write a Class that converts an array to XML using DOMDocument - and on top of that imports HTML into the DOM document. Problem is that the HTML is not imported into the DOM document - it gets imported as a text string (for instance, HTML tags are shown as <p> instead of <p> in the source for the resulting XML document).
Update:
Code added directly to this Question as requested by Hakre. The code is a bit hacked but works - it would be interesting though to get rid of the extend from DomDocument as suggested by Hakre.
class xmlizer extends DomDocument {
function __construct() {
parent::__construct();
}
function node_create($arr, $items = null) {
if (is_null($items))
$items = $this->appendChild($this->createElement("items"));
// Loop the array values.
foreach($arr as $element => $value) {
// If Array has numeric keys, use "node - else use $element.
$element = is_numeric( $element ) ? "node" : $element;
// Create element, add $value unless $value is an array - and append to main object ($items).
$fragment = $this->createElement($element, (is_array($value) ? null : $value));
$items->appendChild($fragment);
// Iterate if $value is an array, .
if (is_array($value)) {
self::node_create($value, $fragment);
}
}
}
public function __toString() {
// html_entity_decode() added by Micha. Thanks.
return html_entity_decode($this->saveXML());
}
}
// Build test Array with HTML string (for testing purposes only).
for($i=0;$i<3;$i++) {
$j = $i+1;
$array['example'][] = array(
"id" => $j,
"title" => "Title $j",
"description" => "<p>Text <strong>string</strong> nr. $j with <em>some</em> <code>HTML code</code>.</p>",
);
}
// Test: Run the code.
header("Content-Type:text/xml");
$xml = new xmlizer();
$xml->node_create($array);
echo $xml;
PS: Please don't close the Question as I don't think this is a duplicate. Thanks.
Try html_entity_decode($value) on line 15 in the second code but why you want the HTML as HTML because then it would be interpreted as XML.
Update
Sorry the one above doesn't work and this doesn't work too:
$this
->createElement($element)
->createTextNode(is_array($value) ? null : $value ));
Finaly I tryed it my self:
I think this is the best solution: http://codepad.org/PpyewkVd

Comparing 2 XML Files using PHP

I want to compare 2 big xml files and retrieve the differences. Like ExamXML and DiffDog do. The solution I found was cycling through all child nodes of each file simultaneously and check if they are equal. But I have no idea how to achieve that... How can I loop through all child nodes and their properties? How can I check if the first element of the first file is equal to the first element of the second file, the second element of the first file is equal to the second element of the second file and so on?
Do yo have a better idea to compare 2 xml files?
I was looking for something to compare two XML like you, and I found this solution that works very well.
http://www.jevon.org/wiki/Comparing_Two_SimpleXML_Documents
I hope that helps to someone.
Have you looked at using XPath at all? Seems like an easy way to grab all of the child nodes. Then you'd be able to loop through the nodes and compare the attributes/textContent.
This might be a very alternative solution for you but this is how I would do it.
First, I'd try to get the format into something much more manageable like an array so I would convert the XML to an array.
http://www.bytemycode.com/snippets/snippet/445/
This is some simple code to do just that.
Then PHP has an array_diff() function that can show you the differences.
http://www.php.net/manual/en/function.array-diff.php
This may or may not work for you considering what you need to do with the differences but if you're looking to just identify and act upon them this might be a very quick solution to your problem.
Try the xmldiff extension
http://pecl.php.net/xmldiff
It's based on the same library as the perl module DifferenceMarkup, you'll get a diff XML document and can even merge then.
//Child by Child XML files comparison in PHP
//Returns an array of non matched children in variable &$reasons
$reasons = array();
$xml1 = new SimpleXMLElement(file_get_contents($xmlFile1));
$xml2 = new SimpleXMLElement(file_get_contents($xmlFile2));
$result = XMLFileComparison($xml1, $xml2, $reasons);
/**
* XMLFileComparison
* Discription :- This function compares XML files. Returns array
* of nodes do not match in pass by reference parameter
* #param $xml1 Object Node Object
* #param $xml2 Object Node Object
* #param &$reasons Array pass by reference
* returns array of nodes do not match
* #param $strict_comparison Bool default False
* #return bool <b>TRUE</b> on success or array of strings on failure.
*/
function XMLFileComparison(SimpleXMLElement $xml1, SimpleXMLElement $xml2, &$reasons, $strict_comparison = false)
{
static $str;
// compare text content
if ($strict_comparison) {
if ("$xml1" != "$xml2") return "Values are not equal (strict)";
} else {
if (trim("$xml1") != trim("$xml2"))
{
return " Values are not equal";
}
}
// get all children
$XML1ChildArray = array();
$XML2ChildArray = array();
foreach ($xml1->children() as $b) {
if (!isset($XML1ChildArray[$b->getName()]))
$XML1ChildArray[$b->getName()] = array();
$XML1ChildArray[$b->getName()][] = $b;
}
foreach ($xml2->children() as $b) {
if (!isset($XML2ChildArray[$b->getName()]))
$XML2ChildArray[$b->getName()] = array();
$XML2ChildArray[$b->getName()][] = $b;
}
//print_r($XML1ChildArray);
//print_r($XML2ChildArray);
// cycle over children
if (count($XML1ChildArray) != count($XML2ChildArray)) return "mismatched children count";// Second File has less or more children names (we don't have to search through Second File's children too)
foreach ($XML1ChildArray as $child_name => $children) {
if (!isset($XML2ChildArray[$child_name])) return "Second file does not have child $child_name"; // Second file has none of this child
if (count($XML1ChildArray[$child_name]) != count($XML2ChildArray[$child_name])) return "mismatched $child_name children count"; // Second file has less or more children
print_r($child_name);
foreach ($children as $child) {
// do any of search2 children match?
$found_match = false;
//$reasons = array();
foreach ($XML2ChildArray[$child_name] as $id => $second_child) {
$str = $str.$child_name.($id+1)."/"; // Adding 1 to $id to match with XML data nodes numbers
//print_r($child, $second_child);
// recursive function call until reach to the end of node
if (($r = XMLFileComparison($child, $second_child, $reasons, $strict_comparison)) === true) {
// found a match: delete second
$found_match = true;
unset($XML2ChildArray[$child_name][$id]);
$str = str_replace($child_name.($id+1)."/", "", $str);
break;
}
else {
unset($XML2ChildArray[$child_name][$id]);
$reasons[$str] = $r;
$str = str_replace($child_name.($id+1)."/", "", $str);
break;
}
}
}
}
return True;
}

Categories