Delete from xml according to attribute [duplicate] - php

This question already has answers here:
How do I remove a specific node using its attribute value in PHP XML Dom?
(4 answers)
Closed 9 years ago.
My xml file is named cgal.xml
<?xml version="1.0"?>
<item>
<name><![CDATA[<img src="event_pic/pic1.jpg" />CALENDAR]]></name>
<description title="NAM ELIT AGNA, ENDRERIT SIT AMET, TINCIDUNT AC." day="13" month="8" year="2010" id="15"><![CDATA[<img src="events/preview/13p1.jpg" /><font size="8" color="#6c6e74">In Gladiator, victorious general Maximus Decimus Meridias has been named keeper of Rome and its empire by dying emperor Marcus Aurelius, so that rule might pass from the Caesars back to the people and Senate. Marcus\' neglected and power-hungry son, Commodus, has other ideas, however. Escaping an ordered execution, Maximus hurries back to his home in Spain, too l</font>]]></description>
</item>
and my PHP function is:-
$doc = new DOMDocument;
$doc->formatOutput = TRUE;
$doc->preserveWhiteSpace = FALSE;
$doc->simplexml_load_file('../cgal.xml');
foreach($doc->description as $des)
{
if($des['id'] == $id) {
$dom=dom_import_simplexml($des);
$dom->parentNode->removeChild($dom);
}
}
$doc->save('../cgal.xml');
id is passed dynamically
I want to remove node according to id

You dont need to load or import the XML from SimpleXml. You can load it directly with DOM. Also, you can remove the node in the same way as you are doing in your question updatin xml in php. Just change the XPath Query to read
$query = sprintf('//description[#id="%s"]', $id);
or
$query = sprintf('/item/description[#id="%s"]', $id);
You can also use getElementById instead of an XPath, if your XML validates against a DTD or Schema that actually defines id as an XML ID. This is explained in Simplify PHP DOM XML parsing - how?.

Well, first off, there's no DomDocument::simplexml_load_file() method. Either use dom document, or don't... So using DomDocument:
$doc = new DomDocument();
$doc->formatOutput = true;
$doc->preserveWhiteSpace = true;
$doc->loadXml(file_get_contents('../cgal.xml'));
$element = $doc->getElementById($id);
if ($element) {
$element->parentNode->removeChild($element);
}
That should do it for you...
Edit:
As Gordon points out, that may not work (I tried it, it doesn't all the time)... So, you could either:
$xpath = new DomXpath($doc);
$elements = $xpath->query('//description[#id="'.$id.'"]');
foreach ($elements as $element) {
$element->parentNode->removeChild($element);
}
Or, using SimpleXML, you can recurse over each node (less performant, but more flexible):
$simple = simplexml_load_file('../cgal.xml', 'SimpleXmlIterator');
$it = new RecursiveIteratorIterator($simple, RecursiveIteratorIterator::SELF_FIRST);
foreach ($it as $element) {
if (isset($element['id']) && $element['id'] == $id) {
$node = dom_import_simplexml($element);
$node->parentNode->removeChild($node);
}
}

Related

PHP - From DOMDocument to XMLReader [duplicate]

This question already has answers here:
How to use XMLReader in PHP?
(7 answers)
Closed 6 years ago.
PHP developers here ??
I have a PHP function who parse an xml file (using DOMDocument, i'm proficien with this tool). I want to do the same with XMLReader, but i don't understand how XMLReader works...
I want to use XMLReader because it's a light tool.
Feel free to ask me others questions about my issue.
function getDatas($filepath)
{
$doc = new DOMDocument();
$xmlfile = file_get_contents($filepath);
$doc->loadXML($xmlfile);
$xmlcars = $doc->getElementsByTagName('car');
$mycars= [];
foreach ($xmlcars as $xmlcar) {
$car = new Car();
$car->setName(
$xmlcar->getElementsByTagName('id')->item(0)->nodeValue
);
$car->setBrand(
$xmlcar->getElementsByTagName('brand')->item(0)->nodeValue
);
array_push($mycars, $car);
}
return $mycars;
}
PS : I'm not a senior PHP dev.
Ahah Thanks.
This is a good example from this topic, I hope it helps you to understand.
$z = new XMLReader;
$z->open('data.xml');
$doc = new DOMDocument;
// move to the first <product /> node
while ($z->read() && $z->name !== 'product');
// now that we're at the right depth, hop to the next <product/> until the end of the tree
while ($z->name === 'product')
{
// either one should work
//$node = new SimpleXMLElement($z->readOuterXML());
$node = simplexml_import_dom($doc->importNode($z->expand(), true));
// now you can use $node without going insane about parsing
var_dump($node->element_1);
// go to next <product />
$z->next('product');
}
XMLReader does not, as far as I can tell, have some equivalent way of filtering by an element name. So the closest equivalent to this would be, as mentioned in rvbarreto's answer, to iterate through all elements using XMLReader->read() and grabbing the info you need when the element name matches what you are wanting.'
Alternatively, you might want to check out SimpleXML, which supports filtering using XPath expressions, as well as seeking to a node in the XML using the element structure like they are sub-objects of the main object. For instance, instead of using:
$xmlcar->getElementsByTagName('id')->item(0)->nodeValue;
You would use:
$xmlcar->id[0];
Assuming all of your car elements are at the first level of the XML document tree, the following should work as an example:
function getDatas($filepath) {
$carsData = new SimpleXMLElement($filepath, NULL, TRUE);
$mycars = [];
foreach($carsData->car as $xmlcar) {
$car = new Car();
$car->setName($xmlcar->id[0]);
$car->setBrand($xmlcar->id[0]);
$mycars[] = $car;
}
}

SimpleXML how to get line number of a node?

I'm using this in SimpleXML and PHP:
foreach ($xml->children() as $node) {
echo $node->attributes('namespace')->id;
}
That prints the id attribute of all nodes (using a namespace).
But now I want to know the line number that $node is located in the XML file.
I need the line number, because I'm analyzing the XML file, and returning to the user information of possible issues to resolve them. So I need to say something like: "Here you have an error at line X". I'm sure that the XML file would be in a standard format that will have enough line breaks for this to be useful.
It is possible with DOM. DOMNode provides the function getLineNo().
DOM
$xml = <<<'XML'
<foo>
<bar/>
</foo>
XML;
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXpath($dom);
var_dump(
$xpath->evaluate('//bar[1]')->item(0)->getLineNo()
);
Output:
int(2)
SimpleXML
SimpleXML is based on DOM, so you can convert SimpleXMLElement objects to DOMElement objects.
$element = new SimpleXMLElement($xml);
$node = dom_import_simplexml($element->bar);
var_dump($node->getLineNo());
And yes, most of the time if you have a problem with SimpleXML, the answer is to use DOM.
XMLReader
XMLReader has the line numbers internally, but here is no direct method to access them. Again you will have to convert it into a DOMNode. It works because both use libxml2. This will read the node and all its descendants into memory, so be careful with it.
$reader = new XMLReader();
$reader->open('data://text/xml;base64,'.base64_encode($xml));
while ($reader->read()) {
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name== 'bar') {
var_dump($reader->expand()->getLineNo());
}
}

Using PHP DOM to extract a certain node value, only if another node value is > 0

I am using php dom to parse xml from another platform, extract certain data from it, and upload to my own platform. I am however stuck when it comes to extracting a certain node value, only if another node value is greater than 0 for the child node 'row'. In the example below, I would like to iterate over the xml and pull out the 'affcustomid' value only if the CPACommission node value is greater than 0. Does anyone have any ideas how I can do this? The below code is a shortened version, in reality, i would get back 100's of rows in the same format as below.
<row>
<rowid>1</rowid>
<currencysymbol>€</currencysymbol>
<totalrecords>2145</totalrecords>
<affcustomid>11159_4498302</affcustomid>
<period>7/1/2014</period>
<impressions>0</impressions>
<clicks>1</clicks>
<clickthroughratio>0</clickthroughratio>
<downloads>1</downloads>
<downloadratio>1</downloadratio>
<newaccountratio>1</newaccountratio>
<newdepositingacc>1</newdepositingacc>
<newaccounts>1</newaccounts>
<firstdepositcount>1</firstdepositcount>
<activeaccounts>1</activeaccounts>
<activedays>1</activedays>
<newpurchases>12.4948</newpurchases>
<purchaccountcount>1</purchaccountcount>
<wageraccountcount>1</wageraccountcount>
<avgactivedays>1</avgactivedays>
<netrevenueplayer>11.8701</netrevenueplayer>
<Deposits>12.4948</Deposits>
<Bonus>0</Bonus>
<NetRevenue>11.8701</NetRevenue>
<TotalBetsHands>4</TotalBetsHands>
<Product1Bets>4</Product1Bets>
<Product1NetRevenue>11.8701</Product1NetRevenue>
<Product1Commission>30</Product1Commission>
<Commission>0</Commission>
<CPACommission>30</CPACommission>
</row>
Thanks in advance!
Mark
The easiest way to fetch data from an XML DOM is Xpath:
$dom = new DOMDocument();
$dom->load('file.xml');
$xpath = new DOMXpath($dom);
var_dump(
$xpath->evaluate('string(//row[CPACommission > 0]/affcustomid)')
);
It would be easier using SimpleXML:
$doc = simplexml_load_file('file.xml');
foreach ($doc->row AS $row) {
if($row->CPACommission > 0){
echo $row->affcustomid;
}
}
But if you still need to use DOMDocument:
$doc = new DOMDocument();
$doc->load('file.xml');
foreach ($doc->getElementsByTagName('row') AS $row) {
if($row->getElementsByTagName('CPACommission')->item(0)->textContent > 0){
echo $row->getElementsByTagName('affcustomid')->item(0)->textContent;
}
}

Assigning a node to an arbitrary node, how to with Libxml2?

This question use PHP, but the problems and algorithms are valid for many other Libxml2 and W3C DOM implementations.
Core problem: there are no $node->replaceThisBy($otherNode). There are only "replace text" (using nodeValue property) and the replaceChild() method — not obviuos neither simple to use.
In the code below, only the second loop works, but I need copy nodes from one DOM tree (simulated by a clone) to another one.
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->load($fileXML);
$xp = new DOMXpath($doc);
$lst = $xp->query("//td");
$z = clone $lst->item(2); // a big and complex node
// needs clone to freeze the node content (before change it also).
// does NOT work:
foreach ($lst as $node)
$node = $z; // no error messages!
//error: $node->parentNode->replaceChild($z,$node);
// This works though:
foreach ($lst as $node)
$node->nodeValue = $z->nodeValue;
Similar questions:
PHP DOM replace element with a new element
PHP DOMDocument question: how to replace text of a node?
nodeValue property, changes only text-value. To change all tags and contents, need a lot more instructions -- DomDocument is not friendly (!) ... Need to import a clone, and clone in the loop: solved!
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->loadXML($xmlFrag);
$xp = new DOMXpath($doc);
$lst = $xp->query("//p");
$import = $doc->importNode( $lst->item(1)->cloneNode(true) , TRUE);
foreach ($lst as $node) {
$tmp = clone $import; // clone because if same, ignores loop.
$node->parentNode->replaceChild($tmp,$node);
}
print $doc->saveXML();

PHP DOMDocument how to get that content of this tag?

I am using domDocument hoping to parse this little html code. I am looking for a specific span tag with a specific id.
<span id="CPHCenter_lblOperandName">Hello world</span>
My code:
$dom = new domDocument;
#$dom->loadHTML($html); // the # is to silence errors and misconfigures of HTML
$dom->preserveWhiteSpace = false;
$nodes = $dom->getElementsByTagName('//span[#id="CPHCenter_lblOperandName"');
foreach($nodes as $node){
echo $node->nodeValue;
}
But For some reason I think something is wrong with either the code or the html (how can I tell?):
When I count nodes with echo count($nodes); the result is always 1
I get nothing outputted in the nodes loop
How can I learn the syntax of these complex queries?
What did I do wrong?
You can use simple getElementById:
$dom->getElementById('CPHCenter_lblOperandName')->nodeValue
or in selector way:
$selector = new DOMXPath($dom);
$list = $selector->query('/html/body//span[#id="CPHCenter_lblOperandName"]');
echo($list->item(0)->nodeValue);
//or
foreach($list as $span) {
$text = $span->nodeValue;
}
Your four part question gets an answer in three parts:
getElementsByTagName does not take an XPath expression, you need to give it a tag name;
Nothing is output because no tag would ever match the tagname you provided (see #1);
It looks like what you want is XPath, which means you need to create an XPath object - see the PHP docs for more;
Also, a better method of controlling the libxml errors is to use libxml_use_internal_errors(true) (rather than the '#' operator, which will also hide other, more legitimate errors). That would leave you with code that looks something like this:
<?php
libxml_use_internal_errors(true);
$dom = new DOMDocument();
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
foreach($xpath->query("//span[#id='CPHCenter_lblOperandName']") as $node) {
echo $node->textContent;
}

Categories