i created a XML document using DOMDocument and simpleXML. I need to add an attibute to the root element.
Below is how I created the document and the element. You will note that although the document is initally created by DOMDocument, the child/user nodes are created with simple XML.
$dom = new DOMDocument('1.0','UTF-8');
/*** create the root element ***/
$root = $dom->appendChild($dom->createElement( "Feed" ));
/*** create the simple xml element ***/
$sxe = simplexml_import_dom( $dom );
/*** add a user node ***/
$firstChild = $sxe->addchild("FirstChild");
I tried adding the attibutes to the root like this:
$root = $dom->appendData($dom->createAttribute("extractDate",
"$now"));
but this does not work.
Using DOMDocument, you can set the attribute using the DOMElement::setAttribute method:
$dom = new DOMDocument('1.0','UTF-8');
$root = $dom->createElement("Feed");
$attr = $root->setAttribute("extractDate", "$now"); // <-- here
$dom->appendChild($root);
For SimpleXML, you'll need to use the SimpleXMLElement::addAttribute method:
$sxe = simplexml_import_dom($dom);
$sxe->addAttribute("extractDate", "$now"); // <-- here
Related
I have wrote this code in PHP to compile an XML file with parameters that are in URL.
But when the XML file is already created instead of adding the new data at the bottom of file inside the root element, overwrite it and delete all old data.
Where is the problem?
I have seen some examples online but I can't figure out how fix it.
I need to verify if file already exist and then add the element?
Or I need to read it and then add again the old elements and new?
I don't know very well dom so I can't figure out
<?php
$FOL = $_GET["FOL"];
$NUM = $_GET["NUM"];
$DAT = $_GET["DAT"];
$ZON = $_GET["ZON"];
$TIP = $_GET["TIP"];
$COM = $_GET["COM"];
$dom = new DOMDocument();
$dom->encoding = 'utf-8';
$dom->xmlVersion = '1.0';
$dom->formatOutput = true;
$xml_file_name = "$NUM.xml";
$xmlString = file_get_contents($xml_file_name);
$dom->loadXML($xmlString);
$loaded_xml = $dom->getElementsByTagName('Territorio');
$territorio_node = $dom->createElement('Territorio');
$child_node_NOM = $dom->createElement('NOM', "$NOM");
$territorio_node->appendChild($child_node_NOM);
$child_node_NUM = $dom->createElement('NUM', "$NUM");
$territorio_node->appendChild($child_node_NUM);
$child_node_DAT = $dom->createElement('DAT', "$DAT");
$territorio_node->appendChild($child_node_DAT);
$child_node_ZON = $dom->createElement('ZON', "$ZON");
$territorio_node->appendChild($child_node_ZON);
$dom->appendChild($territorio_node);
$child_node_TIP = $dom->createElement('TIP', "$TIP");
$territorio_node->appendChild($child_node_TIP);
$child_node_COM = $dom->createElement('COM', "$COM");
$territorio_node->appendChild($child_node_COM);
$dom->appendChild($territorio_node);
$dom->save($FOL.'/'.$xml_file_name);
echo "$xml_file_name creato correttamente";
?>
as per the comment: You should check that the root node of the XML file exists before calling createElement to generate a new one. To do that you can call getElementsByClassName and test whether the first entry is empty
ie: empty( $dom->getElementsByTagName('Territorio')[0] ) sort of thing...
If the root node exists we use that, otherwise add a new root to the document and continue
// check that the querystring has all the required parameters
if( isset(
$_GET['FOL'],
$_GET['NUM'],
$_GET['DAT'],
$_GET['ZON'],
$_GET['TIP'],
$_GET['COM']
)){
// the filename is generated from one of the querystring parameters
// - here the directory used is the same as the script running
$file=sprintf( '%s/%s.xml', __DIR__, $_GET['NUM'] );
// create an empty file if it does not exist
if( !file_exists( $file )){
file_put_contents( $file, '' );
}
clearstatcache();
// create the DOMDocument and set various options
libxml_use_internal_errors( true );
$dom=new DOMDocument('1.0','utf-8');
$dom->strictErrorChecking=false;
$dom->preserveWhiteSpace=true;
$dom->formatOutput=true;
$dom->recover=true;
// load the XML file directly rather than loading a string read from the original file.
$dom->load( $file );
// The ROOT node of the document ... does it exist? if not, create it and add to the DOM.
$root=$dom->getElementsByTagName('Territorio')[0];
if( empty( $root ) ){
$root=$dom->createElement('Territorio');
$dom->appendChild( $root );
}
// I added this part so that you can distinguish easily new elements
$record=$dom->createElement('record');
$root->appendChild( $record );
// add all the querystring parameters within the new record.
$record->appendChild( $dom->createElement('NUM', $_GET["NUM"] ) );
$record->appendChild( $dom->createElement('DAT', $_GET["DAT"] ) );
$record->appendChild( $dom->createElement('ZON', $_GET["ZON"] ) );
$record->appendChild( $dom->createElement('TIP', $_GET["TIP"] ) );
$record->appendChild( $dom->createElement('COM', $_GET["COM"] ) );
$record->appendChild( $dom->createElement('FOL', $_GET["FOL"] ) );
$dom->save( $file );
}
An example of the XML generated:
<?xml version="1.0" encoding="utf-8"?>
<Territorio>
<record>
<NUM>wibble</NUM>
<DAT>25/10/2022</DAT>
<ZON>europe</ZON>
<TIP>total</TIP>
<COM>94</COM>
<FOL>234</FOL>
</record>
<record>
<NUM>wibble</NUM>
<DAT>26/10/2022</DAT>
<ZON>europe</ZON>
<TIP>total</TIP>
<COM>96</COM>
<FOL>238</FOL>
</record>
</Territorio>
In the original code the file is saved to a location defined by another parameter in the querystring ( only just noticed that afterwards ) so rather than
$file=sprintf( '%s/%s.xml', __DIR__, $_GET['NUM'] );
you would likely want to do:
$file=sprintf( '%s/%s.xml', $_GET['FOL'], $_GET['NUM'] );
The root node of an XML document is called document element and here is an property for it. So you can just check if it is undefined. However an document can have only a single document element, so will need to modify the structure of your XML - for example add a "Territori" document element.
Do not use the second argument of the createElement() method or the $nodeValue property. Their escaping is broken - try adding a value with an &. Use $textContent or add a text node.
In modern DOM you can even just append() a string.
$NUM = "NUM";
$DAT = "DAT";
$ZON = "ZON";
$document = new DOMDocument('1.0', 'UTF-8');
// let the parser ignore existing indents
$document->preserveWhiteSpace = false;
$document->loadXML(getXMLString());
// fetch or create document element
$territori = $document->documentElement
?? $document->appendChild($document->createElement('Territori'));
// create/append an item element
$territori
->appendChild(
$territorio = $document->createElement('Territorio')
);
// create/append an element and set its text content
$territorio
->appendChild($document->createElement('NUM'))
->textContent = $NUM;
// create/append an element with a text child node
$territorio
->appendChild($document->createElement('DAT'))
->appendChild($document->createTextNode($DAT));
// create/append an element and a string (DOM Level 3)
$territorio
->appendChild($document->createElement('ZON'))
->append((string)$ZON);
// enable output formatting
$document->formatOutput = true;
echo $document->saveXML();
function getXMLString() {
return <<<'XML'
<?xml version="1.0"?>
<Territori>
<Territorio>
<NUM>NUM</NUM>
<DAT>DAT</DAT>
<ZON>DAT</ZON>
</Territorio>
</Territori>
XML;
}
For a more flexible approach to fetch nodes use Xpath expressions. Here is an example that checks if an Territorio with a specific NUM value exists:
$document = new DOMDocument('1.0', 'UTF-8');
$document->loadXML(getXMLString());
$xpath = new DOMXpath($document);
if ($xpath->evaluate('count(//Territorio[NUM="NUM"]) > 0')) {
echo "Node exists";
}
I have a XML file and I'm trying to insert a new node between two others with PHP script.
XML file:
<playlistLog>
<hour>
<track>
<type>take</type>
<url>URL</url>
<title>1473869236.wav</title>
<mix>0</mix>
</track>
(...)
</hour>
</playlistLog>
PHP file:
$xmldoc = new DOMDocument();
$xmldoc->load('../logs/log14-09-2016.xml');
$elem = $xmldoc->getElementsByTagName("track");
$track = $xmldoc->createElement('track');
$type = $xmldoc->createElement('type', 'take');
$track->appendChild($type);
$url = $xmldoc->createElement('url', 'url');
$track->appendChild($url);
$title = $xmldoc->createElement('title', 'title');
$track->appendChild($title);
$mix = $xmldoc->createElement('mix', 'mix');
$track->appendChild($mix);
$xmldoc->documentElement->insertBefore($track,$elem[660]);
$xmldoc->save('../logs/log14-09-2016.xml');
I'm trying to insert the new node before "track" tag number 660 but when I try to open the php file it doesn't work at all.
Can anybody tell me what I am doing wrong?
SOLUTION:
After #ThW response I change a bit what he wrotes and finally the code is doing right:
$document = new DOMDocument();
$document->preserveWhiteSpace = FALSE;
$document->load('../logs/log14-09-2016.xml');
$xpath = new DOMXpath($document);
$previousTrack = $xpath->evaluate('/playlistLog/hour/track')->item(659);
$newTrack = $previousTrack->parentNode->insertBefore($document->createElement('track'),$previousTrack);
$newTrack
->appendChild($document->createElement('type'))
->appendChild($document->createTextNode('take'));
$document->formatOutput = TRUE;
echo $document->save('../logs/log14-09-2016.xml');
$elem[660] is the 661st element node with the tag name track. But its parent node is not the document element. Here is another hour ancestor between. The node you're providing to insertBefore() has a different parent then the node you're adding the new element to.
You can use the $parentNode property to make sure that you append to that node.
Additionally I suggest using Xpath to fetch the track node.
$xml = <<<'XML'
<playlistLog>
<hour>
<track>
<type>take</type>
<url>URL</url>
<title>1473869236.wav</title>
<mix>0</mix>
</track>
</hour>
</playlistLog>
XML;
$document = new DOMDocument();
$document->preserveWhiteSpace = FALSE;
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$previousTrack = $xpath->evaluate('/playlistLog/hour/track[1]')->item(0);
$newTrack = $previousTrack
->parentNode
->insertBefore(
$document->createElement('track'),
$previousTrack
);
$newTrack
->appendChild($document->createElement('type'))
->appendChild($document->createTextNode('take'));
$document->formatOutput = TRUE;
echo $document->saveXml();
How to create a valid XML tree using only PHP?
I have been running into many troubles with creating an XML tree just using PHP. I already have some code to show where I am at right now:
$xml = new DOMDocument("1.0");
$verb = $xml->createElement ('verb');
$vtext = $xml->createTextNode($_POST['verb']);
$verb->appendChild($vtext);
$xml->appendChild ($verb);
$adverb = $xml->createElement ('adverb');
$advtext = $xml->createTextNode($_POST['adverb']);
$adverb->appendChild($advtext);
$xml->appendChild ($adverb);
$xml ->save("'".$_POST[verb]."'.xml") or die ("error creating file");
So, with this and the user input, it creates a file that is whatever the verb is. The only thing is, I keep getting error messages that there is extra content below the verbs.
I know that this is because I'm not adding a root element, but how exactly would you add this root element without creating errors? There are very little forums even talking about PHP and XML alone, so it's hard to find one that's about making an XML tree.
How about something like this?
$xml = new DOMDocument("1.0");
$root = $xml->createElement ('root');
$xml->appendChild($root);
$verb = $xml->createElement ('verb');
$vtext = $xml->createTextNode($_POST['verb']);
$verb->appendChild($vtext);
$root->appendChild ($verb);
$adverb = $xml->createElement ('adverb');
$advtext = $xml->createTextNode($_POST['adverb']);
$adverb->appendChild($advtext);
$root->appendChild ($adverb);
$xml ->save("'".$_POST[verb]."'.xml") or die ("error creating file");
This is a sample i have found in stackoverflow and this work for me! hope this will also help you!
<?php
/* create a dom document with encoding utf8 */
$domtree = new DOMDocument('1.0', 'UTF-8');
/* create the root element of the xml tree */
$xmlRoot = $domtree->createElement("xml");
/* append it to the document created */
$xmlRoot = $domtree->appendChild($xmlRoot);
$currentTrack = $domtree->createElement("track");
$currentTrack = $xmlRoot->appendChild($currentTrack);
/* you should enclose the following two lines in a cicle */
$currentTrack->appendChild($domtree->createElement('path','song1.mp3'));
$currentTrack->appendChild($domtree->createElement('title','title of song1.mp3'));
$currentTrack->appendChild($domtree->createElement('path','song2.mp3'));
$currentTrack->appendChild($domtree->createElement('title','title of song2.mp3'));
/* get the xml printed */
echo $domtree->saveXML();
?>
How exactly would you add this root element without creating errors?
For a DOMDocument (in general, not only for the PHP extension), the documentElement (often also called root element) is technically a child-node of the DOMDocument object.
You create it by appending it (as you already do (!)):
$xml = new DOMDocument("1.0");
$root = $xml->createElement ('root');
$xml->appendChild($root);
The new documentElement now is available as:
$xml->documentElement;
Try it your own (online Demo):
$xml = new DOMDocument("1.0");
$xml->save('php://output'); // document is empty
The output is just the XML declaration, no root element:
Then adding the root element (here named root):
$root = $xml->createElement ('root');
$xml->appendChild($root);
$xml->save('php://output'); // document has root-node now
And the Document has a Root-Node now as the output shows:
<?xml version="1.0"?>
<root/>
The $root is the documentElement now and Children can be appended to it:
$root->appendChild($xml->createTextNode("\n "));
$root->appendChild($xml->createElement('hello', 'world'));
$root->appendChild($xml->createTextNode("\n "));
$root->appendChild($xml->createElement('answer', '42'));
$root->appendChild($xml->createTextNode("\n "));
$root->appendChild($xml->createElement('danger', 'danger'));
$root->appendChild($xml->createTextNode("\n"));
$xml->save('php://output');
The output then is:
<?xml version="1.0"?>
<root>
<hello>world</hello>
<answer>42</answer>
<danger>danger</danger>
</root>
I'm trying to cleanup some bad html using DOMDocument. The html has an <div class="article"> element, with <br/><br/> instead of </p><p> -- I want to regex these into paragraphs...but can't seem to get my node back into the original document:
//load entire doc
$doc = new DOMDocument();
$doc->loadHTML($htm);
$xpath = new DOMXpath($doc);
//get the article
$article = $xpath->query("//div[#class='article']")->parentNode;
//get as string
$article_htm = $doc->saveXML($article);
//regex the bad markup
$article_htm2 = preg_replace('/<br\/><br\/>/i', '</p><p>', $article_htm);
//create new doc w/ new html string
$doc2 = new DOMDocument();
$doc2->loadHTML($article_htm2);
$xpath2 = new DOMXpath($doc2);
//get the original article node
$article_old = $xpath->query("//div[#class='article']");
//get the new article node
$article_new = $xpath2->query("//div[#class='article']");
//replace original node with new node
$article->replaceChild($article_old, $article_new);
$article_htm_new = $doc->saveXML();
//dump string
var_dump($article_htm_new);
all i get is a 500 internal server error...not sure what I'm doing wrong.
There are several issues:
$xpath->query returns a nodeList, not a node. You must select an item from the nodeList
replaceChild() expects as 1st argument the new node, and as 2nd the node to replace
$article_new is part of another document, you first must import the node into $doc
Fixed code:
//load entire doc
$doc = new DOMDocument();
$doc->loadHTML($htm);
$xpath = new DOMXpath($doc);
//get the article
$article = $xpath->query("//div[#class='article']")->item(0)->parentNode;
//get as string
$article_htm = $doc->saveXML($article);
//regex the bad markup
$article_htm2 = preg_replace('/<br\/><br\/>/i', '</p>xxx<p>', $article_htm);
//create new doc w/ new html string
$doc2 = new DOMDocument();
$doc2->loadHTML($article_htm2);
$xpath2 = new DOMXpath($doc2);
//get the original article node
$article_old = $xpath->query("//div[#class='article']")->item(0);
//get the new article node
$article_new = $xpath2->query("//div[#class='article']")->item(0);
//import the new node into $doc
$article_new=$doc->importNode($article_new,true);
//replace original node with new node
$article->replaceChild($article_new, $article_old);
$article_htm_new = $doc->saveHTML();
//dump string
var_dump($article_htm_new);
Instead of using 2 documents you may create a DocumentFragment of $article_htm2 and use this fragment as replacement.
I think it should be
$article->parentNode->replaceChild($article_old, $article_new);
the article is not a child of itself.
I have a little question ... this is my xml:
<?xml version="1.0" encoding="UTF-8"?>
<links>
<link>
<id>432423</id>
<href>http://www.google.ro</href>
</link>
<link>
<id>5432345</id>
<href>http://www.youtube.com</href>
</link>
<link>
<id>5443</id>
<href>http://www.yoursite.com</href>
</link>
</links>
How can i ad another
<link>
<id>5443</id>
<href>http://www.yoursite.com</href>
</link>
??
I managed only to add a record to ROOT/LINKS -> LINK using xpath, and here is the code
<?php
$doc = new DOMDocument();
$doc->load( 'links.xml' );
$links= $doc->getElementsByTagName("links");
$xpath = new DOMXPath($doc);
$hrefs = $xpath->evaluate("/links");
$href = $hrefs->item(0);
$item = $doc->createElement("item");
/*HERE IS THE ISSUE...*/
$link = $doc->createElement("id","298312800");
$href->appendChild($link);
$link = $doc->createElement("link","www.anysite.com");
$href->appendChild($link);
$href->appendChild($item);
print $doc->save('links.xml');
echo "the link has been added!";
?>
Any help would be appreciated :D
$doc = new DOMDocument();
// Setting formatOutput to true will turn on xml formating so it looks nicely
// however if you load an already made xml you need to strip blank nodes if you want this to work
$doc->load('links.xml', LIBXML_NOBLANKS);
$doc->formatOutput = true;
// Get the root element "links"
$root = $doc->documentElement;
// Create new link element
$link = $doc->createElement("link");
// Create and add id to new link element
$id = $doc->createElement("id","298312800");
$link->appendChild($id);
// Create and add href to new link element
$href = $doc->createElement("href","www.anysite.com");
$link->appendChild($href);
// Append new link to root element
$root->appendChild($link);
print $doc->save('links.xml');
echo "the link has been added!";
XPath is used to locate nodes in an XML document, not to manipulate the tree. Try $dom->appendChild($new_link).