Format output of $SimpleXML->asXML(); [duplicate] - php

This question already has answers here:
PHP simpleXML how to save the file in a formatted way?
(4 answers)
Closed 5 years ago.
The title pretty much says it all.
If I have something like (from PHP site examples):
$xmlstr = <<<XML
<?xml version='1.0' standalone='yes'?>
<movies></movies>
XML;
$sxe = new SimpleXMLElement($xmlstr);
$sxe->addAttribute('type', 'documentary');
$movie = $sxe->addChild('movie');
$movie->addChild('title', 'PHP2: More Parser Stories');
$movie->addChild('plot', 'This is all about the people who make it work.');
$characters = $movie->addChild('characters');
$character = $characters->addChild('character');
$character->addChild('name', 'Mr. Parser');
$character->addChild('actor', 'John Doe');
$rating = $movie->addChild('rating', '5');
$rating->addAttribute('type', 'stars');
echo("<pre>".htmlspecialchars($sxe->asXML())."</pre>");
die();
I end up outputing a long string like so:
<?xml version="1.0" standalone="yes"?>
<movies type="documentary"><movie><title>PHP2: More Parser Stories</title><plot>This is all about the people who make it work.</plot><characters><character><name>Mr. Parser</name><actor>John Doe</actor></character></characters><rating type="stars">5</rating></movie></movies>
This is fine for a program consumer, but for debugging/human tasks, does anyone know how to get this into a nice indented format?

There's a variety of solutions in the comments on the PHP manual page for SimpleXMLElement. Not very efficient, but certainly terse, is a solution by Anonymous
$dom = dom_import_simplexml($simpleXml)->ownerDocument;
$dom->formatOutput = true;
echo $dom->saveXML();
The PHP manual page comments are often good sources for common needs, as long as you filter out the patently wrong stuff first.

The above didn't work for me, I found this worked:
$dom = new DOMDocument("1.0");
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($simpleXml->asXML());
echo $dom->saveXML();

Found a similar solution...to format raw xlm data..from my php SOAP requests __getLastRequest & __getLastResponse, for quick debugging the xml's i have combined it with google-code-prettify.
Its a good solution if you want to format sensitive xml data and don't want to do it online.
Some sample code below, may be helpful to others:
$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($data); //=====$data has the raw xml data...you want to format
echo '<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>';
echo "<br/> <pre class=\"prettyprint\" >". htmlentities($dom->saveXML())."</pre>";
Below is a sample of the Formatted XML Output I got:
Note: The formatted XML is available in $dom->saveXML() and can be directly saved to a xml file using php file write.

Related

Convert array to XML with both tags on empty value [duplicate]

