I have a bookCatalog.xml file as below
<bookCatalog>
<book id='1'>
<title>html</title>
</book>
<book id='2'>
<title>java</title>
</book>
<book id='3'>
<title>php</title>
</book>
</bookCatalog>
I want to programmatically get the title value of a book node by using variable $id of book node, and i used the following code:
$doc=new DOMDocument();
$doc->load('bookCatalog.xml');
$xpath= new DOMXPath($doc);
$findBookNode=$xpath->query("//book[#id='$id']")->item(0);
foreach ($findBookNode as $child) {
if ($child->nodeName === 'title') {
$bookTitle = $child->nodeValue;
}
}
But it turned out that the result is not what i want.
If I replace the variable $id to '1' , I can get the title value of the book node whose id=1;
$findBookNode=$xpath->query("//book[#id='1']")->item(0);
I just found the problem in my code:
The problem is that the variable $id is assigned by $_POST['id'] from a form in another code segment.
$id=$_POST['id'];
Then the value of variable $id has trailing space, for example
$id='1'
become
$id='1 ' // one space after number 1
Related
I want to delete those entries where the title matches my $titleArray.
My XML files looks like:
<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>
Here is my code but I don't think that it is working and my IDE says here $node->removeChild($product); -> "Expected DOMNode, got DOMNodeList"
What is wrong and how can I fix that?
function removeProduct($dom, $productTag, $pathXML, $titleArray){
$doc = simplexml_import_dom($dom);
$items = $doc->xpath($pathXML);
foreach ($items as $item) {
$node = dom_import_simplexml($item);
foreach ($titleArray as $title) {
if (mb_stripos($node->textContent, $title) !== false) {
$product = $node->parentNode->getElementsByTagName($productTag);
$node->removeChild($product);
}
}
}
}
Thank you and Greetings!
Most DOM methods that fetch nodes return a list of nodes. You can have several element nodes with the same name. So the result will a list (and empty list if nothing is found). You can traverse the list and apply logic to each node in the list.
Here are two problems with the approach. Removing nodes modifies the document. So you have to be careful not to remove a node that you're still using after that. It can lead to any kind of unexpected results. DOMNode::getElementsByTagName() returns a node list and it is a "live" result. If you remove the first node the list actually changes, not just the XML document.
DOMXpath::evaluate() solves two of the problems at the same time. The result is not "live" so you can iterate the result with foreach() and remove nodes. Xpath expressions allow for conditions so you can filter and fetch specific nodes. Unfortunately Xpath 1.0 has now lower case methods, but you can call back into PHP for that.
function isTitleInArray($title) {
$titles = [
'battlefield 2'
];
return in_array(mb_strtolower($title, 'UTF-8'), $titles);
}
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$xpath->registerNamespace("php", "http://php.net/xpath");
$xpath->registerPHPFunctions('isTitleInArray');
$expression = '//product[php:function("isTitleInArray", normalize-space(title))]';
foreach ($xpath->evaluate($expression) as $product) {
$product->parentNode->removeChild($product);
}
echo $document->saveXml();
Output:
<?xml version="1.0"?>
<products>
<product>
<title>Battlefield 1</title>
<url>https://www.google.de/</url>
<price>0.80</price>
</product>
</products>
I have an XML like below
<entries>
<entry>
<title lang="en">Sample</title>
<entrydate>0</entrydate>
<contents>0</contents>
<entrynum>0</entrynum>
</entry>
<entry>
<title lang="fr">Sample</title>
<entrydate>1</entrydate>
<contents>1</contents>
<entrynum>1</entrynum>
</entry>
</entries>
Is there a way in PHP to delete the parent node (entry) based on the title lang attribute? I need to keep only the en ones, so in this case I would need to get the XML without the second entry node.
I tried looking around but couldn't find any solution...
You need to use DOMDocument class to parse string to XML document. Then use DOMXpath class to find target element in document and use DOMNode::removeChild() to remove selected element from document.
$doc = new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXpath($doc);
// select target entry tag
$entry = $xpath->query("//entry[title[#lang='fr']]")->item(0);
// remove selected element
$entry->parentNode->removeChild($entry);
$xml = $doc->savexml();
You can check result in demo
You could also read your file and generated new one with your modification
<?php
$entries = array('title' => "What's For Dinner",
'link' => 'http://menu.example.com/',
'description' => 'Choose what to eat tonight.');
print "<entries>\n";
foreach ($entries as $element => $content) {
print " <$element>";
print htmlentities($content);
print "</$element>\n";
}
print "</entries>";
?>
Use the method described in this answer, i.e.
<?php
$xml = simplexml_load_file('1.xml');
$del_items = [];
foreach ($xml->entry as $e) {
$attr = $e->title->attributes();
if ($attr && $attr['lang'] != 'en') {
$del_items []= $e;
}
}
foreach ($del_items as $e) {
$dom = dom_import_simplexml($e);
$dom->parentNode->removeChild($dom);
}
echo $xml->asXML();
Output
<?xml version="1.0" encoding="UTF-8"?>
<entries>
<entry>
<title lang="en">Sample</title>
<entrydate>0</entrydate>
<contents>0</contents>
<entrynum>0</entrynum>
</entry>
</entries>
The items cannot be removed within the first loop, because otherwise we may break the iteration chain. Instead, we collect the entry objects into $del_items array, then remove them from XML in separate loop.
I happen to be unfortunate enough to be working with an api that has images on the same XML tag level as the other tags and have the subscripts i.e 1,2,3,4 as part of the tag name of the image. Total images of each vehicle will vary in count.
<Vehicle>
<TITLE>Some car name i dont need</TITLE>
<DESCRIPTION>Some description i also dont need</DESCRIPTION>
<IMAGE_URL1>{imagelinkhere i want}</IMAGE_URL1>
<IMAGE_URL2>{imagelinkhere i want}</IMAGE_URL2>
<IMAGE_URL3>{imagelinkhere i want}</IMAGE_URL3>
<IMAGE_URL4>{imagelinkhere i want}</IMAGE_URL4>
</Vehicle>
I am using PHP's method simplexml_load_file(xml_url) to parse the entire xml into an object array.
My question: Is there a way to get these images using the same method which is also efficient and clean?
EDIT:
I have just refined the xml to show that there are other tags i dont need there and already handling.
$xml = '<Vehicle>
<DESCRIPTION/>
<IMAGE_URL1>{imagelinkhere}</IMAGE_URL1>
<IMAGE_URL2>{imagelinkhere}</IMAGE_URL2>
<IMAGE_URL3>{imagelinkhere}</IMAGE_URL3>
<IMAGE_URL4>{imagelinkhere}</IMAGE_URL4>
</Vehicle>';
$parsed = simplexml_load_string($xml);
If you know, that the image url tags will always contain the name IMAGE_URL, you can check them:
foreach ($parsed as $key => $image) {
if (strpos($key, 'IMAGE_URL') !== false) {
echo $image, '</br>';
}
}
You can fetch the nodes with Xpath.
$xml = <<<'XML'
<Vehicle>
<TITLE>Some car name i dont need</TITLE>
<DESCRIPTION>Some description i also dont need</DESCRIPTION>
<IMAGE_URL1>image1</IMAGE_URL1>
<IMAGE_URL2>image2</IMAGE_URL2>
<IMAGE_URL3>image3</IMAGE_URL3>
<IMAGE_URL4>image4</IMAGE_URL4>
</Vehicle>
XML;
$vehicle = new SimpleXMLElement($xml);
foreach ($vehicle->xpath('*[starts-with(local-name(), "IMAGE_URL")]') as $imageUrl) {
var_dump((string)$imageUrl);
}
Output:
string(6) "image1"
string(6) "image2"
string(6) "image3"
string(6) "image4"
* selects all element child nodes. [] is a condition. In this case a validation that the local name (tag name without any namespace prefix) starts with a specific string.
This looks not that much different in DOM. But you start at the document context.
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
foreach ($xpath->evaluate('/Vehicle/*[starts-with(local-name(), "IMAGE_URL")]') as $imageUrl) {
var_dump($imageUrl->textContent);
}
I'm trying to read an XML file with PHP but I'm only getting the first result and can't figure out why.
XML structure:
<main>
<data>
<record>
<column name="title">Some title here</column>
</record>
<record>
<column name="title">Some second title here</column>
</record>
</data>
</main>
Pretty basic. This is the code I'm using to getting the results:
foreach($XML->data->record as $product) {
$title = mysql_real_escape_string($product->title);
}
But this only gives an empty string so I guess I'm looking at the wrong path. The code below does work, but only gives me the first record and not the other records in the XML file.
foreach($XML->data as $product) {
$title = mysql_real_escape_string($product->record->title);
}
The answer is probably easy, but I can't figure it out. Hope someone is willing to help :)
title is the value of the name attribute of the <column> node.
You can't access it the way you do it.
To access the <column> node:
$xml = simplexml_load_string($x); // assume XML in $x
foreach ($xml->data->record as $product) {
echo $product->column;
}
Output:
Some title here
Some second title here
see it working: https://eval.in/506519
If there are many columns under each record...
<record>
<column name="title">Some title here</column>
<column name="car">BMW</column>
<column name="color">red</column>
</record>
... and you want to list all, you need a second loop:
foreach ($xml->data->record as $product) { // same as above
foreach ($product->column as $column) { // iterate all column
echo $column['name'] . ": " . $column . PHP_EOL;
}
}
Output:
title: Some title here
car: BMW
color: red
see it working: https://eval.in/506521
In case you want only <column> nodes with name = "title", check with if:
foreach ($xml->data->record as $product) { // same as above
foreach ($product->column as $column) { // iterate all column
if ($column['name'] == "title") echo $column . PHP_EOL;
}
}
see it working: https://eval.in/506523
And be aware that there is a different way to get all <column> with name = "title" with xpath:
$titles = $xml->xpath("//column[#name = 'title']");
foreach ($titles as $title)
echo $title . PHP_EOL;
see it in action: https://eval.in/506531
For an explanation of the xpath syntax, look at Using xPath to access values of simpleXML and zillion others.
and BTW: as said in comments, don't use mysql_...
Not tested but looks ok. Load the xml file / string into DOMDocument and get all the column nodes and iterate through them
$dom=new DOMDocument;
$dom->loadXML( $strxml );
/*
or
--
$dom->load( $xmlfile );
*/
$col=$dom->getElementsByTagName('column');
foreach( $col as $node ){
echo $node->tagName.' '.$node->getAttribute('title').' '.$node->nodeValue;
}
I am have two xml files.. I first get one and loop through it then I need to take an id from the first xml file and find it in the second one and echo out the results associated with that id. If I were to do this with SQL I would simply do this:
$query = (SELECT * FROM HotelSummary WHERE roomTypeCode = '$id') or die();
while($row=mysql_fetch_array($query)){
$name = $row['Name'];
}
echo $name;
How can I do this is in xml and php??
I recommend you to read the DOMDocument documentation.
It's quite heavy but also powerful (not always clear what happens, but the Internet shold always give you a solution)
You can simply walk through your first document, finding your Id and then find your DOMElement via an XPath.
<?php
$dom = new DOMDocument();
$dom->load('1.xml');
foreach ($dom->getElementsByTagName('article') as $node) {
// your conditions to find out the id
$id = $node->getAttribute('id');
}
$dom = new DOMDocument();
$dom->load('2.xml');
$xpath = new DOMXPath($dom);
$element = $xpath->query("//*[#id='".$id."']")->item(0);
// would echo "top_2" based on my example files
echo $element->getAttribute('name');
Based on following test files:
1.xml
<?xml version="1.0" encoding="UTF-8"?>
<articles>
<article id="foo_1">
<title>abc</title>
</article>
<article id="foo_2">
<title>def</title>
</article>
</articles>
2.xml
<?xml version="1.0" encoding="UTF-8"?>
<tests>
<test id="foo_1" name="top_1">
</test>
<test id="foo_2" name="top_2">
</test>
</tests>
Use SimpleXML to create an object representation of the file. You can then loop through the elements of the Simple XML object.
Depending on the format of the XML file:
Assuming it is:
<xml>
<roomTypeCode>
<stuff>stuff</stuff>
<name>Skunkman</name>
</roomTypeCode>
<roomTypeCode>
<stuff>other stuff</stuff>
<name>Someone Else</name>
</roomTypeCode>
</xml>
It would be something like this:
$xml = simplexml_load_file('xmlfile.xml');
for($i = 0; $i < count($xml->roomTypeCode); $i++)
{
if($xml->roomTypeCode[$i]->stuff == "stuff")
{
$name = $xml->roomTypeCode[$i]->name;
}
}
That connects to the XML file, finds how many roomTypeCode entries there are, searches for the value of "stuff" within and when it matches it correctly, you can access anything having to do with that XML entry.