php DOMElement appendChild unwanted nesting - php

I want a list of xml node in a given xml node. The code sample below should do it? (but doesn't)
$doc= new DOMDocument("1.0", "UTF-8");
$rootel = $doc->createElement("drobj");
$el1 = $doc->createElement("el1");
$rootel->appendChild($el1);
$el2 = $doc->createElement("el2");
$rootel->appendChild($el2);
$doc->appendChild($rootel);
$doc->saveXML();
Result is
<drobj>
<el1>
<el2></el2>
</el1>
</drobj>
I expected
<drobj>
<el1></el1>
<el2></el2>
</drobj>

$doc= new DOMDocument("1.0", "UTF-8");
$rootel = $doc->createElement("drobj");
$el1 = $doc->createElement("el1");
$rootel->appendChild($el1);
$el2 = $doc->createElement("el2");
$rootel->appendChild($el2);
$doc->appendChild($rootel);
$doc->save("test.xml");
This works perfectly fine. I tested it out.
The only thing i changed was the last line to save the xml to a file.

Related

How to append XML data without overwriting?

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.

DOMDocument loses some code near php opening tag

I like code element to keep the whole node value. But it loses the part that is: '<?php $this->'. Probably it gets that part as html tag. I do not know.
See the snippet:
<?php
$text = '<div><code><?php $this->goal = \'żźćłóąę\';</code></div>';
$dom = new \DOMDocument();
$dom->loadHTML(mb_convert_encoding($text, 'HTML-ENTITIES', "UTF-8"));
$codeElements = $dom->getElementsByTagName('code');
foreach($codeElements as $elem)
var_dump($elem->nodeValue);
The result is:
`string(24) "goal = 'żźćłóąę';"`
but should be
string(37) "<?php $this->goal = 'żźćłóąę';"
How to achive this?
online: https://3v4l.org/d6DFm
Thank you
edit
After some comments my question needs some explanation.
This
<?php $this->goal = 'żźćłóąę';
must not be parsed by php interpreter. This must be displayed as a code presentation. The whole text, whole row must be displayed as string. The opening php tag, the variable and the arrow too.
Try like this
<?php
$text = '<div><code><?php $this->goal = \'żźćłóąę\';</code></div>';
$text = str_replace("<?","<?",$text);
$dom = new \DOMDocument();
$dom->loadHTML(mb_convert_encoding($text, 'HTML-ENTITIES', "UTF-8"));
$codeElements = $dom->getElementsByTagName('code');
foreach($codeElements as $elem)
var_dump($elem->nodeValue);

php parsing xml formatted data

I'm trying to parse an xml data that I'm getting via an api call. I can use file_get_contents to read into a string but simpleXML_load_string seems to fail to read it. I can save it to a file and then simpleXML_load_file works. But I would rather not write the contents to a file. I can't seem to understand how to use DOM or XMLParse with this either. I'm new to PHP and parsing XML. The output data from the api call is below.
<Search>
<DS_Rating>DS3</DS_Rating>
<Overall>17.5</Overall>
<LargestGiftLow>0</LargestGiftLow>
<LargestGiftHigh>0</LargestGiftHigh>
<EstimatedCapacityRange>I - $15,000 - $24,999</EstimatedCapacityRange>
<EstimatedCapacity>20452</EstimatedCapacity>
<RealEstateEst>270073</RealEstateEst>
<RealEstateCount>1</RealEstateCount>
<LikelyMatchesCount>0</LikelyMatchesCount>
<LikelyMatchesTotal>0</LikelyMatchesTotal>
<FndBoard></FndBoard>
<GSBoard></GSBoard>
<PoliticalLikelyCount>0</PoliticalLikelyCount>
<PoliticalLikelyTotal>0</PoliticalLikelyTotal>
<BusinessRevenues>0</BusinessRevenues>
<SECStockValue>0</SECStockValue>
<SECInsider></SECInsider>
<MarketGuide></MarketGuide>
<IRS990PF></IRS990PF>
<RealEstateTrust></RealEstateTrust>
<MarketGuideComp>0</MarketGuideComp>
<MarketGuideOptions>0</MarketGuideOptions>
<BusinessAffiliation></BusinessAffiliation>
<Pension></Pension>
<PensionAssets>0</PensionAssets>
<CorpTech></CorpTech>
<Pilot></Pilot>
<AirplaneOwner></AirplaneOwner>
<Boat></Boat>
<submit_time>2014-03-11 15:48:45</submit_time>
</Search>
Figured out that the issue was that what I was seeing in the browser was actually a php output with html_entiity encoded. I was able to process it with the code below which let me load it with simplexml.
$rawxml = html_entity_decode($rawxml);
$rawxml = str_replace(array(' ', "<pre>"), '', $rawxml);
$rawxml = utf8_encode($rawxml);
$xml = simplexml_load_string($rawxml);
If you XML is in a file use
simplexml_load_file
if you have it in a string use
simplexml_load_string
Then you can use the following code to access it.
<?php
$yourxml = simplexml_load_file('your.xml');
echo $yourxml->search[0]->DS_Rating;
?>
This would then output
DS3
to the browser via the 'echo' in your code. I hope this points you in the correct direction.
Try to use this:
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8" ?>'.$yourXMLString);
In DOM you load the XML into a DOMDocument and create a DOMXpath instance for it.
$dom = new DOMDocument();
$dom->loadXml($xmlString);
//$dom->load($xmlFile);
$xpath = new DOMXpath($dom);
DOMXpath::evaluate() is used to fetch data from the DOM.
$rating = $dom->evaluate('string(/Search/DS_Rating)');
An Xpath expression like /Search/DS_rating always returns a node list. You can use foreach() to iterate it. The string() function in Xpath takes the first node from the list and casts it into a string. If here is not node in the list the result is an empty string.
$xmlString = <<<'XML'
<Search>
<DS_Rating>DS3</DS_Rating>
<Overall>17.5</Overall>
</Search>
XML;
$dom = new DOMDocument();
$dom->loadXml($xmlString);
$xpath = new DOMXpath($dom);
var_dump(
$xpath ->evaluate('string(/Search/DS_Rating)')
);
Output: https://eval.in/118921
string(3) "DS3"

PHP and XMLWriter

I was successfully using the following code to merge multiple large XML files into a new (larger) XML file. Found at least part of this on StackOverflow
$docList = new DOMDocument();
$root = $docList->createElement('documents');
$docList->appendChild($root);
$doc = new DOMDocument();
foreach(xmlFilenames as $xmlfilename) {
$doc->load($xmlfilename);
$xmlString = $doc->saveXML($doc->documentElement);
$xpath = new DOMXPath($doc);
$query = self::getQuery(); // this is the name of the ROOT element
$nodelist = $xpath->evaluate($query, $doc->documentElement);
if( $nodelist->length > 0 ) {
$node = $docList->importNode($nodelist->item(0), true);
$xmldownload = $docList->createElement('document');
if (self::getShowFileName())
$xmldownload->setAttribute("filename", $filename);
$xmldownload->appendChild($node);
$root->appendChild($xmldownload);
}
}
$newXMLFile = self::getNewXMLFile();
$docList->save($newXMLFile);
I started running into OUT OF MEMORY issues when the number of files grew as did the size of them.
I found an article here which explained the issue and recommended using XMLWriter
So, now trying to use PHP XMLWriter to merge multiple large XML files together into a new (larger) XML file. Later, I will execute xpath against the new file.
Code:
$xmlWriter = new XMLWriter();
$xmlWriter->openMemory();
$xmlWriter->openUri('mynewFile.xml');
$xmlWriter->setIndent(true);
$xmlWriter->startDocument('1.0', 'UTF-8');
$xmlWriter->startElement('documents');
$doc = new DOMDocument();
foreach($xmlfilenames as $xmlfilename)
{
$fileContents = file_get_contents($xmlfilename);
$xmlWriter->writeElement('document',$fileContents);
}
$xmlWriter->endElement();
$xmlWriter->endDocument();
$xmlWriter->flush();
Well, the resultant (new) xml file is no longer correct since elements are escaped - i.e.
<?xml version="1.0" encoding="UTF-8"?>
<CONFIRMOWNX>
<Confirm>
<LglVeh id="GLE">
<AddrLine1>GLEACHER &amp; COMPANY</AddrLine1>
<AddrLine2>DESCAP DIVISION</AddrLine2>
Can anyone explain how to take the content from the XML file and write them properly to new file?
I'm burnt on this and I KNOW it'll be something simple I'm missing.
Thanks.
Robert
See, the problem is that XMLWriter::writeElement is intended to, well, write a complete XML element. That's why it automatically sanitize (replace & with &, for example) the contents of what's been passed to it as the second param.
One possible solution is to use XMLWriter::writeRaw method instead, as it writes the contents as is - without any sanitizing. Obviously it doesn't validate its inputs, but in your case it does not seem to be a problem (as you're working with already checked source).
Hmm, Not sure why it's converting it to HTML Characters, but you can decode it like so
htmlspecialchars_decode($data);
It converts special HTML entities back to characters.