I want to generate xml by using php simplexml.
$xml = new SimpleXMLElement('<xml/>');
$output = $xml->addChild('child1');
$output->addChild('child2', "value");
$output->addChild('noValue', '');
Header('Content-type: text/xml');
print($xml->asXML());
The output is
<xml>
<child1>
<child2>value</child2>
<noValue/>
</child1>
</xml>
What I want is if the tag has no value it should display like this
<noValue></noValue>
I've tried using LIBXML_NOEMPTYTAG from Turn OFF self-closing tags in SimpleXML for PHP?
I've tried $xml = new SimpleXMLElement('<xml/>', LIBXML_NOEMPTYTAG); and it doesn't work. So I don't know where to put the LIBXML_NOEMPTYTAG
LIBXML_NOEMPTYTAG does not work with simplexml, per the spec:
This option is currently just available in the DOMDocument::save and DOMDocument::saveXML functions.
To achieve what you're after, you need to convert the simplexml object to a DOMDocument object:
$xml = new SimpleXMLElement('<xml/>');
$child1 = $xml->addChild('child1');
$child1->addChild('child2', "value");
$child1->addChild('noValue', '');
$dom_sxe = dom_import_simplexml($xml); // Returns a DomElement object
$dom_output = new DOMDocument('1.0');
$dom_output->formatOutput = true;
$dom_sxe = $dom_output->importNode($dom_sxe, true);
$dom_sxe = $dom_output->appendChild($dom_sxe);
echo $dom_output->saveXML($dom_output, LIBXML_NOEMPTYTAG);
which returns:
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<child1>
<child2>value</child2>
<noValue></noValue>
</child1>
</xml>
Something worth pointing out... the likely reason that the NOEMPTYTAG option is available for DOMDocument and not simplexml is that empty elements are not considered valid XML, while the DOM specification allows for them. You are banging your head against the wall to get invalid XML, which may suggest that the valid self-closing empty element would work just as well.
Despite the quite lengthy answers already given - which are not particularly wrong and do shed some light into some libxml library internals and it's PHP binding - you're most likely looking for:
$output->noValue = '';
To add an open tag, empty node-value and end tag (beautified, demo is here: http://3v4l.org/S2PKc):
<?xml version="1.0"?>
<xml>
<child1>
<child2>value</child2>
<noValue></noValue>
</child1>
</xml>
Just noting as it seems it has been overlooked with the existing answers.
Since Simple XML is proving troublesome, perhaps XMLWriter could do what you want.
Here's a fiddle
<?php
$oXMLWriter = new XMLWriter;
$oXMLWriter->openMemory();
$oXMLWriter->startDocument('1.0', 'UTF-8');
$oXMLWriter->startElement('xml');
$oXMLWriter->writeElement('child1', 'Hello world!!');
$oXMLWriter->writeElement('noValue', '');
$oXMLWriter->endElement();
$oXMLWriter->endDocument();
echo htmlentities($oXMLWriter->outputMemory(TRUE));
?>
Output:
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<child1>Hello world!!</child1>
<noValue></noValue>
</xml>
Extending from my comments (some got deleted):
The behavior depends on your environment. This same code:
$xml = new SimpleXMLElement('<xml/>', LIBXML_NOEMPTYTAG);
$output = $xml->addChild('child1');
$output->addChild('child2', "value");
$output->addChild('noValue', '');
echo $xml->asXML();
In 3v4l.org and Ideone.com, it produces self-closing tag;
In eval.in, codepad.org and on my localhost (PHP 5.3/5.4, libXML 2.7, Windows), it produces empty tag.
Since according to PHP document, LIBXML_NOEMPTYTAG only (guarenteed to) work in DOM, you may want to try DOM for ensurance:
$dom=new DOMDocument("1.0","UTF-8");
$dom->formatOutput=true;
$root=$dom->createElement("xml");
$dom->appendChild($root);
$child1=$dom->createElement("child1");
$root->appendChild($child1);
$node=$dom->createElement("child2");
$node->appendChild($dom->createTextNode("value"));
$child1->appendChild($node);
$node=$dom->createElement("noValue");
$child1->appendChild($node);
echo $dom->saveXML($dom,LIBXML_NOEMPTYTAG);
3v4l.org demo.
Edit:
All the above online demo site uses Content-Type: text/plain by default. If you want to directly output XML to browser as a standalone resource, you can specify the header before output:
header("Content-Type: text/xml");
echo $dom->saveXML($dom,LIBXML_NOEMPTYTAG);
I had to get the child node and asign to the nodeValue property an empty string, and the xml close tag appears.
$output->childNodes[1]->nodeValue = '';
Greetings.

php export xml CDATA escaped

I am trying to export xml with CDATA tags. I use the following code:
$xml_product = $xml_products->addChild('product');
$xml_product->addChild('mychild', htmlentities("<![CDATA[" . $mytext . "]]>"));
The problem is that I get CDATA tags < and > escaped with < and > like following:
<mychild><![CDATA[My some long long long text]]></mychild>
but I need:
<mychild><![CDATA[My some long long long text]]></mychild>
If I use htmlentities() I get lots of errors like tag raquo is not defined etc... though there are no any such tags in my text. Probably htmlentities() tries to parse my text inside CDATA and convert it, but I dont want it either.
Any ideas how to fix that? Thank you.
UPD_1 My function which saves xml to file:
public static function saveFormattedXmlFile($simpleXMLElement, $output_file) {
$dom = new DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML(urldecode($simpleXMLElement->asXML()));
$dom->save($output_file);
}
A short example of how to add a CData section, note the way it skips into using DOMDocument to add the CData section in. The code builds up a <product> element, $xml_product has a new element <mychild> created in it. This newNode is then imported into a DOMElement using dom_import_simplexml. It then uses the DOMDocument createCDATASection method to properly create the appropriate bit and adds it back into the node.
$xml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><Products />');
$xml_product = $xml->addChild('product');
$newNode = $xml_product->addChild('mychild');
$mytext = "<html></html>";
$node = dom_import_simplexml($newNode);
$cdata = $node->ownerDocument->createCDATASection($mytext);
$node->appendChild($cdata);
echo $xml->asXML();
This example outputs...
<?xml version="1.0" encoding="UTF-8"?>
<Products><product><mychild><![CDATA[<html></html>]]></mychild></product></Products>

How to prevent self closing tag in php simplexml

I want to generate xml by using php simplexml.
$xml = new SimpleXMLElement('<xml/>');
$output = $xml->addChild('child1');
$output->addChild('child2', "value");
$output->addChild('noValue', '');
Header('Content-type: text/xml');
print($xml->asXML());
The output is
<xml>
<child1>
<child2>value</child2>
<noValue/>
</child1>
</xml>
What I want is if the tag has no value it should display like this
<noValue></noValue>
I've tried using LIBXML_NOEMPTYTAG from Turn OFF self-closing tags in SimpleXML for PHP?
I've tried $xml = new SimpleXMLElement('<xml/>', LIBXML_NOEMPTYTAG); and it doesn't work. So I don't know where to put the LIBXML_NOEMPTYTAG
LIBXML_NOEMPTYTAG does not work with simplexml, per the spec:
This option is currently just available in the DOMDocument::save and DOMDocument::saveXML functions.
To achieve what you're after, you need to convert the simplexml object to a DOMDocument object:
$xml = new SimpleXMLElement('<xml/>');
$child1 = $xml->addChild('child1');
$child1->addChild('child2', "value");
$child1->addChild('noValue', '');
$dom_sxe = dom_import_simplexml($xml); // Returns a DomElement object
$dom_output = new DOMDocument('1.0');
$dom_output->formatOutput = true;
$dom_sxe = $dom_output->importNode($dom_sxe, true);
$dom_sxe = $dom_output->appendChild($dom_sxe);
echo $dom_output->saveXML($dom_output, LIBXML_NOEMPTYTAG);
which returns:
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<child1>
<child2>value</child2>
<noValue></noValue>
</child1>
</xml>
Something worth pointing out... the likely reason that the NOEMPTYTAG option is available for DOMDocument and not simplexml is that empty elements are not considered valid XML, while the DOM specification allows for them. You are banging your head against the wall to get invalid XML, which may suggest that the valid self-closing empty element would work just as well.
Despite the quite lengthy answers already given - which are not particularly wrong and do shed some light into some libxml library internals and it's PHP binding - you're most likely looking for:
$output->noValue = '';
To add an open tag, empty node-value and end tag (beautified, demo is here: http://3v4l.org/S2PKc):
<?xml version="1.0"?>
<xml>
<child1>
<child2>value</child2>
<noValue></noValue>
</child1>
</xml>
Just noting as it seems it has been overlooked with the existing answers.
Since Simple XML is proving troublesome, perhaps XMLWriter could do what you want.
Here's a fiddle
<?php
$oXMLWriter = new XMLWriter;
$oXMLWriter->openMemory();
$oXMLWriter->startDocument('1.0', 'UTF-8');
$oXMLWriter->startElement('xml');
$oXMLWriter->writeElement('child1', 'Hello world!!');
$oXMLWriter->writeElement('noValue', '');
$oXMLWriter->endElement();
$oXMLWriter->endDocument();
echo htmlentities($oXMLWriter->outputMemory(TRUE));
?>
Output:
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<child1>Hello world!!</child1>
<noValue></noValue>
</xml>
Extending from my comments (some got deleted):
The behavior depends on your environment. This same code:
$xml = new SimpleXMLElement('<xml/>', LIBXML_NOEMPTYTAG);
$output = $xml->addChild('child1');
$output->addChild('child2', "value");
$output->addChild('noValue', '');
echo $xml->asXML();
In 3v4l.org and Ideone.com, it produces self-closing tag;
In eval.in, codepad.org and on my localhost (PHP 5.3/5.4, libXML 2.7, Windows), it produces empty tag.
Since according to PHP document, LIBXML_NOEMPTYTAG only (guarenteed to) work in DOM, you may want to try DOM for ensurance:
$dom=new DOMDocument("1.0","UTF-8");
$dom->formatOutput=true;
$root=$dom->createElement("xml");
$dom->appendChild($root);
$child1=$dom->createElement("child1");
$root->appendChild($child1);
$node=$dom->createElement("child2");
$node->appendChild($dom->createTextNode("value"));
$child1->appendChild($node);
$node=$dom->createElement("noValue");
$child1->appendChild($node);
echo $dom->saveXML($dom,LIBXML_NOEMPTYTAG);
3v4l.org demo.
Edit:
All the above online demo site uses Content-Type: text/plain by default. If you want to directly output XML to browser as a standalone resource, you can specify the header before output:
header("Content-Type: text/xml");
echo $dom->saveXML($dom,LIBXML_NOEMPTYTAG);
I had to get the child node and asign to the nodeValue property an empty string, and the xml close tag appears.
$output->childNodes[1]->nodeValue = '';
Greetings.

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.

XML encode/decode class in PHP

Can anybody suggest a good and stable class to encode and decode XML in PHP?
edit
found this example on php.net, but can't make it work.. no error msgs is returned
$sxe = new SimpleXMLElement();
$sxe->addAttribute('type', 'documentary');
$movie = $sxe->addChild('movie');
$movie->addChild('title', 'PHP2: More Parser Stories');
$movie->addChild('plot', 'This is all about the people who make it work.');
$characters = $movie->addChild('characters');
$character = $characters->addChild('character');
$character->addChild('name', 'Mr. Parser');
$character->addChild('actor', 'John Doe');
$rating = $movie->addChild('rating', '5');
$rating->addAttribute('type', 'stars');
echo $sxe->asXML();
SimpleXML would probably be the easiest. Example:
<root>
<node>
<sub>Text</sub>
</node>
</root>
$xml = new SimpleXMLElement('xml_file.xml', 0, true);
echo $xml->node->sub; // Displays "Text"
Edit:
In response to your code that isn't working, you need to include a root node in the initiation of the class:
$sxe = new SimpleXMLElement('<root />');
By encode/decode do you mean write/read? XML is not code, just markup. There is something called character/entity encoding for XML, though.
PHP has basically two class or libraries to work with. SimpleXML and DOMElement. You'll have to check your phpinfo to see if they are enabled and php.ini to enable them.

Categories