PHP XML Output Concatenation - php

I am using XPath to query an xml document and then copy found nodes to a separate results xml file, however the problem I am having is that the XML declaration at the top gets added each time a new node is added to the results file.
Here is my php code:
$allEventsForVenue = $xPath->query(
"/Ticket/EventsPoints/Event[contains(PerformanceName,'$searchParam')]"
);
foreach ($allEventsForVenue as $event) {
$dstDom = new DOMDocument('1.0', 'utf-8');
$dstDom->appendChild($dstDom->createElement('EventsPoints'));
$dstDom->documentElement->appendChild($dstDom->importNode($event, true));
//echo $dstDom->saveXml();
$dstDom->formatOutput = true;
$strxml .= $dstDom->saveXML();
$handle = fopen("/var/www/html/xml/searchresults$searchID.xml", "w");
fwrite($handle, $strxml);
fclose($handle);
}
Invalid XML File (two xml declarations in the one file):
->> <?xml version="1.0" encoding="utf-8"?>
<EventsPoints>
<Event ID="17">
<PerformanceName>Bressie</PerformanceName>
<PresaleTime/>
<PriceRange>17 - 17</PriceRange>
<VenueID ID="19"/>
</Event>
</EventsPoints>
->> <?xml version="1.0" encoding="utf-8"?>
<EventsPoints>
<Event ID="180">
<PerformanceName>U2</PerformanceName>
<PriceRange>20 - 20</PriceRange>
<VenueID ID="198"/>
</Event>
</EventsPoints>

You are creating new DOMDocument instance in loop. Also you are writing in each iteration. This is making new XML document on each iteration and its get appended in your xml file. What you need to do is, loop the XML generation part. Not the whole thing.
If you loop only the appendChild parts, your problem will be solved.
$dstDom = new DOMDocument('1.0', 'utf-8');
$dstDom->formatOutput = true;
foreach ($allEventsForVenue as $event) {
$dstDom->appendChild($dstDom->createElement('EventsPoints'));
$dstDom->documentElement->appendChild($dstDom->importNode($event, true));
}
file_put_contents("/var/www/html/xml/searchresults$searchID.xml", $dstDom->saveXML());

DomDocument lets you output a specific node without the xml declaration.
Just include a node in saveXML, like saveXML($node), with node being an XMLNode type object.
With this method I think you can bypass the whole "create sub-xmldocument/import node" thing, by outputting directly the desired node in a string.

use something like this
enter <?php
// Continued from example XML above.
/* Search for <a><b><c> */
$result = $xml->xpath('/a/b/c');
while(list( , $node) = each($result)) {
echo $node->asXML();
}
?>

Related

How to load string back xml with simpleXML in PHP?

Would anyone know how i can "explode" a string back into "normal" xml format?
I found this script (ref:gooseflight,2010) that looks like it can do the job but the output comes out stuck together.
Here's the code:
enter code herefunction combineXML($file)
{
global $xmlstr;
$xml = simplexml_load_file($file);
foreach($xml as $element)
$xmlstr .= $element->asXML();
}
$files[] = "tmp.xml";
$files[] = "traduction.xml";
$xmlstr = '<CAB>';
foreach ($files as $file)
combineXML($file);
$xmlstr .= '</CAB>';
// Convert string to XML for further processing
$xml = simplexml_load_string($xmlstr);
$bytes = file_put_contents("combined.xml", $xml->asXML())
Here is the output:
<?xml version="1.0" encoding="UTF-8"?>
<CAB>
<CABO>XXXXXXXXXX0987650003</CABO><ACTIVITY>NONE</ACTIVITY><BEORI>blablaE</BEORI>BEDEST>blabla</BEDEST><NATRELA>more blabla</NATRELA><ANE>2014</ANE><NODEP>1111</NODEP>
</CAB>
So how could i seperate the nodes to look like this?:
<?xml version="1.0" encoding="UTF-8"?>
<CAB>
<CABO>XXXXXXXXXX0987650003</CABO>
<ACTIVITY>NONE</ACTIVITY>
<BEORI>blablaE</BEORI>
<BEDEST>blabla</BEDEST>
<NATRELA>more blabla</NATRELA>
<ANE>2014</ANE>
<NODEP>1111</NODEP>
.....
</CAB>
Would anyone know how to fix it?
I would suggest to use DomDocument class to save the XML; check this:
$dom_obj = new DOMDocument();
$dom_obj->loadXML($file);
// Do all your changes to the file by using DomDocument command (e.g. CreateElement, CreateAttribute, etc)
$dom_obj->formatOutput = true;
$dom_obj->save($file);

How to create a valid XML tree root element in using only PHP?

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>

Add XML header information after transform

