This question already has answers here:
Remove a child with a specific attribute, in SimpleXML for PHP
(18 answers)
xml remove child node in php
(3 answers)
Closed 7 years ago.
The goal is to delete every XML child with the same parent that matches the id.
The problem I've encountered was that only the first XML child was removed. When I ran this file the output was "The id is: 1" and also "CarlCarlok". The script removed the first XML child with the attribute "1". Which also was proven when I checked the XML file afterwards where the comment with id "9" was removed. So..To resolve this issue i have to know:
How do I remove every XML child with the same parent?
Because now it only removes the first one it encounters.
<?php
require '../functions.php';
$id = $_GET['id'];
echo "The id is: " . $id . "\n";
$comment_xml_path = '../data/comments.xml';
$comments_xml_file = simplexml_load_file($comment_xml_path);
foreach ($comments_xml_file->comment as $c) {
echo $c->author;
if (strcmp($id, $c['parent']) == 0) {
$dom = dom_import_simplexml($c);
$dom->parentNode->removeChild($dom);
echo "ok";
}
save_xml($comment_xml_path, $comments_xml_file);
(save_xml() is a function for saving utilizing asXML that we made during class)
The XML file
<?xml version="1.0" encoding="UTF-8"?>
<comments>
<comment id="6" parent="3" date="20150317224217">
<author>Carl</author>
<cmt>3</cmt>
</comment>
<comment id="9" parent="1" date="20150312225112">
<author>Carl</author>
<cmt>dsa</cmt>
</comment>
<comment id="10" parent="1" date="20150356225256">
<author>Carl</author>
<cmt>2</cmt>
</comment>
<comment id="11" parent="1" date="20150357225257">
<author>Carl</author>
<cmt>2</cmt>
</comment>
</comments>
Here's a way with SimpleXML and xpath(), which is like SQL for XML:
$id = 1; // parent
$xml = simplexml_load_file("some/path/to/an/xml/file.xml");
$comments = $xml->xpath("/comments/comment[#parent='$id']");
foreach ($comments as $comment) unset($comment[0]);
echo $xml->asXML(); // show result
Comments:
line 3: the xpath-statement will select all <comment> where attribute parent = $id. The # in the statement means attribute. The condition is wrapped in []. The result is stored in $comments as an array of SimpleXML elements.
line 4: deleting each <comment> in the array.
If you want to remove all the children, I would instead loop over the childNodes and call removeChild().
Updated Code:
if (strcmp($id, $c['parent'])== 0) {
foreach ($c->childNodes as $child) {
$child->parentNode->removeChild($child);
}
}
This assumes you are using DOMDocument. However, you seem to be mixing this and SimpleXML.
Related
I have different XML files where I renamed for each XML file all individual tags, so that every XML file has the same tag name. That was easy because the function was customized for the XML file.
But instand of writing 7 new functions for each XML file now I want to check if a XML file has a specidifed child or not. Because if I want to say:
foreach ($items as $item) {
$node = dom_import_simplexml($item);
$title = $node->getElementsByTagName('title')->item(0)->textContent;
$price = $node->getElementsByTagName('price')->item(0)->textContent;
$url = $node->getElementsByTagName('url')->item(0)->textContent;
$publisher = $node->getElementsByTagName('publisher')->item(0)->textContent;
$category = $node->getElementsByTagName('category')->item(0)->textContent;
$platform = $node->getElementsByTagName('platform')->item(0)->textContent;
}
I get sometimes: PHP Notice: Trying to get property of non-object in ...
For example. Two different XML sheets. One contains publisher, category and platform, the other not:
XML 1:
<products>
<product>
<desc>This is a Test</desc>
<price>11.69</price>
<price_base>12.99</price_base>
<publisher>Stackoverflow</publisher>
<category>PHP</category>
</packshot>
<title>Check if child exists? - SimpleXML (PHP)</title>
<url>http://stackoverflow.com/questions/ask</url>
</product>
</products>
XML 2:
<products>
<product>
<image></image>
<title>Questions</title>
<price>23,90</price>
<url>google.de/url>
<platform>Stackoverflow</platform>
</product>
</products>
You see, sometimes one XML file contains publisher, category and platform but sometimes not. But it could also be that not every node of a XML file contains all attributes like in the first!
So I need to check for every node of a XML file individual if the node is containing publisher, category or/and platform.
How can I do that with SimpleXML?
I thought about switch case but at first I need to check which childs are contained in every node.
EDIT:
Maybe I found a solution. Is that a solution or not?
if($node->getElementsByTagName('platform')->item(0)){
echo $node->getElementsByTagName('platform')->item(0)->textContent . "\n";
}
Greetings and Thank You!
One way to rome... (working example)
$xml = "<products>
<product>
<desc>This is a Test</desc>
<price>11.69</price>
<price_base>12.99</price_base>
<publisher>Stackoverflow</publisher>
<category>PHP</category>
<title>Check if child exists? - SimpleXML (PHP)</title>
<url>http://stackoverflow.com/questions/ask</url>
</product>
</products>";
$xml = simplexml_load_string($xml);
#set fields to look for
foreach(['desc','title','price','publisher','category','platform','image','whatever'] as $path){
#get the first node
$result = $xml->xpath("product/{$path}[1]");
#validate and set
$coll[$path] = $result?(string)$result[0]:null;
#if you need here a local variable do (2 x $)
${$path} = $coll[$path];
}
#here i do array_filter() to remove all NULL entries
print_r(array_filter($coll));
#if local variables needed do
extract($coll);#this creates $desc, $price
Note </packshot> is an invalid node, removed here.
xpath syntax https://www.w3schools.com/xmL/xpath_syntax.asp
Firstly, you're over-complicating your code by switching from SimpleXML to DOM with dom_import_simplexml. The things you're doing with DOM can be done in much shorter code with SimpleXML.
Instead of this:
$node = dom_import_simplexml($item);
$title = $node->getElementsByTagName('title')->item(0)->textContent;
you can just use:
$title = (string)$item->title[0];
or even just:
$title = (string)$item->title;
To understand why this works, take a look at the SimpleXML examples in the manual.
Armed with that knowledge, you'll be amazed at how simple it is to see if a child exists or not:
if ( isset($item->title) ) {
$title = (string)$item->title;
} else {
echo "There is no title!";
}
This question already has answers here:
How to convert XML attributes to text nodes
(3 answers)
Closed 7 years ago.
I want to write a PHP script that will modify my XML file.
I have my productId within the node as an attribute and I want to parse the entire file and convert it to a separate node. So I want to read the attribute of the node and put that attribute in its own node. But the rest of the nodes will stay as is.
Before:
<product id="123">
<name>bob</name>
<lastname>tim</lastname>
</product>
To:
<product>
<id>123</id>
<name>bob</name>
<lastname>tim</lastname>
</product>
Can I do this in PHP? Bearing in mind the file will have over one thousand separate products in it.
You could do it this way.
$xml = new SimpleXMLElement('<product id="123"></product>');
if(!empty($xml['id'])) {
$xml->addChild('id', $xml['id']);
unset($xml['id']);
}
echo $xml->asXML();
Output:
<?xml version="1.0"?>
<product><id>123</id></product>
Here's the manual's link and the addchild functions link. http://php.net/manual/en/class.simplexmlelement.php
http://php.net/manual/en/simplexmlelement.addchild.php
Update:
If you had multiple products you could loop like this.
$xml = new SimpleXMLElement('<proudcts><product id="123"></product><product id="234"></product></proudcts>');
foreach($xml as $key => $data){
if(!empty($data['id'])) {
$data->addChild('id', $data['id']);
unset($data['id']);
}
}
echo $xml->asXML();
Output:
<?xml version="1.0"?>
<proudcts><product><id>123</id></product><product><id>234</id></product></proudcts>
Need help with updating some simplexml code I did along time ago. The XML file I'm parsing from is formatted in a new way, but I can't figure out how to navigate it.
Example of old XML format:
<?xml version="1.0" encoding="UTF-8"?>
<pf version="1.0">
<pinfo>
<pid><![CDATA[test1 pid]]></pid>
<picture><![CDATA[http://test1.image]]></picture>
</pinfo>
<pinfo>
<pid><![CDATA[test2 pid]]></pid>
<picture><![CDATA[http://test2.image]]></picture>
</pinfo>
</pf>
and then the new XML format (note "category name" added):
<?xml version="1.0" encoding="UTF-8"?>
<pf version="1.2">
<category name="Cname1">
<pinfo>
<pid><![CDATA[test1 pid]]></pid>
<picture><![CDATA[http://test1.image]]></picture>
</pinfo>
</category>
<category name="Cname2">
<pinfo>
<pid><![CDATA[test2 pid]]></pid>
<picture><![CDATA[http://test2.image]]></picture>
</pinfo>
</category>
</pf>
And below the old code for parsing that doesn't work since the addition of "category name" in the XML:
$pinfo = new SimpleXMLElement($_SERVER['DOCUMENT_ROOT'].'/xml/file.xml', null, true);
foreach($pinfo as $resource)
{
$Profile_id = $resource->pid;
$Image_url = $resource->picture;
// and then some echo´ing of the collected data inside the loop
}
What do I need to add or do completely different? I tried with xpath,children and sorting by attributes but no luck - SimpleXML has always been a mystery to me :)
You were iterating over all <pinfo> elements located in the root element previously:
foreach ($pinfo as $resource)
Now all <pinfo> elements have moved from the root element into the <category> elements. You now need to query those elements first:
foreach ($pinfo->xpath('/*/category/pinfo') as $resource)
The now wrong named variable $pinfo is standing a bit in the way so it better do some more changes:
$xml = new SimpleXMLElement($_SERVER['DOCUMENT_ROOT'].'/xml/file.xml', null, true);
$pinfos = $xml->xpath('/*/category/pinfo');
foreach ($pinfos as $pinfo) {
$Profile_id = $pinfo->pid;
$Image_url = $pinfo->picture;
// ... and then some echo´ing of the collected data inside the loop
}
The category elements exist as their own array when you load the XML file. The XML you are used to parsing is contained within. All you need to do is wrap your current code with another foreach. Other than that there isn't much to change.
foreach($pinfo as $category)
{
foreach($category as $resource)
{
$Profile_id = $resource->pid;
$Image_url = $resource->picture;
// and then some echo´ing of the collected data inside the loop
}
}
This question already has answers here:
PHP SimpleXML: insert node at certain position
(2 answers)
Closed 8 years ago.
I'm trying to add a comment list to a page using an xml file. I'd like to list the comments most recent first, so when a new comment is added, I'd like to add it to the start of the xml. addChild appends to the end, so that's no good, and I can't get my head around the the DOMNode insert_before method, as I want to add it at the start before every other occurrence of a child (and I can't find an example anywhere that does this - weird).
xml file looks like;
<comments>
<comment>
<date>20130625</date>
<name>Jocky Wilson</name>
<text>Something about darts presumably</text>
</comment>
<comment>
<date>20130622</date>
<name>Jacky Wilson</name>
<text>It was reet petite etc</text>
</comment>
</comments>
I create the file initially with;
<?php
$xmlData = "< load of xml etc...";
$xml = new SimpleXMLElement($xmlData);
file_put_contents("comments.xml", $xml->asXML());
?>
And that works fine. Any suggestions at all gratefully received.
As an alternative to the solutions mentioned in the comments:
use addChild, let it add the node wherever it wants to, sort it by <date> and echo it:
$xml = simplexml_load_string($x); // assume XML in $x
$comments = $xml->xpath("//comment");
$field = 'date';
sort_obj_arr($comments, $field, SORT_DESC);
var_dump($comments);
// function sort_obj_array written by GZipp, see link below
function sort_obj_arr(& $arr, $sort_field, $sort_direction) {
$sort_func = function($obj_1, $obj_2) use ($sort_field, $sort_direction) {
if ($sort_direction == SORT_ASC) {
return strnatcasecmp($obj_1->$sort_field, $obj_2->$sort_field);
} else {
return strnatcasecmp($obj_2->$sort_field, $obj_1->$sort_field);
}
};
usort($arr, $sort_func);
}
see GZipp's original function: Sorting an array of SimpleXML objects
This question already has answers here:
SimpleXML: Selecting Elements Which Have A Certain Attribute Value
(2 answers)
Implementing condition in XPath [duplicate]
(2 answers)
Closed 9 years ago.
I am trying to parse out the value of a node I am referencing by one of its attributes. but I am not sure of the syntax
XML:
<data>
<poster name="E-Verify" id="everify">
<full_image url="e-verify-swa-poster.jpg"/>
<full_other url=""/>
</poster>
<poster name="Minimum Wage" id="minwage">
<full_image url="minwage.jpg"/>
<full_other url="spa_minwage.jpg"/>
</poster>
</data>
here is where I want to get the url value of full_image where poster id equal to minwage:
$xml = simplexml_load_file('PosterData.xml');
$main_url = $xml->full_image[name] where poster[id] = "minwage";
//something like that.
echo $main_url;
Result: minwage.jpg
If anyone has any resources that cover this stuff please share.
You should be able to use SimpleXMLElement::xpath() to run an xpath query on a simple XML document.
$xml = simplexml_load_file('PosterData.xml');
$main_url = $xml->xpath('name[#id="minwage"]/full_image')[0];
echo $main_url;
Simply loop the poster elements and remember to cast the attribute values to strings, since you want to compare them (and probably output them) as strings:
$xml = simplexml_load_file('PosterData.xml');
foreach ($xml->poster as $poster) {
if ((string) $poster['id'] == 'minwage') {
echo (string) $poster->full_image['url'];
}
}
You can use:
$object = simplexml_load_string('<data><poster name="E-Verify" id="everify"><full_image url="e-verify-swa-poster.jpg"/><full_other url=""/></poster><poster name="Minimum Wage" id="minwage"><full_image url="minwage.jpg"/><full_other url="spa_minwage.jpg"/></poster></data>');
foreach ($object as $value) {
echo "URL: ".$value->full_image->attributes();
echo "<br>";
}
Either use simplexml_load_file('Some external file.xml') if calling external file.