A question about saving a file in php

I've used the following code to do an XSLT in php:
# LOAD XML FILE
$XML = new DOMDocument();
$XML = simplexml_load_file("images/upload/source.xml");
# START XSLT
$xslt = new XSLTProcessor();
$XSL = new DOMDocument();
$XSL->load( 'xsl/transfer.xsl', LIBXML_NOCDATA);
$xslt->importStylesheet( $XSL );
#PRINT
print $XML->saveXML();
print $XML->save("newfile.xml") ;
The code is quite straightforward, we need to load the source xml file and then load up the stylesheet, and indeed it actually works.
The code that causes trouble is the last line:
print $XML->save("newfile.xml") ;
after running which I got error "Fatal error: Call to undefined method SimpleXMLElement::save() ". But, actually ,I was following a tutorial here:
http://devzone.zend.com/article/1713.
Maybe I screwed up something, could anybody give me a hint? thanks in advance.
Following your guys' advice, I modified the code like this:
# LOAD XML FILE
$XML = new DOMDocument();
$XML->load("images/upload/source.xml");
# START XSLT
$xslt = new XSLTProcessor();
$XSL = new DOMDocument();
$XSL->load( 'xsl/transfer.xsl', LIBXML_NOCDATA);
$xslt->importStylesheet( $XSL );
#PRINT
print $xslt->transformToXML( $XML );
now the correctly-transformed XML gets shown in the browser, I've tried some ways but still couldn't figure out how to print this result to a file instead of showing in the browser, any help is appreciated, thanks in advance.
You're changing how $XML is defined, simply call the load method on $XML instead of simplexml_load_file:
$XML = new DOMDocument();
$XML->load("images/upload/source.xml");
There's no reason at all to use simplexml since the XSLT processing is all done with DOMDocument. So just replace that one line, and you should be good to go...
$XML = new DOMDocument();
$XML = simplexml_load_file("images/upload/source.xml");
First you store a DOMDocument in $XML, and then you replace it with a SimpleXMLElement. DOMDocument does have a save method, but SimpleXMLElement does not.
Admission: didn't look at the tutorial, so I don't know why/if that one works.
$XML = new DOMDocument();
$XML = simplexml_load_file("images/upload/source.xml");
You're saying that $XML is a DOMDocument and then you replace it with a SimpleXMLElement on line 2
Use
$XML = new DOMDocument();
$XML->load("images/upload/source.xml");
instead
Problem:
$XML = new DOMDocument();
$XML = simplexml_load_file("images/upload/source.xml");
You create a DOMDocument, which you then overwrite with a SimpleXMLElement object. The first line is dead code. You aren't using it at all, since you overwrite it in the next statement.
save is a method in DOMDocument. asXML($file) is the equivalent for SimpleXML (or saveXML($file) which is an alias.
If you look at the tutorial, it's clearly:
$xsl = new DomDocument();
$xsl->load("articles.xsl");
$inputdom = new DomDocument();
$inputdom->load("articles.xml");
So, if you use simplexml_load_file, then you're not really following the tutorial.

Categories