I have problems to deal with XML in PHP. What i want is to remove a not needed element and replace it with a other one.
Let's say the XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<software>
<info>
<version>1.2</version>
<lasterror>1386680712</lasterror>
</info>
<decryption>
<funcgroup siglength="86">
<func>
<name>Mk</name>
<args>$a</args>
<code>XXXX</code>
</func>
<func>
<name>Nk</name>
<args>$a,$b</args>
<code>XXXX</code>
</func>
</funcgroup>
</decryption>
</software>
PHP Code:
$domtree = new DOMDocument('1.0', 'UTF-8');
$domtree->loadXML(file_get_contents('test.xml'));
$thedocument = $domtree->documentElement;
$list = $thedocument->getElementsByTagName('funcgroup');
foreach ($list as $domElement) {
$sig_length = $domElement->getAttribute('siglength');
if($sig_length == $signature_length) {
$domElement->parentNode->removeChild($domElement);
break;
}
}
$some_stuff = $domtree->getElementsByTagName('software');
$some_stuff = $domtree->getElementsByTagName('decryption');
$funcgroup = $domtree->appendChild($domtree->createElement('funcgroup'));
$funcgroup->setAttribute('siglength', $signature_length);
$func = $funcgroup->appendChild($domtree->createElement('func'));
$func->appendChild($domtree->createElement('name', $outer_element[0]));
$func->appendChild($domtree->createElement('args', $outer_element[1]));
$code = $func->appendChild($domtree->createElement('code'));
$code->appendChild($domtree->createTextNode($outer_element[2]));
Note: I removed some stuff otherwise it would get too complicated i guess. The above code shows what i do, but without some other loops and variables which are not needed in that question. Every variable (and array) is defined. So don't worry about that.
What i want is to remove the whole <funcgroup siglength="86"> in order to replace it with a different one.
The script works fine, but there is one problem in the output XML. It looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<software>
<info>
<version>6.3</version>
<lasterror>1386680712</lasterror>
</info>
<decryption/>
</software>
<funcgroup siglength="86">
<func>
<name>Nk</name>
<args>$a</args>
<code>YYYYY</code>
</func>
<func>
<name>Ok</name>
<args>$a,$b</args>
<code>YYYY</code>
</func>
</funcgroup>
As you can see, the closing software and decryption tags are on the wrong place now.
How can i fix that? I spent hours but can't find a working solution.
The problem is caused by the removeChild() since it works fine if i do not remove something.
You are adding your new child node to the document itself (instead of the decryption node), which is not what you want
$domtree->appendChild
Instead you should:
$decryption = $domtree->getElementsByTagName('decryption')->item(0);
$funcgroup = $decryption->appendChild($domtree->createElement('funcgroup'));
Edit:
You can edit the text value of the lasterror node by doin:
$domtree->getElementsByTagName('lasterror')->item(0)->firstChild->nodeValue = "New value";
Consult the documentation of the DOMNodeList and DOMNode class to see what else you can do with it.
Related
While adding child, this error is thrown :
Cannot add child. Parent is not a permanent member of the XML tree.
I cannot resolve this.
This is my code :
if($visited=='FIRST')
{
$xml=new SimpleXMLElement("<xml/>");
$topology=$xml->addChild("Topology_Configuration");
$flavor=$topology->addChild("Flavor");
$networks=$topology->addChild("Networks");
$vms=$topology->addChild("VMs");
$vnfs=$topology->addChild("VNFs");
$xml->asXML('saddening.xml');
}
else
{
$xml= simplexml_load_file('saddening.xml');
$Topology_Configuration = new SimpleXMLElement($xml->asXML());
$vmcount=$_POST['arguments']['vmcount'];
$flavor=$Topology_Configuration->Flavor;
$flavor_name=$flavor->addChild($_POST['arguments']['flavorName']);
$Topology_Configuration->asXML('saddening.xml');
}
When it is executed for the first time, the file is created(in if part). Otherwise else part is executed. It cannot add the child and is throwing the error in line :
$flavor_name=$flavor->addChild($_POST['arguments']['flavorName']);. Please help!!
The XML from your first run results in an XML like this:
<?xml version="1.0"?>
<xml>
<Topology_Configuration>
<Flavor/>
<Networks/>
<VMs/><VNFs/>
</Topology_Configuration>
</xml>
So if you strip down the problem you can reproduce it with:
$Topology_Configuration = simplexml_load_file($fileName);
$flavor=$Topology_Configuration->Flavor;
$flavor->addChild('abc');
echo $Topology_Configuration->asXml();
Results in:
Warning: SimpleXMLElement::addChild(): Cannot add child.
Parent is not a permanent member of the XML tree in
The message is a little wrong, you just try to add the element to an element that does not exists. $Topology_Configuration contains the xml element node, not the Topology_Configuration.
Here are two possible solutions:
Change the XML structure
Create the XML with the Topology_Configuration as the root element.
$topology =new SimpleXMLElement("<Topology_Configuration/>");
Change the access to the Flavor
$xml = simplexml_load_file($fileName);
$flavor=$xml->Topology_Configuration->Flavor;
$flavor->addChild('abc');
At the first time, you can use example to add child nodes
$new_xml = new SimpleXMLElement("<root></root>");
$new_xml->addAttribute('newAttr', 'value');
$newsIntro = $new_xml->addChild('content');
$newsIntro->addAttribute('type', 'value');
Header('Content-type: text/xml');
echo $new_xml->asXML();
and result
<?xml version="1.0"?>
<news newAttr="value">
<content type="value"/>
</news
please help.
I have XML file which is not filled correctly (some values are missed)
Bad example of XML
<?xml version="1.0"?>
<csv_data>
<row>
<brand>Maxxis</brand>
<code>1126668/02</code>
<articul_brand></articul_brand>
<tiporazmer></tiporazmer>
<name>(26/9.00 R12) 6PR TL</name>
<model>M-966 Mudzilla</model>
<sklad_msk></sklad_msk>
<sklad_nahabino></sklad_nahabino>
<sklad_chelny>8</sklad_chelny>
<price_big_opt>6,786р.</price_big_opt>
<price_small_opt>7,100р.</price_small_opt>
<price_retail>8,170р.</price_retail>
</row>
</csv_data>
Good example of XML
<?xml version="1.0"?>
<csv_data>
<row>
<brand>Metzeler</brand>
<code>ЦО04257/21</code>
<articul_brand>2491500</articul_brand>
<tiporazmer>110/80R18</tiporazmer>
<name>(110/80 R18) 58W TL (M) Front</name>
<model>Roadtec Z8</model>
<sklad_msk></sklad_msk>
<sklad_nahabino>2</sklad_nahabino>
<sklad_chelny></sklad_chelny>
<price_big_opt>7,720р.</price_big_opt>
<price_small_opt>8,106р.</price_small_opt>
<price_retail>9,270р.</price_retail>
</row>
</csv_data>
The point is that values in <tiporazmer></tiporazmer> are missed in bad example but they can be found in <name>(26/9.00 R12) 6PR TL</name>
The question is how to update fields <tiporazmer></tiporazmer> using name filed? Copy that without brackets? How?
What I did, but i've got an error Fatal error: Call to a member function getElementsByTagName() on a non-object in
Code:
<?php
$xml = new DOMDocument('1.0', 'utf-8');
$xml->formatOutput = true;
$xml->preserveWhiteSpace = false;
$xml->load('test2.xml');
$new_tiporazmer="26/9.00 R12";
//Get item Element
$element = $xml->getElementsByTagName('rows')->item(0);
//Load child elements
$tiporazmer = $element->getElementsByTagName('tiporazmer')->item(0);
//Replace old elements with new
$element->replaceChild($tiporazmer, $new_tiporazmer);
?>
The error is caused because you're calling getElementsByTagName('rows'), but the name of your XML node is row, so there shouldn't be an s in rows
Then in order to Replace old elements with new, you will need to use nodeValue instead of replaceChild unless you already have a constructed DOMDocument object ready.
But a better way to achieve what you want is to simply find the element by name directly, replace its text, and then save as a new file. I saved to the "/tmp" folder because permissions are usually writeable there on unix systems, but you can write anywhere your php interpreter is allowed.
<?php
$xml = new DOMDocument('1.0', 'utf-8');
$xml->formatOutput = true;
$xml->preserveWhiteSpace = false;
$xml->load('test2.xml');
$new_tiporazmer="26/9.00 R12";
//Get item Element and Replace old elements with new
$xml->getElementsByTagName('tiporazmer')->item(0)->nodeValue = $new_tiporazmer;
//Save as new file
$xml->save("/tmp/good.xml");
This results in good.xml:
<?xml version="1.0"?>
<csv_data>
<row>
<brand>Maxxis</brand>
<code>1126668/02</code>
<articul_brand/>
<tiporazmer>26/9.00 R12</tiporazmer>
<name>(26/9.00 R12) 6PR TL</name>
<model>M-966 Mudzilla</model>
<sklad_msk/>
<sklad_nahabino/>
<sklad_chelny>8</sklad_chelny>
<price_big_opt>6,786р.</price_big_opt>
<price_small_opt>7,100р.</price_small_opt>
<price_retail>8,170р.</price_retail>
</row>
</csv_data>
I'm creating XML response for the one of our clients with the namespace URLs in that using PHP. I'm expecting the output as follows,
<?xml version="1.0" encoding="UTF-8"?>
<ns3:userResponse xmlns:ns3="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://www.w3.org/2001/XMLSchema">
<Content>
<field1>fieldvalue1</field1>
</Content>
</ns3:userResponse>
But by using the following code,
<?php
// create a new XML document
$doc = new DomDocument('1.0', 'UTF-8');
// create root node
$root = $doc->createElementNS('http://www.w3.org/2001/XMLSchema-instance', 'ns3:userResponse');
$root = $doc->appendChild($root);
$root->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'ns1:schemaLocation','');
$root->setAttributeNS('http://www.w3.org/2001/XMLSchema', 'ns2:schemaLocation','');
// add node for each row
$occ = $doc->createElement('Content');
$occ = $root->appendChild($occ);
$child = $doc->createElement("field1");
$child = $occ->appendChild($child);
$value = $doc->createTextNode('fieldvalue1');
$value = $child->appendChild($value);
// get completed xml document
$xml_string = $doc->saveXML();
echo $xml_string;
DEMO:
The demo is here, http://codepad.org/11W9dLU9
Here the problem is, the third attribute is mandatory attribute for the setAttributeNS PHP function. So, i'm getting the output as,
<?xml version="1.0" encoding="UTF-8"?>
<ns3:userResponse xmlns:ns3="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://www.w3.org/2001/XMLSchema" ns3:schemaLocation="" ns2:schemaLocation="">
<Content>
<field1>fieldvalue1</field1>
</Content>
</ns3:userResponse>
So, is there anyway to remove that ns3:schemaLocation and ns2:schemaLocation which is coming with empty value? I googled a lot but couldn't able to find any useful answers.
Any idea on this would be so great. Please help.
You create this attributes:
$root->setAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'ns1:schemaLocation','');
$root->setAttributeNS('http://www.w3.org/2001/XMLSchema', 'ns2:schemaLocation','');
remove this lines and they will be removed.
If you want to add some xmlns without using it in code is:
$attr_ns = $doc->createAttributeNS( 'http://www.w3.org/2001/XMLSchema', 'ns2:attr' );
Read this comment: http://php.net/manual/pl/domdocument.createattributens.php#98210
The API integration documents specify that that all node name are case sensitive. I'm using PHP SimpleXMLElement and I don't see a way to force upper case node names. Has someone run across a way to force node names to upper case?
$xmlstr = '<Request>'.
'</Request>';
$sxe = new SimpleXMLElement($xmlstr);
$authentication = $sxe->addChild('Authentication');
$authentication->addChild('Version', '2.0');
$processid = $sxe->addChild('Process ID=importSale');
$importsale = $processid->addChild('importSale');
$importsale->addChild('SCRIPTCODE', '<![CDATA[SCRIPT001]]>');
$importsale->addChild('PRODID','<!CDATA[DNTMAN]]>');
echo $sxe->asXML();
When viewing this in “View Source” both “SCRIPTCODE” and “PRODID” are in lower case. How do I force these to upper case?
In theory the code you provided does the job already! all children added to a simpleXMLElement will preserve its original case by default!
$sxe = new SimpleXMLElement('<Request></Request>');
$authentication = $sxe->addChild('Authentication');
$authentication->addChild('Version', '2.0');
$processid = $sxe->addChild('Process ID=importSale');
$importsale = $processid->addChild('importSale');
$importsale->addChild('SCRIPTCODE', '<![CDATA[SCRIPT001]]>');
$importsale->addChild('PRODID','<!CDATA[DNTMAN]]>');
echo $sxe->asXML();
What you get executing the code is something like that :
<Request>
<Authentication>
<Version>2.0</Version>
</Authentication>
<Process>
<Process ID=importSale>
<SCRIPTCODE><![CDATA[SCRIPT001]]></SCRIPTCODE>
<PRODID><!CDATA[DNTMAN]]></PRODID>
</importSale>
</Process>
</Request>
SCRIPTCODE and PRODID both remained uppercase!
Notice : this is not the propper way to add cdata to your node values... this will lead to html-entity conversion like <!CDATA[]]>
Instead of viewing the source code, try echoing the output to the screen with:
echo htmlentities($sxe->asXML());
I have code which add item like below to xml file:
<newWord>
<Heb>rer</Heb>
<Eng>twew</Eng>
</newWord>
the problem is when I add element it will be not in the right place, it is out from xml tag like below.
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<newWord>
<Heb>hebword</Heb>
<Eng>banna</Eng>
</newWord>
</xml>
<newWord>
<Heb>rer</Heb>
<Eng>twew</Eng>
</newWord>
How can I add the new element to correct place? thx
my code:
<?php
$wordH=$_GET['varHeb'];
$wordE=$_GET['varEng'];
$domtree='';
if(!$domtree)
{
$domtree = new DOMDocument('1.0', 'UTF-8');
$domtree->formatOutput = true;
$domtree->load('Dictionary_user.xml');
}
$Dictionary_user = $domtree->documentElement;
$currentitem = $domtree->createElement("newWord");
$currentitem = $domtree->appendChild($currentitem);
$currentitem->appendChild($domtree->createElement('Heb', $wordH));
$currentitem->appendChild($domtree->createElement('Eng',$wordE));
$Dictionary_user->childNodes->item(0)->parentNode->insertBefore($currentitem,$Dictionary_user->childNodes->item(0));
header("Content-type: text/xml");
$domtree->save("Dictionary_user.xml");
/* get the xml printed */
echo $domtree->saveXML();
?>
Perhaps you want something like:
<?php
if(!empty($_GET['varHeb']) && !empty($_GET['varEng'])){
//Dont forget utf-8 if using Hebrew letters
header('Content-type: text/xml; charset=utf-8');
//Load It
$xml = simplexml_load_file('./Dictionary_user.xml');
//Add a new node & add the children
$node = $xml->addChild('newWord');
$node->addChild('Heb', trim($_GET['varHeb']));
$node->addChild('Eng', trim($_GET['varEng']));
//DOMDocument to format code output
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($xml->asXML());
echo $dom->saveXML();
}else{
//params not set for $_GET
}
/**
* Result
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<newWord>
<Heb>hebword</Heb>
<Eng>banna</Eng>
</newWord>
<newWord>
<Heb>אנחנו אוהבים גלישת מחסנית</Heb>
<Eng>We Love Stack overflow</Eng>
</newWord>
</xml>
*/
The issue is you doing:
$currentitem = $domtree->createElement("newWord");
$currentitem = $domtree->appendChild($currentitem);
Change it to
$currentitem = $domtree->createElement("newWord");
$currentitem = $domtree->documentElement->appendChild($currentitem);
This will them append to the root node.
There is also few other things to be improved in your code:
$wordH=$_GET['varHeb'];
$wordE=$_GET['varEng'];
Make sure you sanitize/filter this content. DOM is quite good at making sure no malicious content will be inserted, but anything coming from the outside world should be sanity checked.
$domtree='';
if(!$domtree)
{
This is rather pointless to do. You made $domtree an empty string, so the if block will always get triggered. Remove that part and simply load the XML.
$domtree = new DOMDocument('1.0', 'UTF-8');
$domtree->formatOutput = true;
$domtree->load('Dictionary_user.xml');
}
When you load a string or file with DOM, it will discard any arguments you put into the constructor and use the arguments found in the xml prolog of the string or file instead. So you do not need to supply them when newing the DOMDocument.
$Dictionary_user = $domtree->documentElement;
This is the root node (<xml>) you want to append to.
$currentitem = $domtree->createElement("newWord");
$currentitem = $domtree->appendChild($currentitem);
This is where the error is happening. You create a new element and then append it to the $domtree, which is the document as a whole. In an XPath query, $domtree is / while you want to append to /xml, e.g. the root element.
$currentitem->appendChild($domtree->createElement('Heb', $wordH));
$currentitem->appendChild($domtree->createElement('Eng',$wordE));
This works as expected. You are adding to <newWord>
$Dictionary_user
->childNodes
->item(0)
->parentNode
->insertBefore($currentitem, $Dictionary_user->childNodes->item(0));
You are traversing from the root element (<xml>) to the first child element and then back up to the parent, which is the root element again obviously. So the entire traversal is not necessary. In fact, unless you want to move the appended <newWord> above the first child of the root element, the entire block is not necessary at all.