I have an xml document which I transform, view in the browser and save to a directory.
I would like to add a linked stylesheet to the saved version of the file. I've tried str_replace as below (might be incorrect usage) and also using xsl processing instruction.
xsl processing instruction worked to a degree, if you view source in the browser, you would see the stylesheet link, however it would not save this information to the saved file!!
All I want to do is take the raw xml file, transform it with the stylesheet, save it to a directory and append the xsl stylesheet to the header of the new file, so when the newly saved xml file is opened in the browser, the stylesheet is applied automatically. Hope this makes sense!!
My code is below.
//write to the file
$id = $_POST['id'];//xml id
$path = 'xml/';//send to xml directory
$filename = $path. $id . ".xml";
$header = str_replace('<?xml version="1.0" encoding="UTF-8"?>', '<?xml version="1.0" encoding="UTF-8" ?><?xml-stylesheet type="text/xsl" href="../foo.xsl"?>');
$article->asXML($filename);//saving the original xml as new file with posted id
$xml = new DOMDocument;
$xml->load($filename);
$xsl = new DOMDocument;
$xsl->load('insert.xsl');
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
echo $proc->transformToXML($xml);
Thanks in advance!
You could use the <xsl:processing-instruction/> tag in the XSLT-file to add the stylesheet link.
<xsl:processing-instruction name="xml-stylesheet">
type="text/xsl" href="../foo.xsl"
</xsl:processing-instruction>
This would produce:
<?xml-stylesheet type="text/xsl" href="../foo.xsl"?>
Alternativly, you could do it with a DOMDocument.
$newXml = $proc->transformToXML($xml);
// Re-create the DOMDocument with the new XML
$xml = new DOMDocument;
$xml->loadXML($newXml);
// Find insertion-point
$insertBefore = $xml->firstChild;
foreach($xml->childNodes as $node)
{
if ($node->nodeType == XML_ELEMENT_NODE)
{
$inertBefore = $node;
break;
}
}
// Create and insert the processing instruction
$pi = $xml->createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="../foo.xsl"');
$xml->insertBefore($pi, $insertBefore);
echo $xml->saveXML();

Appending Nodes from a DOM Object to another DOM

Here is what i have as best i can figure what needs to be done. But im just at a loss as to how to get it to work.
Both $xml and $xmlfile are DOM objects/XML. $xml is the source of the data i want to append into $mxlfile.
$xml is sent in to the function from another function as a dom object.
This is how the xml comes to me.
<?xml version="1.0" encoding="utf-8"?>
<!-- Automatically generated data from EVE-Central.com -->
<!-- This is the new API :-) -->
<evec_api version="2.0" method="marketstat_xml">
<marketstat>
<type id="18">
<all>
<volume>1934078988.00</volume>
<avg>98.31</avg>
<max>26721.00</max>
<min>0.53</min>
<stddev>1339.78</stddev>
<median>26.31</median>
<percentile>0.00</percentile>
</all>
<buy>
<volume>1894081100.00</volume>
<avg>20.77</avg>
<max>31.31</max>
<min>0.53</min>
<stddev>7.25</stddev>
<median>26.00</median>
<percentile>28.17</percentile>
</buy>
<sell>
<volume>39997888.00</volume>
<avg>34.32</avg>
<max>26721.00</max>
<min>4.01</min>
<stddev>2339.28</stddev>
<median>29.80</median>
<percentile>26.76</percentile>
</sell>
</type>
<type id="ectera">
</type>
</marketstat>
</evec_api>
And here the part of my function i just cant get working.
$xml->preserveWhiteSpace = FALSE;
$xml->formatOutput = TRUE;
if(is_file($file)){
$xmlfile = new DOMDocument;
$xmlfile->load($file);
$dst = $xmlfile->getElementsByTagName('marketStat');
foreach($xml->getElementsByTagName('type') as $child){
$dst->appendChild($xmlfile->importNode($child));}
$xmlfile->save($file);
}
I know there's something wrong, but im just teaching myself xml and dom, so help would be much appreciated.
<?php
if(is_file($file))
{
$xmlfile = new DOMDocument;
$xml->preserveWhiteSpace = FALSE;
$xml->formatOutput = TRUE;
$xmlfile->load($file);
$dst = $xmlfile->getElementsByTagName('marketStat');
$parentLength = $dst->length;
for($i=0; $i< $parentLength ; $i++)
{
foreach($xmlfile->getElementsByTagName('type') as $child)
{
$dst->item($i)->appendChild($child);
}
}
$xmlfile->save($file);
}
Your code has two mistakes
You can set options for DOM parser after you create the document.
You can not use appendChild on list of DOM object. You have to add them on indivisual DOM element one by one.

How do I remove a specific node using its attribute value in PHP XML Dom?

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);

Categories