How to append XML data without overwriting? - php

I'm in the process of writing an XML file:
<?php
$xml2 = "currenttest";
$xml = new DOMDocument("1.0");
$root = $xml->createElement ('tv');
$xml->appendChild($root);
$root->appendChild($xml->createTextNode("\n"));
$root->appendChild($xml->createTextNode($xml2));
$root->appendChild($xml->createTextNode("\n"));
$xml->save('epg.xml');
XML:
<?xml version="1.0"?>
<tv>
test
</tv>
If i change the text and again runs the code, the old content is deleted.
And I want the old text to stay.
Let's say this:
<?xml version="1.0"?>
<tv>
currenttest...
newtest...
</tv>
My previous way was to write the XML with:
file_put_contents($file, $xml2, FILE_APPEND | LOCK_EX);
FILE_APPEND | LOCK_EX, its helped me that the previous text would not be erased
I found a solution in another post:
$doc->loadXML(file_get_contents('epg.xml'));
foreach($doc->getElementsByTagName('***') as $node)
{
}
But how can it fit into my code?

You have nothing particular to do, just to reload your xml string and to append a new text node to your root element:
// your previous code (I only changed the variable names and added a default encoding)
$text = "currenttest";
$dom = new DOMDocument("1.0", "UTF-8");
$root = $dom->createElement('tv');
$dom->appendChild($root);
$root->appendChild($dom->createTextNode("\n"));
$root->appendChild($dom->createTextNode($text));
$root->appendChild($dom->createTextNode("\n"));
$xml = $dom->saveXML();
// let's add a new element
$newtext = 'newtext';
$dom = new DOMDocument;
$dom->loadXML($xml);
$root = $dom->documentElement; // conveniant way to target the root element
// but you can also write:
//$root = $dom->getElementsByTagName('tv')->item(0);
$root->appendChild($dom->createTextNode($newtext));
$newxml = $dom->saveXML();
echo $newxml;
demo
About $doc->loadXML(file_get_contents('epg.xml'));, note that you don't need to use file_get_contents since DOMDocument has already two methods:
DOMDocument::loadXML that loads the xml content from a string.
DOMDocument::load that loads the xml content directly from a file.
In addition to DOMNode::appendChild that adds a node to an element after all the children nodes of this element, you have also DOMNode::insertBefore to add a node to an element before the child node of your choice.

I tryed the code on top, 'cause i was overwriting my data, but when I coded in my application, it didn't worked cause I was trying to add the new node data in the loaded xml, you have to create a root to add data inside.
$xml = new DOMDocument("1.0", "UTF-8");
//an tag root must be first thing to add
$root = $xml->createElement('root');
$xml->appendChild($root);
Then, just add the data when you need
$xml = new DOMDocument("1.0", "UTF-8");
$xml->load($sFilepath);
$root = $xml->getElementsByTagName('root')->item(0);
your structure must looks like this:
<xml version="1.0" encoding="UTF-8">
<root>
</root>
The answer on top is totally correct. This answer is only to help if somebody is having trouble to understand.

Related

Adding or Updating XML Node PHP? [duplicate]

