This question already has answers here:
Remove a child with a specific attribute, in SimpleXML for PHP
(18 answers)
Closed 8 years ago.
I have an xml file of this structure
<?xml version="1.0" encoding="iso-8859-1"?>
<my_events>
<event id="e20111129215359">
<title>the title</title>
<channel id="1">
<name>A name</name>
<onclick></onclick>
</channel>
<event_site>
<name/>
<url/>
</event_site>
<start_date>Thu Mar 08 2012</start_date>
<start_time>11:00 AM</start_time>
<end_date>null</end_date>
<end_time>null</end_time>
<notes>Notes for the event</notes>
</event>
</my_events>
To delete an event, I have this php function.
<?php
include_once("phpshared.php");
function delete_event( $nodeid ) {
$nodes = new SimpleXMLElement('my_events.xml', LIBXML_NOCDATA, true);
$node = $nodes->xpath("/my_events/event[#id='$nodeid']");
$node->parentNode->removeChild($node);
$formatted = formatXmlString($nodes->asXML());
$file = fopen ('my_events.xml', "w");
fwrite($file, $formatted);
fclose ($file);
}
echo delete_event(trim($_REQUEST['nodeid']));
?>
That doesn't delete the node. Is there a different way to do this?
SimpleXML allows removal of elements via PHP's unset() keyword.
For your code snippet, simply replace
$node->parentNode->removeChild($node);
with
if ( ! empty($node)) {
unset($node[0][0]);
}
If the XPath query returned a matching <event> element, we instruct SimpleXML to unset() it.
Aside: here are two occurrences of [0] because:
xpath() returns an array, even if only one element matches. So [0] is used to get the first item in that array, which is the element we want to delete.
The SimpleXMLElement returned from $node[0] represents a collection of <event> elements (but if you access elements/attributes on it then the values from the first in the collection is used). So, we use [0] to get at the actual SimpleXMLElement that we want to delete, which is the first in this magical collection.
Use unset(): Remove a child with a specific attribute, in SimpleXML for PHP
Related
We are using SimpleXML to try and convert XML to JSON, and in turn convert to a PHP object, so that we can compare out Soap API with our Rest API. We have a request that returns quite a lot of data, but the part in question is where we have a nested array.
The array is returned with the tag in XML, however we do not want this translated into the JSON.
The XML that we get is as follows:
<apns>
<item>
<apn>apn</apn>
</item>
</apns>
So when it is translated into JSON it looks like this:
{"apns":{"item":{"apn":"apn"}}
In reality, we want SimpleXML to convert to the same JSON as in our Rest API, which looks like the following:
{"apns":[{"apn":"apn"}]}
The array could contain more than one thing, for example:
<apns>
<item>
<apn>apn</apn>
</item>
<item>
<apn>apn2</apn>
</item>
</apns>
Which I'm assuming will just error in JSON or have the first one overwritten.
I'd expect SimpleXML to be able to handle this natively, but if not has anyone got a fix that doesn't involve janky string manipulation?
TIA :)
A generic conversion has no possibility to know that a single element should be an array in JSON.
SimpleXMLElement properties can be treated as an Iterable to traverse sibling with the same name. They can be treated as an list or a single value.
This allows you to build up your own array/object structure and serialize it to JSON.
$xml = <<<'XML'
<apns>
<item>
<apn>apn1</apn>
</item>
<item>
<apn>apn2</apn>
</item>
</apns>
XML;
$apns = new SimpleXMLElement($xml);
$json = [
'apns' => []
];
foreach ($apns->item as $item) {
$json['apns'][] = ['apn' => (string)$item->apn];
}
echo json_encode($json, JSON_PRETTY_PRINT);
This still allows you to read/convert parts in a general way. Take a more in deep look at the SimpleXMLElement class. Here are method to iterate over all children or to get the name of the current node.
I hope this code is useful as a template to what your after, the problem is that it's difficult to know if this is the only instance of what your trying to do...
What this does is first looks for any nodes which have a item/apn structure underneath using XPath (//*[item/apn] says any node //* with the following nodes underneath).
Then it loops through these items and adds new <apn> nodes underneath the start node (the <apns> node in this case) from each <item> with the value ($list->addChild("apn", (string)$item->apn);.
Once the nodes are copied it removes all of the <item> nodes (unset($list->item);).
$input = '<apns>
<item>
<apn>apn</apn>
</item>
<item>
<apn>apn2</apn>
</item>
</apns>';
$xml = simplexml_load_string($input);
$itemList = $xml->xpath("//*[item/apn]");
foreach ( $itemList as $list ) {
foreach ( $list->item as $item ) {
$list->addChild("apn", (string)$item->apn);
}
unset($list->item);
}
echo $xml->asXML();
gives...
<?xml version="1.0"?>
<apns>
<apn>apn</apn><apn>apn2</apn></apns>
and
echo json_encode($xml);
gives...
{"apn":["apn","apn2"]}
If you just want the last value, then you can just keep track of the last value and set the new element outside the inner loop...
$itemList = $xml->xpath("//*[item/apn]");
foreach ( $itemList as $list ) {
foreach ( $list->item as $item ) {
$apn = (string)$item->apn;
}
$list->addChild("apn", $apn);
unset($list->item);
}
I have xml like:
<root xmlns="urn:test:apis:baseComponents">
<books>
<book>
<name>50 shades of grey</name>
</book>
</books>
<disks>
<disk>
<name>Britney Spears</name>
</disk>
</disks>
</root>
And such php code:
$xml = new SimpleXMLElement($xml);
$books = $xml->books;
$disks = $xml->disks;
$disks->registerXPathNamespace('x', 'urn:test:apis:baseComponents');
$books->registerXPathNamespace('x', 'urn:test:apis:baseComponents');
$b_names = $books->xpath('//x:name');
b_names contains array with 2 values instead of 1. First holds books->book->name, second holds disks->disk->name.
Can you please explain what am I doing wrong and how could I find children of only one element?
The reason that I am using xpath instead of taking manually values using SimpleXMLElement, is that I don't know what value, which I want to search in advance.
Use $books->xpath('.//x:name') to search descendants of your $books variable and not descendants of the root node/document node (which the path //x:name does).
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>
This question already has answers here:
How to retrieve data from # xml attribute in PHP
(2 answers)
Closed 8 years ago.
I saw lot of examples but nothing is working perfectly. This is the array i got after parsing.
SimpleXMLElement Object
(
[#attributes] => Array
(
[areaUnits] => acre
)
)
Now i try to get attributes,like this:
var_dump($list->attributes());
I got this error:
var_dump(): Node no longer exists
<?php
function xml_attribute($object, $attribute)
{
if(isset($object[$attribute]))
return (string) $object[$attribute];
}
print xml_attribute($xml, 'areaUnits'); //prints "acre"
?>
Obtaining the attributes of a SimpleXMLElement is very straight forward.
The XML:
<?xml version="1.0"?>
<root>
<node attribute1="value1" attribute2="value2">data</node>
</root>
The PHP:
// assume $xml variable contains the XML document above
$sxe = new SimpleXMLElement($xml)
$value1 = $sxe->node->attributes()->attribute1;
$value2 = $sxe->node->attributes()->attribute2;
In your example above, $list MUST reference an actual XML node in order for you to attempt to access its attributes. Based on your error, it sounds like you're not doing that, which can often happen if you modify the XML structure referenced by $list at run time.
I am parsing XML strings using simplexml_load_string(), but I noticed that i don't get the name of the very first tag.
For example, I have these two xml strings:
$s = '<?xml version="1.0" encoding="UTF-8"?>
<ParentTypeABC>
<chidren1>
<children2>1000</children2>
</chidren1>
</ParentTypeABC>
';
$t = '<?xml version="1.0" encoding="UTF-8"?>
<ParentTypeDEF>
<chidren1>
<children2>1000</children2>
</chidren1>
</ParentTypeDEF>
';
NOTICE that they are nearly identical, the only difference being that one has the first node as <ParentTypeABC> and the other as <ParentTypeDEF>
then I just convert them to SimpleXML objects:
$o = simplexml_load_string($s);
$p = simplexml_load_string($t);
but then i have two equal objects, none of them having the "top" node's name appearing, either ParentTypeABC or ParentTypeDEF (I examine the objects using print_r()):
// with top node "ParentTypeABC"
SimpleXMLElement Object
(
[chidren1] => SimpleXMLElement Object
(
[children2] => 1000
)
)
// with top node "ParentTypeDEF"
SimpleXMLElement Object
(
[chidren1] => SimpleXMLElement Object
(
[children2] => 1000
)
)
So how I am supposed to know the top node's name? If I parse unknown XMLs and I need to know what's the top node name, what can I do?
Is there an option in simplexml_load_string() I could use?
I know there are MANY ways to parse XML's with PHP, but I'd like it to be as simple as posible, and to get a simple object or array I could navigate easily.
I made a simple example here to fiddle with.
SimpleXML has a getName() method.
echo $xml->getName();
This should return the name of the respective node, no matter if root or not.
http://php.net/manual/en/simplexmlelement.getname.php