I know this should be simple, but I'm new to PHP and just do not understand the documentation I've come across. I need a simple explanation.
I have an XML document that I would like to add nodes to. I can add nodes to the document, they only appear outside of the root node and cause errors to happen on subsequent attempts.
Here is my XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
</root>
and here is my PHP:
$playerID = "id_" . $_POST['player'];
//load xml file to edit
$xml = new DOMDocument();
$xml->load('../../saves/playerPositions.xml') or die("Error: Cannot load file.");
//check if the player is already in the database
if(isset($xlm->$playerID)){
echo "Player ID was found.";
}
else{
echo "Player ID was not found.";
//so we want to create a new node with this player information
$playerElement = $xml->createElement($playerID, "");
$playerName = $xml->createElement("name", "John Doe");
//add the name to the playerElement
$playerElement->appendChild($playerName);
//add the playerElement to the document
//THIS IS WHERE THE ERROR OCCURS
$xml->root->appendChild($playerElement);
//save and close the document
$xml->formatOutput = true;
$xml->save("../../saves/playerPositions.xml");
}
echo "done";
If I just use $xml->appendChild() then I can modify the document, but the new text appears outside of <root></root>.
The exact error is:
Notice: Undefined property: DOMDocument::$root
$xml->root isn't the correct way to access root element in this context, since $xml is an instance of DOMDocument (It will work if $xml were SimpleXMLElement instead). You can get root element of a DOMDocument object from documentElement property :
$xml->documentElement->appendChild($playerElement);
eval.in demo 1
Or, more generally, you can get element by name using getElementsByTagName() :
$xml->getElementsByTagName("root")->item(0)->appendChild($playerElement);
eval.in demo 2
Further reading : PHP DOM: How to get child elements by tag name in an elegant manner?
That said, this part of your code is also not correct for the same reason (plus a typo?):
if(isset($xlm->$playerID)){
echo "Player ID was found.";
}
Replace with getElementsByTagName() :
if($xml->getElementsByTagName($playerID)->length > 0){
echo "Player ID was found.";
}
You are trying to handle the dom like a Standar Object, but is not.
To look for elements, you need to use the function getElementsByTagName, and handle the dom like a collection of nodes, like this:
$xml = new DOMDocument();
$xml->loadXML('<?xml version="1.0" encoding="UTF-8"?>
<root>
</root>') or die("Error: Cannot load file.");
$len = $xml->getElementsByTagName('player')->length;
if ( $len > 0 ) {
} else {
$player = $xml->createElement('player', '');
$playerName = $xml->createElement('playerName', "Jhon Doe");
$player->appendChild( $playerName );
$root = $xml->getElementsByTagName('root');
if ( $root->length > 0 ) {
$root[0]->appendChild( $player );
}
$xml->formatOutput = true;
echo $xml->saveXML();
}
This code produces:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<player><playerName>Jhon Doe</playerName></player></root>
Related
I am having an issue with DOMDocument and whitespace. Currently I have two types of XML files. One file was created manually about a year ago, I will call this file A. The second file, file B, is being generated using a PHP DOMDocument. I have been trying very hard (unsuccessfully) to make the whitespace in file A match file B.
Here's how it works... The user is given an option to add new <Slide> elements to the XML file. After new slides have been added the user has the option to add new <Items> to the XML file as a child of the <Slide> element.
When I add a <Slide> element to file B it works like a charm. I can even add a new <Item> element with zero problem. However, when I try to access the new <Identifier> element I just added in file B using the second PHP script below with $order != 'remove' I miss the node by one and select <Information/> instead.
It appears that manually created file A has white space that is not present in my generated file B. I have experimented with the preserveWhitespace property but it did not help.
Are there any suggestions on how I can correct this problem. Constructive criticism is also welcome as this is my first shot at dynamic XML manipulation. I apologize for the length and appreciate your time!!
File A - Created Manually - I am trying to match this file!
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Areas>Head & Neck</Areas>
<Area>Head & Neck</Area>
<Type>Angiograph</Type>
<Slide>Ag-01a
<Title>Catheter Angiography</Title>
<Item1>
<Identifier interestCoord=".51,.73" locator="point" labelBool="true" labelTxt="" leaderBool="true">Aortic Arch
</Identifier>
<Information/>
<Question A="" B="" C="" D="" E="" Answer=""/>
</Item1>
.... More Items
File B - Before user adds <Slide>. This portion is created Manually. A template if you will. After the user enters slide names new slides are generated using the chunk of code below.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Areas>Head & Neck</Areas>
<Area>Head & Neck</Area>
<Type>Brain Sections</Type>
</root>
File B - After users adds new <Slide> and <Item>. Formatting shown represents formatting created by DOMDocument. I think this is where the error is occuring! Whitespace!!!
<Slide>Ag-09a
<Title>Catheter Angiography</Title>
<Item1><Identifier locator="point" interestCoord="0.143,0.65" labelBool="true" labelTxt="" leaderBool="false">Orbit</Identifier><Information/><Question A="" B="" C="" D="" E="" Answer=""/></Item1></Slide>
PHP script used to add new <Slide> elements to XML
<?php
session_start();
//Constants
$SECTION_SEP = "========================================================================</br>";
//Variables used to construct file path
$area = trim($_POST['area']);
$slideType = trim($_POST['slideType']);
$rawSlides = trim($_POST['theseSlides']);
$newSlideList = explode(",", $rawSlides);
$fileLocation = "../XML/".$area."/".$slideType."/".$area.".XML";
$dom = new DOMDocument();
echo('New DOMDocument created!</br>');
$dom->load($fileLocation);
echo('XML file loaded!</br>');
/*$dom->preserveWhiteSpace = false;
echo('White space removed!</br>');*/
$dom->documentElement;
echo('DOM initialized!</br>');
if ($dom->getElementsByTagName('Slide')->length == 0){ //New file with no slides
foreach ($newSlideList as $slide){
$newSlide = $dom->createElement('Slide', $slide);
$newTitle = $dom->createElement('Title', 'Scan');
//Add the title element to the Item
$newSlide->appendChild($newTitle);
$dom->childNodes->item(0)->appendChild($newSlide);
echo($slide." has been added to the list!</br>");
}
} else {
$locators = $dom->getElementsByTagName('Slide');
}
if($dom->save($fileLocation)){
echo("File saved successfully!!");
}else echo("There was a problem saving the file!");
PHP script used to add/edit/remove <Item> and <Identifier> nodes depending on value of $orders == WARNING! Lengthy :/
<?php
session_start();
//Constants
$SECTION_SEP = "========================================================================</br>";
//Variables used to construct file path
$area = trim($_POST['area']);
$slideType = trim($_POST['slideType']);
$fileLocation = "../XML/".$area."/".$slideType."/".$area.".XML";
//echo("File location:".$fileLocation);
//Current data (c_ for current)
$c_poi = "";
$c_type = "";
$c_lblBool = "";
$c_lblOverride = "";
$c_leaderBool = "";
//Determine if this visit is for new or old data
$orders = trim($_POST['orders']);
//Variables used to replace information in XML file loaded below (n_ for new)
$n_slideName = trim($_POST['slideName']); //slide name in view format ie Ag-01a
$n_identName = trim($_POST['ident']); //contains multiple information separated by comma ie 0,Aortic Arch
$n_type = trim($_POST['type']); //locator type
$n_poi = trim($_POST['poi']);
$n_lblBool = trim($_POST['lblBool']);
$n_lblOverride = trim($_POST['lblOverride']);
echo("Modified: ".date('c')."</br>");
$dom = new DOMDocument();
echo('New DOMDocument created!</br>');
$dom->load($fileLocation);
echo('XML file loaded!</br>');
/*$dom->preserveWhiteSpace = false;
echo('White space removed!</br>');*/
$dom->documentElement;
echo('DOM initialized!</br>');
$locators = $dom->getElementsByTagName('Slide');
echo($locators->length.' elements retrieved</br>');
$slideEntryFound = false;
$identEntryFound = false;
$identAttributesFound = false;
echo($SECTION_SEP);
//Locate the correct slide node
foreach ($locators as $locator){
//If there is a match, store the infomation
// rawSlide[x].childNode[0].nodeValue
if(strcmp(trim($locator->childNodes->item(0)->nodeValue),$n_slideName) == 0){
$slideEntryFound = true;
$slideChildren = $locator->childNodes;
//Locate the correct identifier node
foreach($slideChildren as $child){
if( strcmp(trim($child->nodeValue), substr($n_identName,strpos($n_identName,",")+1)) == 0){
$identEntryFound = true;
if (strcmp($orders, "remove") == 0){//Removing an element
echo("The identifier being removed is: ".trim($child->nodeValue."</br>"));
echo("The node path is: ".($child->childNodes->item(1)->getNodePath())."</br>");
echo($SECTION_SEP);
$locator->removeChild($child);
echo("Identifier successfully removed!</br>");
echo($SECTION_SEP);
break;
} else {//Not removing anything - Adding or Editing
echo("The identifier being modified is: ".trim($child->nodeValue."</br>"));
echo("The node path is: ".($child->childNodes->item(1)->getNodePath())."</br>");
echo($SECTION_SEP);
if($child->childNodes->item(1)->hasAttributes()){
$identAttributesFound = true;
$c_poi = $child->childNodes->item(1)->getAttribute('interestCoord');
echo("--Current interestCoord: ".$c_poi."</br>");
echo("++New interestCoord: ".$n_poi."</br>");
if(strcmp($c_poi, $n_poi) != 0){
$child->childNodes->item(1)->setAttribute('interestCoord',$n_poi);
}
$c_type = $child->childNodes->item(1)->getAttribute('locator');
echo("--Current locator: ".$c_type."</br>");
echo("++New locator: ".$n_type."</br>");
$c_lblBool = $child->childNodes->item(1)->getAttribute('labelBool');
echo("--Current labelBool: ".$c_lblBool."</br>");
//echo("++New labelBool: ".$n_lblBool."</br>");
$c_lblOverride = $child->childNodes->item(1)->getAttribute('labelTxt');
echo("--Current labelOverride: ".$c_lblOverride."</br>");
echo("++New labelOverride: ".$n_lblOverride."</br>");
$c_leaderBool = $child->childNodes->item(1)->getAttribute('leaderBool');
echo("--Current leaderBool: ".$c_leaderBool."</br>");
//echo("++New leaderBool: ".$n_leaderBool."</br>");
if($n_lblOverride != ""){
echo("**A new label override was detected. The identifier will have the alias ".$n_lblOverride.".");
}
break;
} else echo("Fatal Error - Node does not contain attributes!</br>");
if($identEntryFound == true && $identAttributesFound == false)
echo("Error - Attribute entry not found!");
break;
}
}
}
if($slideEntryFound == true && $identEntryFound == false && $orders != "remove"){
echo("The identifier was not found... creating a new identifier!</br>");
//Create a new Element
$newElement = $dom->createElement("Item".((integer)(substr($n_identName,0,strpos($n_identName,",")))+1));
echo("New element created!!</br>");
//Create new Item children
$newSubElem = $dom->createElement("Identifier", substr($n_identName,strpos($n_identName,",")+1));
$newSubElem->setAttribute('locator',$n_type);
$newSubElem ->setAttribute('interestCoord',$n_poi);
$newSubElem->setAttribute('labelBool', $n_lblBool);
$newSubElem->setAttribute('labelTxt', $n_lblOverride);
//TODO link this next one to a variable instead of hard coding
$newSubElem->setAttribute('leaderBool', "false");
//Info Child
$newInfoElem = $dom->createElement("Information");
//Question Child
$newQuestion = $dom->createElement("Question");
$newQuestion->setAttribute('A', "");
$newQuestion->setAttribute('B', "");
$newQuestion->setAttribute('C', "");
$newQuestion->setAttribute('D', "");
$newQuestion->setAttribute('E', "");
$newQuestion->setAttribute('Answer', "");
//Add new children to main Item
$newElement->appendChild($newSubElem);
$newElement->appendChild($newInfoElem);
$newElement->appendChild($newQuestion);
$locator->appendChild($newElement);
echo("New identifier added!!</br>");
break;
}
} else {
}
}
if($slideEntryFound == false)
echo("Error - Slide entry not found!");
if($dom->save($fileLocation)){
echo("File saved successfully!!");
echo('<div id="phpHandleBtns>"></br><form><button type="submit" id="continueEdit" formaction="../edit.php">Continue Editing</button>'.
'</br><button type="submit" id="doneEdit" formaction="../main.php">Done Editing</button></form></div>');
}else echo("There was a problem saving the file!");
?>
I would strongly recommend that you use an XPath API like this http://php.net/manual/en/class.domxpath.php to find the nodes you are interested in. Attempting to use the DOM API directly is only going to cause you heartache.
More specifically, I think that your call to childNode() is getting tripped up by white space, but if you used childElement() instead (not sure if that exists, but with XPath it is easy), it would just ignore any whitespace.
I'm trying to parse an xml file and print the items in different order and also with some other text between. The xml file is something like this
<list>
<item>
<price>200</price>
<title>Title1</title>
<description>something here</description>
</item>
<item>
<price>350</price>
<title>Title2</title>
<description>something there</description>
</item>
</list>
and I want to have the output exactly like this, 2 different lines:
"Title1","something here","","200","1",""
"Title2","something there","","350","1",""
It's important to see quotes and commas.
I'm using this, but it is not enough. I don't know what to do next...
<?php
//Initialize the XML parser
$parser=xml_parser_create();
//Function to use at the start of an element
function start($parser,$element_name,$element_attrs)
{
switch($element_name)
{
case "PRICE":
echo """;
break;
case "TITLE":
echo """;
break;
case "DESCRIPTION":
echo """;
}
}
//Function to use at the end of an element
function stop($parser,$element_name)
{
echo "",";
}
//Function to use when finding character data
function char($parser,$data)
{
echo $data;
}
//Specify element handler
xml_set_element_handler($parser,"start","stop");
//Specify data handler
xml_set_character_data_handler($parser,"char");
//Open XML file
$fp=fopen("shopmania_ro.xml","r");
//Read data
while ($data=fread($fp,4096))
{
xml_parse($parser,$data,feof($fp)) or
die (sprintf("XML Error: %s at line %d",
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser)));
}
//Free the XML parser
xml_parser_free($parser);
?>
Thank you in advance.
I have another code
<?php
$xfile = "file.xml";
$xparser=xml_parser_create();
xml_set_character_data_handler($xparser, "cdataHandler");
if(!($fp=fopen($xfile,"r")))
{
die ("File does not exist");
}
while($data=fread($fp, 4096))
{
if(!xml_parse($xparser,$data,feof($fp)))
{
die("XML parse error: xml_error_string(xml_get_error_code($xparser))");
}
}
xml_parser_free($xml_parser);
function cdataHandler($xparser, $cdata)
{
echo "$cdata";
}
?>
and the output is
200 Title1 something here 350 Title2 something there
I don't know how to extract data and print it in the way that I want. Any help?
Sorry, I'm a newbie...
That's exactely what the XSLT stylesheet language was made for. Transforming XML documents to different formats. PHP comes with the XSL extension that's pretty easy to use. Examples are there, too.
EDIT
If that's an overkill for your purpose you should take a look at PHP's SimpleXML extension that allows you to use a node like you would use an object.
EDIT 2
$xmlstr = <<<XML
<?xml version='1.0' standalone='yes'?>
<list>
<item>
<price>200</price>
<title>Title1</title>
<description>something here</description>
</item>
...
</list>
XML;
$xml = simplexml_load_string($xmlstr);
$list = $xml->list;
foreach ( $list->item as $item ) {
// Not sure if this works, but if it doesn't, substitute the sprintf
// with plain string concatenation
echo sprintf(
'"%s","%s","","%d","1",""',
$item->title,
$item->description,
$item->price
);
}
I've had some success due to the help of StackOverflow community to modify a complex XML source for use with jsTree. However now that I have data that is usable, it is only so if i manually edit the XML to do the following :
Rename all <user> tags to <item>
Remove some elements before the first <user> tag
insert an 'encoding=UTF-8' into the XML opener
and lastly modify the <response> (opening XML tag) to <root>
XML File Example : SampleXML
I have read and read through so many pages on here and google but cannot find a method to achieve the above items.
Point (2) I have found out that by loading it via SimpleXML and using UNSET i can delete the portions I do not require, however I am still having troubles with the rest.
I thought I could perhaps modify the source with SimpleXML (that I am more familiar with) and then continue to modify the code via the help I had been provided before.
<?php
$s = file_get_contents('http://www.fluffyduck.com.au/sampleXML.xml');
$doc1 = simplexml_load_string($s);
unset($doc1->row);
unset($doc1->display);
#$moo = $doc1->user;
echo '<textarea>';
echo $doc1->asXML();
echo '</textarea>';
$doc = new DOMDocument();
$doc->loadXML($doc1);
$users = $doc->getElementsByTagName("user");
foreach ($users as $user)
{
if ($user->hasAttributes())
{
// create content node
$content = $user->appendChild($doc->createElement("content"));
// transform attributes into content elements
for ($i = 0; $i < $user->attributes->length; $i++)
{
$attr = $user->attributes->item($i);
if (strtolower($attr->name) != "id")
{
if ($user->removeAttribute($attr->name))
{
if($attr->name == "username") {
$content->appendChild($doc->createElement('name', $attr->value));
} else {
$content->appendChild($doc->createElement($attr->name, $attr->value));
}
$i--;
}
}
}
}
}
$doc->saveXML();
header("Content-Type: text/xml");
echo $doc->saveXML();
?>
Using recursion, you can create a brand new document based on the input, solving all your points at once:
Code
<?php
$input = file_get_contents('http://www.fluffyduck.com.au/sampleXML.xml');
$inputDoc = new DOMDocument();
$inputDoc->loadXML($input);
$outputDoc = new DOMDocument("1.0", "utf-8");
$outputDoc->appendChild($outputDoc->createElement("root"));
function ConvertUserToItem($outputDoc, $inputNode, $outputNode)
{
if ($inputNode->hasChildNodes())
{
foreach ($inputNode->childNodes as $inputChild)
{
if (strtolower($inputChild->nodeName) == "user")
{
$outputChild = $outputDoc->createElement("item");
$outputNode->appendChild($outputChild);
// read input attributes and convert them to nodes
if ($inputChild->hasAttributes())
{
$outputContent = $outputDoc->createElement("content");
foreach ($inputChild->attributes as $attribute)
{
if (strtolower($attribute->name) != "id")
{
$outputContent->appendChild($outputDoc->createElement($attribute->name, $attribute->value));
}
else
{
$outputChild->setAttribute($attribute->name, $attribute->value);
}
}
$outputChild->appendChild($outputContent);
}
// recursive call
ConvertUserToItem($outputDoc, $inputChild, $outputChild);
}
}
}
}
ConvertUserToItem($outputDoc, $inputDoc->documentElement, $outputDoc->documentElement);
header("Content-Type: text/xml; charset=" . $outputDoc->encoding);
echo $outputDoc->saveXML();
?>
Output
<?xml version="1.0" encoding="utf-8"?>
<root>
<item id="41">
<content>
<username>bsmain</username>
<firstname>Boss</firstname>
<lastname>MyTest</lastname>
<fullname>Test Name</fullname>
<email>lalal#test.com</email>
<logins>1964</logins>
<lastseen>11/09/2012</lastseen>
</content>
<item id="61">
<content>
<username>underling</username>
<firstname>Under</firstname>
<lastname>MyTest</lastname>
<fullname>Test Name</fullname>
<email>lalal#test.com</email>
<logins>4</logins>
<lastseen>08/09/2009</lastseen>
</content>
</item>
...
With the following script I rename the elements through XSLT of an XML and output the result. However I want to save it in a new file on my browser but the only I managed to do is to save the input XML.
<?php
// create an XSLT processor and load the stylesheet as a DOM
$xproc = new XsltProcessor();
$xslt = new DomDocument;
$xslt->load('stylesheet.xslt'); // this contains the code from above
$xproc->importStylesheet($xslt);
// your DOM or the source XML (copied from your question)
$xml = '<document><item><title>Hide your heart</title><quantity>1</quantity><price>9.90</price></item></document>';
$dom = new DomDocument;
$dom->loadXML($xml);
// do the transformation
if ($xml_output = $xproc->transformToXML($dom)) {
echo $xml_output;
} else {
trigger_error('Oops, XSLT transformation failed!', E_USER_ERROR);
}
?>
I used
echo $dom->saveXML();
$dom->save("write.xml")
and replaced it with xml_output with no luck.
if ($xml_output = $xproc->transformToDoc($dom)) {
$xml_output->save('write.xml');
} else {
trigger_error('Oops, XSLT transformation failed!', E_USER_ERROR);
}
transformToXML() returns a string instead of a DOMDocument. You can also write the string to a file using the common file handling functions
if ($xml_output = $xproc->transformToXML($dom)) {
echo $xml_output;
file_put_contents('write.xml', $xml_output);
} else {
trigger_error('Oops, XSLT transformation failed!', E_USER_ERROR);
}
Update: Just found the third method :) Depending on what you want to achieve this is the one you are looking for
$xproc->transformToURI($doc, 'write.xml');
http://php.net/xsltprocessor.transformtouri
You can find the signature of the whole class at the manual: http://php.net/class.xsltprocessor
I have the following XML structure, and I would like to have code that:
INSERTS
DELETES
Delete an entire element based on its TITLE value.
Replace the publisher's value with another value
<Game type="XXX">
<TITLE>XXX</TITLE>
<PUBLISHER>XXX</PUBLISHER>
</Game>
This is my XML structure:
<?xml version="1.0" encoding ="utf-8"?>
<GameStore>
<Game type="adventure">
<TITLE>Assassin's Creed: Brotherhood</TITLE>
<PUBLISHER>Ubisoft</PUBLISHER>
</Game>
<Game type="adventure">
<TITLE>Batman: Arkham Asylum</TITLE>
<PUBLISHER>Eidos</PUBLISHER>
</Game>
</GameStore>
Using SimpleXML:
<?php
// load the XML file
$games = simplexml_load_file('games.xml');
$i = 0;
foreach ($games as $game) {
if ($game->TITLE == 'Batman: Arkham Asylum') {
// remove the Game element
unset($games->Game[$i]);
}
$i++;
}
// save the amended file
$games->asXML('games2.xml');
?>
You will want to use the PHP DOM classes. I think it goes something like this.
To load an XML file.
$xml = new DOMDocument();
$xml->load('path/to/file.xml');
To insert.
$node = $xml->createElement('Game');
$newnode = $xml->appendChild($node);
To delete based on title.
foreach ($xml->Game as $node) {
if ($node->TITLE->nodeValue == 'some_title') {
$xml->removeChild($node);
}
}
etc
more info at http://www.php.net/manual/en/book.dom.php
You will need to learn http://www.php.net/manual/fr/book.dom.php for manipulating xml.
There is some hints :
// Inserts
$new_node = $dom->createElement('foo', 'bar');
$gamestore_node = $xpath->query('/*')->item(0)->appendChild($new_node);
echo $dom->saveXML();
// Delete based on title
$deletable_node = $xpath->query("//Game[TITLE/text() = 'Batman: Arkham Asylum']")->item(0);
$deletable_node->parentNode->removeChild($deletable_node);
echo $dom->saveXML();
// EDIT Replace Publisher's value with another value
$editable_node = $xpath->query("//PUBLISHER[text() = 'Ubisoft']")->item(0);
$editable_node->nodeValue = 'baz';
echo $dom->saveXML();