Delete Node isn't working with Simple XML (PHP) - php

I want to delete a node if the title of an node is matching a filter (array). I use unset() and I already tried $node and $item but both arguments won't delete my node...
What is wrong in this code? - I do enter the if condition, because I see in if in my console!
$dom = new DOMDocument('1.0', 'utf-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load("shop1.xml");
$pathXML = "/products/product";
$titleArray = array("Test", "Battlefield 1");
$doc = simplexml_import_dom($dom);
$items = $doc->xpath($pathXML);
foreach ($items as $item) {
$node = dom_import_simplexml($item);
$title = $node->getElementsByTagName('title')->item(0)->textContent;
echo $title . "\n";
foreach ($titleArray as $titles) {
echo $titles . "\n";
if (mb_stripos($title, $titles) !== false) {
echo "in if\n\n";
unset($item);
}
}
}
$dom->saveXML();
$dom->save("shop1_2.xml");
XML File:
<products>
<product>
<title>Battlefield 1</title>
<url>https://www.google.de/</url>
<price>0.80</price>
</product>
<product>
<title>Battlefield 2</title>
<url>https://www.google.de/</url>
<price>180</price>
</product>
</products>
Greetings and Thank You!

All you're doing is unsetting a local variable. Instead you need to alter the DOM:
$dom = new DOMDocument('1.0', 'utf-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load("shop1.xml");
$xpathQuery = "/products/product";
$titleArray = array("Test", "Battlefield 1");
$xp = new DomXpath($dom);
$items = $xp->query($xpathQuery);
foreach ($items as $item) {
$title = $item->getElementsByTagName('title')->item(0)->textContent;
echo "$title\n";
if (in_array($title, $titleArray)) {
$item->parentNode->removeChild($item);
}
}
$dom->saveXML();
$dom->save("shop1_2.xml");

Related

How to search in XML file using php?

I don't know if there is a method to search in xml file. For instance. I want to get the Value using the Name from AttrList and the ProductCode. Is it possible ? This is how my xml look likes:
<Product>
<ProductCode>70-14UF44-00</ProductCode>
<Vendor>NBM</Vendor>
<ProductType>Soft. Unsorted application</ProductType>
<ProductCategory>Software</ProductCategory>
<ProductDescription>{Bluetooth Driver IVT V.1.4.9.3, 1pk, Full Package, OEM, 1pk for 12M3W/15G3WS, 1pk, 1pk}</ProductDescription>
<Image>https://www.it4profit.com/catalogimg/wic/1/70-14UF44-00</Image>
<ProductCard>https://content.it4profit.com/itshop/itemcard_cs.jsp?ITEM=50409104050320315&THEME=asbis&LANG=ro</ProductCard>
<AttrList>
<element Name="Tipul licentei" Value="Full Package"/>
<element Name="License Conditions" Value="OEM"/>
<element Name="Produs de baza(1)" Value="12M3W/15G3WS"/>
<element Name="Greutatea bruta a pachetului" Value="1.546 kg"/>
<element Name="Bucati in pachet" Value="1"/>
</AttrList>
<MarketingInfo>
<element></element>
</MarketingInfo>
<Images/>
</Product>
Im using the SimpleXML libraries from PHP
Trying with DOMDocument:
$doc = new DOMDocument;
$doc->preserveWhiteSpace = false;
$doc->Load('produse_catalog.xml');
$xpath = new DOMXPath($doc);
$query = '//ProductCatalog/Product/ProductCode[. = "PMP5297C_QUAD"]';
$entries = $xpath->query($query);
foreach ($entries as $entry) {
echo "Found {$entry->previousSibling->previousSibling->nodeValue}," .
" by {$entry->previousSibling->nodeValue}\n";
}
The result is: Notice: Trying to get property of non-object. What am i doing wrong ?
Yes you can use simplexml with xpath in this case:
$xml = simplexml_load_file('path/to/xml/file.xml');
$name = 'Tipul licentei';
$product_code = '70-14UF44-00';
$products = $xml->xpath("//Product/ProductCode[contains(text(), '$product_code')]/following-sibling::AttrList/element[#Name='$name']");
if(count($products) > 0) { // if found
$value = (string) $products[0]->attributes()->Value;
echo $value; // Full Package
}
Sample Output
Also possible with DOMDocument:
$dom = new DOMDocument();
$dom->load('path/to/xml/file.xml');
$xpath = new DOMXpath($dom);
$name = 'Tipul licentei';
$product_code = '70-14UF44-00';
$value = $xpath->evaluate("string(//Product/ProductCode[contains(text(), '$product_code')]/following-sibling::AttrList/element[#Name='$name']/#Value)");
echo $value; // Full Package
You can use the DOMXPath for this purpose.
Example taken from php.net
$doc = new DOMDocument;
$doc->preserveWhiteSpace = false;
$doc->Load('book.xml');
$xpath = new DOMXPath($doc);
// We starts from the root element
$query = '//book/chapter/para/informaltable/tgroup/tbody/row/entry[. = "en"]';
$entries = $xpath->query($query);
foreach ($entries as $entry) {
echo "Found {$entry->previousSibling->previousSibling->nodeValue}," .
" by {$entry->previousSibling->nodeValue}\n";
}

Why is my PHP-generated RSS declaring rogue tags?

This is my code which I expected to generate normal RSS. However, after each <item> there's a </channel></rss><?xml version="1.0"?><rss version="2.0">. What do I need to change so that this is only declared at the end of the script as it should be?
Do let me know if I've missed out any important information.
function jobscrape($title, $link, $root, $description, $job_location) {
header("Content-Type: application/rss+xml; charset=UTF-8");
$xml = new SimpleXMLElement('<rss/>');
$xml->addAttribute("version", "2.0");
$channel = $xml->addChild("channel");
$channel->addChild("title", $title);
$channel->addChild("link", $link);
$channel->addChild("description", "This is a description");
$channel->addChild("language", "en-us");
$html = file_get_contents($link);
$doc = new DOMDocument();
libxml_use_internal_errors(TRUE);
if(!empty($html)) {
$doc->loadHTML($html);
libxml_clear_errors(); // remove errors for yucky html
$xpath = new DOMXPath($doc);
$row = $xpath->query($job_location);
if ($row->length > 0) {
foreach ($row as $job) {
$jobs = array();
$entries = array();
$jobs['title'] = $job->nodeValue;
$jobs['description'] = "This is a description";
$jobs['link'] = $job->getAttribute('href');
array_push($entries,$jobs);
foreach ($entries as $entry) {
$item = $channel->addChild("item");
$item->addChild("title", $entry['title']);
$item->addChild("link", $entry['link']);
$item->addChild("description", $entry['description']);
}
echo $xml->asXML();
}
}
else { echo "row is less than 0";}
}
else {
echo "this is empty";
}
}
Create one XML document, add all your items to that document, and call ->asXML(); on the complete document at the end (instead of on each fragment every time you go around the loop).

Replace HTML tags with DOMdocument while keeping nested tags [duplicate]

I'm trying to figure out how to rename a node in XML using PHP?
I Have come this far:
$dom = new DOMDocument( '1.0' );
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
// load the xml file
$dom->loadXML( '<?xml version="1.0" encoding="ISO-8859-1"?>
<library>
<data>
<invite>
<username>jmansa</username>
<userid>1</userid>
</invite>
<update>1</update>
</data>
</library>', LIBXML_NOBLANKS );
$xpath = new DOMXPath($dom);
//find all 'data' nodes.
$node = $xpath->query("//data");
// if found
if( $node->length ) {
foreach ($node as $n) {
// RENAME HERE? //
}
}
echo "<xmp>". $dom->saveXML() ."</xmp>";
Now, I want to rename <data> to <invites>. Can this be done and if yes, how?
A Node's name ("data" or "invites" respectively) cannot be renamed via the DOM because the Node::nodeName property is read-only.
You can create a new node named "invites", append it before the "data" node, move the children of "data" to the new "invites" node, remove the "data" node, and then output the tree to get your result.
Example:
<?php
// Create a test document.
$dom = new DOMDocument( '1.0' );
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
// Load the xml file.
$dom->loadXML('<?xml version="1.0" encoding="ISO-8859-1"?'.'>
<library>
<data attr1="1" attr2="2">
<invite>
<username>jmansa</username>
<userid>1</userid>
</invite>
<update>1</update>
</data>
</library>', LIBXML_NOBLANKS );
$xpath = new DOMXPath($dom);
// Convert <data> to <invites>.
if ($dataNode = $xpath->query("/library/data")->item(0))
{
// Create the <invites> node.
$invitesNode = $dom->createElement('invites');
$dataAttrs = $dataNode->attributes;
foreach ($dataAttrs as $dataAttr)
{ $invitesNode->setAttributeNodeNS($dataAttr->cloneNode()); }
$dom->documentElement->appendChild($invitesNode);
// Move the <data> children over.
if ($childNodes = $xpath->query("/library/data/*"))
{
foreach ($childNodes as $childNode)
{ $invitesNode->appendChild($childNode); }
}
// Remove <data>.
$dataNode->parentNode->removeChild($dataNode);
}
// Test the result.
echo $dom->saveXML();
?>
My solution, with extended test case:
// Changes the name of element $element to $newName.
function renameElement($element, $newName) {
$newElement = $element->ownerDocument->createElement($newName);
$parentElement = $element->parentNode;
$parentElement->insertBefore($newElement, $element);
$childNodes = $element->childNodes;
while ($childNodes->length > 0) {
$newElement->appendChild($childNodes->item(0));
}
$attributes = $element->attributes;
while ($attributes->length > 0) {
$attribute = $attributes->item(0);
if (!is_null($attribute->namespaceURI)) {
$newElement->setAttributeNS('http://www.w3.org/2000/xmlns/',
'xmlns:'.$attribute->prefix,
$attribute->namespaceURI);
}
$newElement->setAttributeNode($attribute);
}
$parentElement->removeChild($element);
}
function prettyPrint($d) {
$d->formatOutput = true;
echo '<pre>'.htmlspecialchars($d->saveXML()).'</pre>';
}
$d = new DOMDocument( '1.0' );
$d->loadXML('<?xml version="1.0"?>
<library>
<data a:foo="1" x="bar" xmlns:a="http://example.com/a">
<invite>
<username>jmansa</username>
<userid>1</userid>
</invite>
<update>1</update>
</data>
</library>');
$xpath = new DOMXPath($d);
$elements = $xpath->query('/library/data');
if ($elements->length == 1) {
$element = $elements->item(0);
renameElement($element, 'invites');
}
prettyPrint($d);
By the way, I added this solution as a comment to the PHP documentation for DOMElement.

Replace & Modify XML node in PHP

Here's a sample XML structure:
<Products>
<Product>
<Id>1</Id>
<Name>Product 1</Name>
<Category>MEN</Category>
<Category>Women</Category>
<Product>
<Product>
<Id>2</Id>
<Name>Product 2</Name>
<Category>MEN2</Category>
<Category>Women2</Category>
<Product>
</Products>
And I want the file like this:
<Products>
<Product>
<Id>1</Id>
<Name>Product 1</Name>
<CategoryName>MEN:Women</CategoryName>
<Product>
<Product>
<Id>2</Id>
<Name>Product 2</Name>
<CategoryName>MEN:Women</CategoryName>
<Product>
</Products>
So basically it will search through the nodes in products. If it finds "Category", it will change the name to "CategoryName" and concatenate all the sub-sequent category node values into a single one separated by semicolon.
So I have wrote this small PHP, but not sure how to get this to work.
<?php
$xmlFile = "test.xml" //assume the contents are in the file
$xml = simplexml_load_file($xmlFile);
foreach($xml as $item)
{
$name = $item->Product;
if($name->count()) //check if its a "product" node
{
foreach($item as $i)
{
$category = $i->Category;
}
}
}
?>
Can someone point me to the right direction? I haven't much worked with XML.
Please Use this
<?php
$xmlFile = "test.xml"; //assume the contents are in the file
$xml = simplexml_load_file($xmlFile);
$table = '<Products>';
foreach($xml as $item)
{
$table .= '<Product>';
$table .= '<Id>'.$item->Id.'</Id>';
$table .= '<Name>'.$item->Name.'</Name>';
$table .= '<Category>';
$i = 0;
foreach($item->Category as $cat)
{
if($i>0){
$table .= ':';
}
$table .= $cat;
$i++;
}
$table .= '</Category>';
$table .= '</Product>';
}
$table .= '</Products>';
echo $table;
?>
Try this:
$xml = '<Products>
<Product>
<Id>1</Id>
<Name>Product 1</Name>
<Category>MEN</Category>
<Category>Women</Category>
</Product>
<Product>
<Id>2</Id>
<Name>Product 2</Name>
<Category>MEN2</Category>
<Category>Women2</Category>
</Product>
</Products>';
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML( $xml, LIBXML_NOBLANKS );
$xpath = new DOMXPath( $dom );
$ProductNode = $xpath->query( "//Product" );
if( $ProductNode->length ) {
foreach ( $ProductNode as $node ) {
$category = $node->getElementsByTagName( 'Category' );
$str = '';
// store a reference to the nodes,
// so that they can be deleted later
$del = array();
foreach ( $category as $p ) {
$str .= $p->nodeValue . ':';
$del[] = $p;
}
$str = trim( $str, ':' );
$child = $dom->createElement( 'CategoryName', $str );
$node->appendChild( $child );
foreach ( $del as $p ) {
$p->parentNode->removeChild( $p );
}
}
}
header('content-type: text/xml');
echo $dom->saveXML();
Hope it helps.
Use DomDocument, if you want to modify XML while traversing it:
$xml_obj = new DOMDocument();
$xml_obj->loadXML($xml_string, LIBXML_NOBLANKS );
$xml_obj->preserveWhiteSpace = false;
$xml_obj->formatOutput = true;
$products = $xml_obj->getElementsByTagName('Product');
foreach ($products as $product) {
$cats = array();
$categories = $product->getElementsByTagName('Category');
$tot = $categories->length;
$to_delete = array();
for($i = 0; $i < $tot;$i++) {
$cat = $categories->item($i);
$cats[] = $cat->textContent;
$to_delete[] = $cat;
}
foreach ($to_delete as $delete_node) {
$product->removeChild($delete_node);
}
$product->appendChild($xml_obj->createElement('CategoryName', implode(":", $cats)));
}
print ($xml_obj->saveXML());

Need to show child data on parent id

i'm struggling with Xpath, i have an xml list and i need to get the child data based on the parent id ...
My xml file :
<projecten>
<project id="1">
<titel>Shop 1</titel>
<siteurl>http://test.be</siteurl>
<screenshot>test.jpg</screenshot>
<omschrijving>comment 1</omschrijving>
</project>
<project id="2">
<titel>Shop 2</titel>
<siteurl>http://test2.be</siteurl>
<screenshot>test2.jpg</screenshot>
<omschrijving>comment</omschrijving>
</project>
</projecten>
the code i use to get for example the project 1 data (does not work):
$xmlDoc = new DOMDocument();
$xmlDoc->load(data.xml);
$xpath = new DOMXPath($xmlDoc);
$projectId = '1';
$query = '//projecten/project[#id='.$projectId.']';
$details = $xpath->query($query);
foreach( $details as $detail )
{
echo $detail->titel;
echo $detail->siteurl;
echo $detail->screenshot;
echo $detail->omschrijving;
}
But this does not show anything, if someone can point me out ... thanks
In addition to the solution already given you can also use:
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
echo
$projectNode->getElementsByTagName('titel')->item(0)->nodeValue,
$projectNode->getElementsByTagName('siteurl')->item(0)->nodeValue,
$projectNode->getElementsByTagName('screenshot')->item(0)->nodeValue,
$projectNode->getElementsByTagName('omschrijving')->item(0)->nodeValue;
}
or fetch the DOMText node values directly with Xpath
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
echo
$xpath->evaluate('string(titel)', $projectNode),
$xpath->evaluate('string(siteurl)', $projectNode),
$xpath->evaluate('string(screenshot)', $projectNode),
$xpath->evaluate('string(omschrijving)', $projectNode);
}
or import the node to SimpleXml
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
$detail = simplexml_import_dom($projectNode);
echo
$detail->titel,
$detail->siteurl,
$detail->screenshot,
$detail->omschrijving;
}
or even concatenate all the values directly in the XPath:
$xpath = new DOMXPath($dom);
echo $xpath->evaluate(
sprintf(
'concat(
/projecten/project[#id = %1$d]/titel,
/projecten/project[#id = %1$d]/siteurl,
/projecten/project[#id = %1$d]/screenshot,
/projecten/project[#id = %1$d]/omschrijving
', $id
)
);
Accessing the child nodes as you do:
echo $detail->title;
Is not valid, if you use DOM* functions. This would probably work if you were using SimpleXML.
For DOM* try this:
$dom = new DOMDocument;
$dom->loadXml('<projecten>
<project id="1">
<titel>Shop 1</titel>
<siteurl>http://test.be</siteurl>
<screenshot>test.jpg</screenshot>
<omschrijving>comment 1</omschrijving>
</project>
<project id="2">
<titel>Shop 2</titel>
<siteurl>http://test2.be</siteurl>
<screenshot>test2.jpg</screenshot>
<omschrijving>comment</omschrijving>
</project>
</projecten>
');
$id = 2;
$xpath = new DOMXPath($dom);
foreach ($xpath->query(sprintf('/projecten/project[#id="%s"]', $id)) as $projectNode) {
// repeat this for every needed node
$titleNode = $xpath->query('titel', $projectNode)->item(0);
if ($titleNode instanceof DOMElement) {
echo $titleNode->nodeValue;
}
// or us a loop for all child nodes
foreach ($projectNode->childNodes as $childNode) {
echo $childNode->nodeValue;
}
}

Categories