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.
Related
I know there are many examples of creating cdata with php but I have not found one that helps in my situation. I need to create an xml file that will be used by something other than php. I need to create cdata in the xml that will contain a function to be used. The final xml file should look as follows.
<?xml version="1.0" encoding="utf-8" ?>
<component name="Test" extends="out" >
<script type="text" >
<![CDATA[
function init()
m.content = createObject("RoSGNode","ContentNode")
m.top.setFocus(true)
dateNow = CreateObject("roDateTime")
dateNow = dateNow.asSeconds() - 2000
addItemName($Iname)
end function
]]>
</script>
</component>
Code to create the xml. I just don't know how to create cdata info. Any help would be greatly appreciated.
$xml=new DOMDocument('1.0', 'UTF-8');
$xml->preserveWhiteSpace = false;
$xml->formatOutput = true;
$components = $xml->createElement("components");
$name=$xml->createAttribute("name");
$name->value = "Test";
$extends=$xml->createAttribute("extends");
$extends->value = "out";
$components->appendChild($name);
$components->appendChild($extends);
$script = $xml->createElement("script");
$type=$xml->createAttribute("type");
$type->value = "text";
$script->appendChild($type);
$components->appendChild($script);
$xml->appendChild($components);
$xml->save($filename2);
After little more effort, was able to get cdata in.
$cdata = $xml->createCDATASection("function init()");
$script->appendChild($cdata);
What I tried and what doesn't work:
Input:
$d = new DOMDocument();
$d->formatOutput = true;
// Out of my control:
$someEl = $d->createElementNS('http://example.com/a', 'a:some');
// Under my control:
$envelopeEl = $d->createElementNS('http://example.com/default',
'envelope');
$d->appendChild($envelopeEl);
$envelopeEl->appendChild($someEl);
echo $d->saveXML();
$someEl->prefix = null;
echo $d->saveXML();
Output is invalid XML after substitution:
<?xml version="1.0"?>
<envelope xmlns="http://example.com/default">
<a:some xmlns:a="http://example.com/a"/>
</envelope>
<?xml version="1.0"?>
<envelope xmlns="http://example.com/default">
<:some xmlns:a="http://example.com/a" xmlns:="http://example.com/a"/>
</envelope>
Note that <a:some> may have children. One solution would be
to create a new <some>, and copy all children from <a:some> to <some>. Is
that the way to go?
This is really an interesting question. My first intention was to clone the <a:some> node, remove the xmlns:a attribute, remove the <a:some> and insert the clone - <a>. But this will not work, as PHP does not allow to remove the xmlns:a attribute like any regular attribute.
After some struggling with DOM methods of PHP I started to google the problem. I found this comment in the PHP documentation on this. The user suggest to write a function that clones the node manually without it's namespace:
<?php
/**
* This function is based on a comment to the PHP documentation.
* See: http://www.php.net/manual/de/domnode.clonenode.php#90559
*/
function cloneNode($node, $doc){
$unprefixedName = preg_replace('/.*:/', '', $node->nodeName);
$nd = $doc->createElement($unprefixedName);
foreach ($node->attributes as $value)
$nd->setAttribute($value->nodeName, $value->value);
if (!$node->childNodes)
return $nd;
foreach($node->childNodes as $child) {
if($child->nodeName == "#text")
$nd->appendChild($doc->createTextNode($child->nodeValue));
else
$nd->appendChild(cloneNode($child, $doc));
}
return $nd;
}
Using it would lead to a code like this:
$xml = '<?xml version="1.0"?>
<envelope xmlns="http://example.com/default">
<a:some xmlns:a="http://example.com/a"/>
</envelope>';
$doc = new DOMDocument();
$doc->loadXML($xml);
$elements = $doc->getElementsByTagNameNS('http://example.com/a', 'some');
$original = $elements->item(0);
$clone = cloneNode($original, $doc);
$doc->documentElement->replaceChild($clone, $original);
$doc->formatOutput = TRUE;
echo $doc->saveXML();
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();
}
?>
XML:
<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<pages>
<page><title>Home</title><content>Lorem Ipsum</content></page>
<page><title>Pictures</title><content>Lorem Ipsum</content></page>
<page><title>Information</title><content>Lorem Ipsum</content></page>
</pages>
<css>
<css-tag><title>background-color</title><value>#FFF</value></css-tag>
</css>
<layout>1</layout>
</root>
PHP:
$title = $_GET['0'];
$xml = new DOMDocument('1.0', 'ISO-8859-1');
$xml->formatOutput = true;
$xml->preserveWhiteSpace = true;
$xml->load($location);
$pages = $xml->getElementsByTagName("page");
foreach($pages as $page){
$pagetitle = $page->getElementsByTagName("title");
$pagetitlevalue = $pagetitle->item(0)->nodeValue;
if($title == $pagetitlevalue){
$pagetitle->item(0)->parentNode->removeChild($pagetitle->item(0));
}
}
$xml->save($location);
This code gets rid of just the <Title> node, how can it be changed to get rid of the parent <Page> node?
I can't figure out how to do this, I just manage to get rid of the title node and get loads of error codes
Whilst this is something I'd probably use xpath for, you can find...
$pagetitle->item(0)->parentNode->removeChild($pagetitle->item(0));
and replace with...
$pagetitle->item(0)->parentNode->parentNode->removeChild($pagetitle->item(0)->parentNode);
to go one level higher in your XML tree
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);