XML encode/decode class in PHP - 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.

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.

getting xml node with php

Im attempting to echo/assign a variable to the contents of the node "code" which is inside status;
I can get request-id just fine...
Any ideas people?
<?
$responseXML = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<payment xmlns="http://www.example.com" self="http://www.example.com">
<merchant-account-id ref="http://www.example.com">0000</merchant-account-id>
<transaction-id>0000</transaction-id>
<request-id>0000</request-id>
<transaction-type>auth</transaction-type>
<transaction-state>success</transaction-state>
<completion-time-stamp>2015-12-28T17:39:25.000Z</completion-time-stamp>
<statuses>
<status code="201.0000" description="3d-acquirer:The resource was successfully created." severity="information"/>
</statuses>
<avs-code>P</avs-code>
<requested-amount currency="GBP">0.01</requested-amount>
<account-holder>
<first-name>test</first-name>
<last-name>test</last-name>
<email>test.test#hotmail.co.uk</email>
<phone>00000000000</phone>
<address>
<street1>test</street1>
<city>test test</city>
<state>test</state>
<country>GB</country>
</address>
</account-holder>
<card-token>
<token-id>000</token-id>
<masked-account-number>000000******0000</masked-account-number>
</card-token>
<ip-address>192.168.0.1</ip-address>
<descriptor></descriptor>
<authorization-code>000000</authorization-code>
<api-id>000-000</api-id>
</payment>';
$doc = new DOMDocument;
$doc->loadXML($responseXML);
echo $doc->getElementsByTagName('request-id')->item(0)->nodeValue;
echo $doc->getElementsByTagName('status code')->item(0)->nodeValue;
?>
I've tried simplexml looad string, but pulling hair out with this one, can anybody shed some light, speed of getting this info out in one process is quite important so not to stress the webserver out!
Many thanks.
Using DOM is a good idea, but the API methods are a little cumbersome. Using Xpath makes it a lot easier.
Xpath allows you to use expressions to fetch node lists or scalar values from a DOM:
$document = new DOMDocument;
$document->loadXML($responseXML);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('example', 'http://www.example.com');
echo $xpath->evaluate('string(//example:request-id)'), "\n";
echo $xpath->evaluate('string(//example:status/#code)');
Output:
0000
201.0000
Xpath does not have a default namespace so if you XML has a namespace (like your example) you need to register a prefix for it and use it.
As code is an attribute of xml tag status, doing
getElementsByTagName('status code')
is wrong.
There's a special method for getting attribute value getAttribute:
echo $doc->getElementsByTagName('status')->item(0)->getAttribute('code');
Using XPath allows to access the status node very precisely.
DOMDocument + XPath:
$responseXML = '...';
$doc = new DOMDocument();
$doc->loadXML($responseXML);
$xp = new DOMXpath($doc);
$xp->registerNamespace('example', 'http://www.example.com');
// Every status node.
$statusNodes = $xp->query('//example:status');
// or a very specific one.
$statusNodes = $xp->query('/example:payment/example:statuses/example:status');
$statusNode = $statusNodes[0];
$code = $statusNode->getAttribute('code');
// $code is '201.0000'.
// To change the 'code' value.
$statusNode->setAttribute('code', '302.0000');

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.

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

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.

Getting SimpleXMLElement to include the encoding in output

This:
$XML = new SimpleXMLElement("<foo />");
echo($XML->asXML());
...outputs this:
<?xml version="1.0"?>
<foo/>
But I want it to output the encoding, too:
<?xml version="1.0" encoding="UTF-8"?>
<foo/>
Is there some way to tell SimpleXMLElement to include the encoding attribute of the <?xml?> tag? Aside from doing this:
$XML = new SimpleXMLElement("<?xml version='1.0' encoding='utf-8'?><foo />");
echo($XML->asXML());
Which works, but it's annoying to have to manually specify the version and encoding.
Assume for the purposes of this question that I cannot use DOMDocument instead.
You can try this, but you must use simplexml_load_string for $xml
$xml // Your main SimpleXMLElement
$xml->addAttribute('encoding', 'UTF-8');
Or you can still use other means to add the encoding to your output.
Simple Replacement
$outputXML=str_replace('<?xml version="1.0"?>', '<?xml version="1.0" encoding="UTF-8"?>', $outputXML);
Regular Expressions
$outputXML=preg_replace('/<\?\s*xml([^\s]*)\?>/' '<?xml $1 encoding="UTF-8"?>', $outputXML);
DOMDocument - I know you said you don't want to use DOMDocument, but here is an example
$xml = dom_import_simplexml($simpleXML);
$xml->xmlEncoding = 'UTF-8';
$outputXML = $xml->saveXML();
You can wrap this code into a function that receives a parameter $encoding and adds it to the
Simple and clear only do this
$XMLRoot = new SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><element></element>');
OutPut
<?xml version="1.0" encoding="UTF-8"?>
<element></element>
to add attributes in element only use
$XMLRoot->addAttribute('name','juan');
to add child use
$childElement = $XMLRoot->addChild('elementChild');
$childElement->addAttribute('attribName','somthing');
The DOMDoc proposal of Cristian Toma seems a good approach if the document isn't too heavy. You could wrap it up in something like this:
private function changeEncoding(string $xml, string $encoding) {
$dom = new \DOMDocument();
$dom->loadXML($xml);
$dom->encoding = $encoding;
return $dom->saveXML();
}
Comes in useful when you don't have access to the serializer producing the xml.
I would say you will need to do this on creation of each XML object. Even if SimpleXMLElement had a way of setting it you would still need to set it as I guess it would be possible for the object to pick a valid default.
Maybe create a constant and Create objects like this
$XML = new SimpleXMLElement($XMLNamespace . "<foo />");
echo($XML->asXML());
If you don't specify an encoding, SimpleXML cannot (sanely) guess which one you intended.

Categories