This is my structure:
<Users>
<User>
<username>admin</username>
<server>10.xx.xx.xx</server>
<image>images/pic.png</image>
</User>
<User>
<username>bob</username>
<server>10.xx.xx.xx</server>
<image>images/pic2.png</image>
</User>
</Users>
Now I have this code that gets me a server value from node which has certain username that I am searching for.
$query = '//Users/User/username[. = "'.$_SESSION['SESS_FIRST_NAME'].'"]';
$entries = $xpath->query($query);
foreach ($entries as $entry) {
//Getting the "server" node value
$server=$entry->nextSibling->nextSibling->nodeValue;
//I wanted to have one more variable here which will save me the image string in the global php variable
$images=$entry->nextSibling->nextSibling->nextSibling->nodeValue; //this is giving me a server value instead of image value
}
Unless the schema of the document explicitly states otherwise I wouldn't rely on the order of elements but either query the elements via xpath by passing the user element as context node (see example) or iterate over the child elements of the user element and fetch what is needed (e.g. in an php array).
<?php
$doc = new DOMDOcument;
$doc->loadxml( getData() );
$xpath = new DOMXPath( $doc );
$query = '/Users/User[username= "'.'bob'.'"]';
foreach( $xpath->query($query) as $user ) {
$username = singleNodeValue($xpath->query('username', $user)); // ok, you already have this one....
$server = singleNodeValue($xpath->query('server', $user));
$image = singleNodeValue($xpath->query('image', $user));
printf("%s, %s, %s\r\n", $username, $server, $image);
}
function singleNodeValue($nodeset) {
// add tests here....
return $nodeset->item(0)->nodeValue;
}
function getData() {
return <<< eox
<Users>
<User>
<username>admin</username>
<server>10.xx.xx.xx</server>
<image>images/pic.png</image>
</User>
<User>
<username>bob</username>
<server>10.xx.xx.xx</server>
<image>images/pic2.png</image>
</User>
</Users>
eox;
}
prints
bob, 10.xx.xx.xx, images/pic2.png
Related
Say I have an XML file like this.
<users>
<user>
<username>desbest</username>
<email>desbest#example.com</email>
<password>testpass1</password>
</user>
<user>
<username>demo</username>
<email>nobody#example.com</email>
<password>demo</password>
</user>
</users>
How do I use XPath to select the desbest user, then use php to edit the password under the desbest user, and save it as a file?
I have searched Google and Stack Overflow and I haven't found the answer.
Here is my current code.
// print_r($xml);
$newpass = "mynewpass";
// $newpass = password_hash($newpass, PASSWORD_DEFAULT);
$nodes = $xml->xpath(sprintf(" //users/user[(username = \"$myusername\")] "));
// print_r($nodes);
// $nodes[0]->password = "$newpass";
$domnode = dom_import_simplexml($nodes[0]);
$nodepath = $domnode->getNodePath();
// $xml = $xml->$nodepath->password = $newpass;
// $danodepath = $nodes[0]->getNodePath();
// print_r($nodes);
// $xml->users->user["(username = \"$myusername\")"] = "$newpass";
print_r($xml);
echo "<hr>";
print_r($nodepath);
You can achieve this just with SimpleXML - you don't need to involve DOMDocument at all.
The xpath method returns the <user> element you're looking for. You can then modify it simply by updating the password property (or add new ones, or attributes, etc). This updates the underlying SimpleXMLElement object, which you can then write back to the file as a string using asXML.
$filename = 'file.xml';
$sxml = simplexml_load_file($filename);
$username = "desbest";
$user = $sxml->xpath("./user[./username = '{$username}']")[0];
$user->password = 'testpassCHANGED';
file_put_contents($filename, $sxml->asXML());
See https://eval.in/923654 for an example
Example with DOMDocument:
$source = '<users>
<user>
<username>desbest</username>
<email>desbest#example.com</email>
<password>testpass1</password>
</user>
<user>
<username>demo</username>
<email>nobody#example.com</email>
<password>demo</password>
</user>
</users>';
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($source);
$xpath = new DOMXPath($dom);
$myusername = htmlspecialchars("desbest", ENT_XML1 | ENT_QUOTES, 'UTF-8');
$expression = sprintf('/users/user[(username="%s")]', $myusername);
$users = $xpath->query($expression);
if($users->length) {
$user = $users->item(0);
$password = $xpath->query('password', $user)->item(0);
$password->nodeValue = 'new password';
}
file_put_contents('filename.xml', $dom->saveXML());
I am creating PHP system for edit XML files to translation of game.
I am using DOM e.g for file-comparision for translators (with update XML file).
I have old and new XML (in advance: I can not change XML structure) with new strings and/or new IDs.
For future echo node value to comparision by the same ID order, I have following code:
<?php
$xml2 = new DOMDocument('1.0', 'utf-16');
$xml2->formatOutput = true;
$xml2->preserveWhiteSpace = false;
$xml2->load(substr($file, 0, -4).'-pl.xml');
$xml = new DOMDocument('1.0', 'utf-16');
$xml->formatOutput = true;
$xml->preserveWhiteSpace = false;
$xml->load($file);
for ($i = 0; $i < $xml->getElementsByTagName('string')->length; $i++) {
if ($xml2->getElementsByTagName('string')->item($i)) {
$element_pl = $xml2->getElementsByTagName('string')->item($i);
$body_pl = $element_pl->getElementsByTagName('body')->item(0);
$id_pl = $element_pl->getElementsByTagName('id')->item(0);
} else $id_pl->nodeValue = "";
$element = $xml->getElementsByTagName('string')->item($i);
$id = $element->getElementsByTagName('id')->item(0);
$body = $element->getElementsByTagName('body')->item(0);
if ($id_pl->nodeValue == $id->nodeValue) {
$element->appendChild( $xml->createElement('body-pl', $body_pl->nodeValue) );
}
}
$xml = simplexml_import_dom($xml);
?>
Above code change:
<?xml version="1.0" encoding="utf-16"?>
<strings>
<string>
<id>1</id>
<name>ABC</name>
<body>English text</body>
</string>
</strings>
to (by adding text from *-pl.xml file):
<?xml version="1.0" encoding="utf-16"?>
<strings>
<string>
<id>1</id>
<name>ABC</name>
<body>English text</body>
<body-pl>Polish text</body-pl>
</string>
</strings>
But I need find "body" value in *-pl.xml by "name" value.
"For" loop:
get "ABC" from "name" tag [*.xml] ->
find "ABC" in "name" tag [*-pl.xml] ->
get body node from that "string" [*-pl.xml]
I can do that by strpos(), but my (the smallest) file have 25346 lines..
Is there something to do e.g. "has children ("name", "ABC") -> parent" ?
Then I can get "body" value of this string.
Thank you in advance for suggestions or link to similar, resolved ask,
Greetings
You need XPath expressions:
//name[text()='ABC']/../body
or
//name[text()='ABC']/following-sibling::body
Check the PHP manual for DOMXPath class and its query method. In a nutshell, you'd use it like this:
$xpath = new DOMXPath($dom_document);
// find all `body` nodes that have a `name` sibling
// with an `ABC` value in the entire document
$nodes = $xpath->query("//name[text()='ABC']/../body");
foreach($nodes as $node) {
echo $node->textContent , "\n\n";
}
I have a function to add a valueless element and a bunch of child elements with values to an xml file, so I wrote it to take two arguments $tag(String the parent node) and $hash(Array $elm=>$elm_value)
function addUser($tag, $hash)
{
$dom = new DOMDocuemnt();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load('file');
$parent = $dom->createElement($tag);
foreach($hash as $elm => $value){
$n = $dom->createElement($elm, $value);
$parent->appendChild($n);
}
$dom->appendChild($parent);
$dom->save('file');
return $dom->saveXML();
}
Only problem is $dom->appendChild($parent) appends everything after the root element's closing tag, mucking up my xml file. So I tried $dom->insertBefore($parent) with the same result. So instead I tried $xpath = new DOMXPath($dom); $root = $xpath->query('/')->item(0); $root->appendChild($parent);. Same result. Then I tried selecting the root element with $dom->getElementsByTagName(name of root)->item(0); And was surprised when that actually worked! But what if I don't know the tag name? Is there another way to select the root element so that calling appendChild or inserBefore will add the element before the root closing tag instead of after it?
This seems to work:
Initial XML file -
<!-- test.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
<node Id="1">
<Clicks>click1</Clicks>
</node>
</root>
PHP -
<?php
function addUser($tag, $hash) {
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load('test.xml');
$parent = $dom->createElement($tag);
$dom->documentElement->appendChild($parent);
foreach($hash as $elm => $value){
$n = $dom->createElement($elm);
$n->appendChild( $dom->createTextNode( $value ) );
$parent->appendChild($n);
}
$dom->save('test.xml');
}
$arr = array( 'name' => 'pushpesh', 'age' => 30, 'profession' => 'SO bugger' );
addUser('user', $arr);
XML file is now -
<!-- test.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
<node Id="1">
<Clicks>click1</Clicks>
</node>
<user>
<name>pushpesh</name>
<age>30</age>
<profession>SO bugger</profession>
</user>
</root>
Hope this helps.
I have come across couple of similar answers for my question in this form, but could not solve my exact problem. Therefore I am posting this here:
I have an xml file as shown below:
<?xml version="1.0" encoding="ISO-8859-1"?>
<document>
<user>
<user_id>0121</user_id>
<name>Tim</name>
<file>0121.file</file>
</user>
<user>
<user_id>0178</user_id>
<name>Henry</name>
<file>0178.file</file>
</user>
<user>
<user_id>0786</user_id>
<name>Martin</name>
<file>0786.file</file>
</user>
<user>
<user_id>1239</user_id>
<name>Jan</name>
<file>1239.file</file>
</user>
</document>
I ask the user to input his user_id and post this user_id and perform a check on the entire xml file to check whether the entered user_id exists in the xml file or not. If exists then I go further if not echo a error message.
Can anybody pull me out of this?
Thanks
EDIT answer
Forget my previous answer :P
$user_id = '1239';
$xml = new SimpleXMLElement($xml);
$found = false;
foreach($xml->user AS $user) {
if ($user->user_id == $user_id) {
$found = true;
break;
}
}
if (!found) echo 'error';
You could use DOMDocument with DOMXpath query, an example :-
$doc = new DOMDocument;
$doc->load(...);
$xpath = new DOMXPath($doc);
$query = '//user/user_id[.="0121"]';
$entries = $xpath->query($query);
if ( $entries->length )
{
// found
}
My question is best phrase as:
Remove a child with a specific attribute, in SimpleXML for PHP
except I'm not using simpleXML.
I'm new to XML for PHP so I may not be doing the best way
I have a xml created using the $dom->save($xml) for each individual user. (not placing all in one xml due to undisclosed reasons)
It gives me that xml declaration <?xml version="1.0"?> (no idea how to make it to others, but that's not the point, hopefully)
<?xml version="1.0"?>
<details>
<person>name</person>
<data1>some data</data1>
<data2>some data</data2>
<data3>some data</data3>
<category id="0">
<categoryName>Cat 1</categoryName>
<categorydata1>some data</categorydata1>
</category>
<category id="1">
<categoryName>Cat 2</categoryName>
<categorydata1>some data</categorydata1>
<categorydata2>some data</categorydata2>
<categorydata3>some data</categorydata3>
<categorydata4>some data</categorydata4>
</category>
</details>
And I want to remove a category that has a specific attribute named id with the DOM class in php when i run a function activated from using a remove button.
the following is the debug of the function im trying to get to work. Can i know what I'm doing wrong?
function CatRemove($myXML){
$xmlDoc = new DOMDocument();
$xmlDoc->load( $myXML );
$categoryArray = array();
$main = $xmlDoc->getElementsByTagName( "details" )->item(0);
$mainElement = $xmlDoc->getElementsByTagName( "details" );
foreach($mainElement as $details){
$currentCategory = $details->getElementsByTagName( "category" );
foreach($currentCategory as $category){
$categoryID = $category->getAttribute('id');
array_push($categoryArray, $categoryID);
if($categoryID == $_POST['categorytoremoveValue']) {
return $categoryArray;
}
}
}
$xmlDoc->save( $myXML );
}
Well the above prints me an array of [0]->0 all the time when i slot the return outside the if.
is there a better way? I've tried using getElementbyId as well but I've no idea how to work that.
I would prefer not to use an attribute though if that would make things easier.
Ok, let’s try this complete example of use:
function CatRemove($myXML, $id) {
$xmlDoc = new DOMDocument();
$xmlDoc->load($myXML);
$xpath = new DOMXpath($xmlDoc);
$nodeList = $xpath->query('//category[#id="'.(int)$id.'"]');
if ($nodeList->length) {
$node = $nodeList->item(0);
$node->parentNode->removeChild($node);
}
$xmlDoc->save($myXML);
}
// test data
$xml = <<<XML
<?xml version="1.0"?>
<details>
<person>name</person>
<data1>some data</data1>
<data2>some data</data2>
<data3>some data</data3>
<category id="0">
<categoryName>Cat 1</categoryName>
<categorydata1>some data</categorydata1>
</category>
<category id="1">
<categoryName>Cat 2</categoryName>
<categorydata1>some data</categorydata1>
<categorydata2>some data</categorydata2>
<categorydata3>some data</categorydata3>
<categorydata4>some data</categorydata4>
</category>
</details>
XML;
// write test data into file
file_put_contents('untitled.xml', $xml);
// remove category node with the id=1
CatRemove('untitled.xml', 1);
// dump file content
echo '<pre>', htmlspecialchars(file_get_contents('untitled.xml')), '</pre>';
So you want to remove the category node with a specific id?
$node = $xmlDoc->getElementById("12345");
if ($node) {
$node->parentNode->removeChild($node);
}
You could also use XPath to get the node, for example:
$xpath = new DOMXpath($xmlDoc);
$nodeList = $xpath->query('//category[#id="12345"]');
if ($nodeList->length) {
$node = $nodeList->item(0);
$node->parentNode->removeChild($node);
}
I haven’t tested it but it should work.
Can you try with this modified version:
function CatRemove($myXML, $id){
$doc = new DOMDocument();
$doc->loadXML($myXML);
$xpath = new DOMXpath($doc);
$nodeList = $xpath->query("//category[#id='$id']");
foreach ($nodeList as $element) {
$element->parentNode->removeChild($element);
}
echo htmlentities($doc->saveXML());
}
It's working for me. Just adapt it to your needs. It's not intended to use as-is, but just a proof of concept.
You also have to remove the xml declaration from the string.
the above funciton modified to remove an email from a mailing list
function CatRemove($myXML, $id) {
$xmlDoc = new DOMDocument();
$xmlDoc->load($myXML);
$xpath = new DOMXpath($xmlDoc);
$nodeList = $xpath->query('//subscriber[#email="'.$id.'"]');
if ($nodeList->length) {
$node = $nodeList->item(0);
$node->parentNode->removeChild($node);
}
$xmlDoc->save($myXML);
}
$xml = 'list.xml';
$to = $_POST['email'];//user already submitted they email using a form
CatRemove($xml,$to);