I just wanted to ask how I can insert a new node in an XML using PHP. my XML file (questions.xml) is given below
<?xml version="1.0" encoding="UTF-8"?>
<Quiz>
<topic text="Preparation for Exam">
<subtopic text="Science" />
<subtopic text="Maths" />
<subtopic text="english" />
</topic>
</Quiz>
I want to add a new "subtopic" with the "text" attribute, that is "geography". How can I do this using PHP? Thanks in advance though.
well my code is
<?php
$xmldoc = new DOMDocument();
$xmldoc->load('questions.xml');
$root = $xmldoc->firstChild;
$newElement = $xmldoc->createElement('subtopic');
$root->appendChild($newElement);
// $newText = $xmldoc->createTextNode('geology');
// $newElement->appendChild($newText);
$xmldoc->save('questions.xml');
?>
I'd use SimpleXML for this. It would look somehow like this:
// Open and parse the XML file
$xml = simplexml_load_file("questions.xml");
// Create a child in the first topic node
$child = $xml->topic[0]->addChild("subtopic");
// Add the text attribute
$child->addAttribute("text", "geography");
You can either display the new XML code with echo or store it in a file.
// Display the new XML code
echo $xml->asXML();
// Store new XML code in questions.xml
$xml->asXML("questions.xml");
The best and safe way is to load your XML document into a PHP DOMDocument object, and then go to your desired node, add a child, and finally save the new version of the XML into a file.
Take a look at the documentation : DOMDocument
Example of code:
// open and load a XML file
$dom = new DomDocument();
$dom->load('your_file.xml');
// Apply some modification
$specificNode = $dom->getElementsByTagName('node_to_catch');
$newSubTopic = $xmldoc->createElement('subtopic');
$newSubTopicText = $xmldoc->createTextNode('geography');
$newSubTopic->appendChild($newSubTopicText);
$specificNode->appendChild($newSubTopic);
// Save the new version of the file
$dom->save('your_file_v2.xml');
You can use PHP's Simple XML. You have to read the file content, add the node with Simple XML and write the content back.

php export xml CDATA escaped

I am trying to export xml with CDATA tags. I use the following code:
$xml_product = $xml_products->addChild('product');
$xml_product->addChild('mychild', htmlentities("<![CDATA[" . $mytext . "]]>"));
The problem is that I get CDATA tags < and > escaped with < and > like following:
<mychild><![CDATA[My some long long long text]]></mychild>
but I need:
<mychild><![CDATA[My some long long long text]]></mychild>
If I use htmlentities() I get lots of errors like tag raquo is not defined etc... though there are no any such tags in my text. Probably htmlentities() tries to parse my text inside CDATA and convert it, but I dont want it either.
Any ideas how to fix that? Thank you.
UPD_1 My function which saves xml to file:
public static function saveFormattedXmlFile($simpleXMLElement, $output_file) {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML(urldecode($simpleXMLElement->asXML()));
$dom->save($output_file);
}
A short example of how to add a CData section, note the way it skips into using DOMDocument to add the CData section in. The code builds up a <product> element, $xml_product has a new element <mychild> created in it. This newNode is then imported into a DOMElement using dom_import_simplexml. It then uses the DOMDocument createCDATASection method to properly create the appropriate bit and adds it back into the node.
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><Products />');
$xml_product = $xml->addChild('product');
$newNode = $xml_product->addChild('mychild');
$mytext = "<html></html>";
$node = dom_import_simplexml($newNode);
$cdata = $node->ownerDocument->createCDATASection($mytext);
$node->appendChild($cdata);
echo $xml->asXML();
This example outputs...
<?xml version="1.0" encoding="UTF-8"?>
<Products><product><mychild><![CDATA[<html></html>]]></mychild></product></Products>

DOMDocument: the root element gets just a single tag, not both

the following code should create an XML file using DOMDocument with a root element:
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true;
$root = $doc->createElement('gallery');
$doc->appendChild($root);
$doc->save($filename);
but I just get a single tag element (with that slash "/"), instead of
<gallery> </gallery>
I managed to get
<gallery> A content </gallery>
if I use createElement with the second parameter ( $doc->createElement('gallery', 'A content') ) but since it's the root element, I don't need to put a content in there.
How to fix it?
Thank you
<gallery /> is equivalent to <gallery></gallery>. If you were to add content to the "single tag element", it would be rendered with the start and end tags, and the content between.
<gallery/> is the short serialization form for an empty tag. If loaded into an DOM it will result in the same node. So most of the time it is just irrelevant and you can use the short empty form.
However, you can require DOM to save empty tags in the long form:
$document = new DOMDocument('1.0', 'utf-8');
$document->formatOutput = true;
$root = $document->appendChild(
$document->createElement('gallery')
);
// echo $document->save($filename, LIBXML_NOEMPTYTAG);
echo $document->saveXml(null, LIBXML_NOEMPTYTAG);
Output:
<?xml version="1.0" encoding="utf-8"?>
<gallery></gallery>

Adding namespace to XML

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

how to add xml element tag to correct place?

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.

Categories