Is there any way to convert json to xml in PHP? I know that xml to json is very much possible.
If you're willing to use the XML Serializer from PEAR, you can convert the JSON to a PHP object and then the PHP object to XML in two easy steps:
include("XML/Serializer.php");
function json_to_xml($json) {
$serializer = new XML_Serializer();
$obj = json_decode($json);
if ($serializer->serialize($obj)) {
return $serializer->getSerializedData();
}
else {
return null;
}
}
It depends on how exactly you want you XML to look like. I would try a combination of json_decode() and the PEAR::XML_Serializer (more info and examples on sitepoint.com).
require_once 'XML/Serializer.php';
$data = json_decode($json, true)
// An array of serializer options
$serializer_options = array (
'addDecl' => TRUE,
'encoding' => 'ISO-8859-1',
'indent' => ' ',
'rootName' => 'json',
'mode' => 'simplexml'
);
$Serializer = &new XML_Serializer($serializer_options);
$status = $Serializer->serialize($data);
if (PEAR::isError($status)) die($status->getMessage());
echo '<pre>';
echo htmlspecialchars($Serializer->getSerializedData());
echo '</pre>';
(Untested code - but you get the idea)
Crack open the JSON with json_decode, and traverse it to generate whatever XML you want.
In case you're wondering, there is no canonical mapping between JSON and XML, so you have to write the XML-generation code yourself, based on the needs of your application.
I combined the two earlier suggestions into:
/**
* Convert JSON to XML
* #param string - json
* #return string - XML
*/
function json_to_xml($json)
{
include_once("XML/Serializer.php");
$options = array (
'addDecl' => TRUE,
'encoding' => 'UTF-8',
'indent' => ' ',
'rootName' => 'json',
'mode' => 'simplexml'
);
$serializer = new XML_Serializer($options);
$obj = json_decode($json);
if ($serializer->serialize($obj)) {
return $serializer->getSerializedData();
} else {
return null;
}
}
A native approch might be
function json_to_xml($obj){
$str = "";
if(is_null($obj))
return "<null/>";
elseif(is_array($obj)) {
//a list is a hash with 'simple' incremental keys
$is_list = array_keys($obj) == array_keys(array_values($obj));
if(!$is_list) {
$str.= "<hash>";
foreach($obj as $k=>$v)
$str.="<item key=\"$k\">".json_to_xml($v)."</item>".CRLF;
$str .= "</hash>";
} else {
$str.= "<list>";
foreach($obj as $v)
$str.="<item>".json_to_xml($v)."</item>".CRLF;
$str .= "</list>";
}
return $str;
} elseif(is_string($obj)) {
return htmlspecialchars($obj) != $obj ? "<![CDATA[$obj]]>" : $obj;
} elseif(is_scalar($obj))
return $obj;
else
throw new Exception("Unsupported type $obj");
}
Another option would be to use a JSON streaming parser.
Using a streamer parser would come in handy if you want to bypass the intermediate object graph created by PHP when using json_decode. For instance, when you got a large JSON document and memory is an issue, you could output the XML with XMLWriter directly while reading the document with the streaming parser.
One example would be https://github.com/salsify/jsonstreamingparser
$writer = new XMLWriter;
$xml->openURI('file.xml');
$listener = new JSON2XML($writer); // you need to write the JSON2XML listener
$stream = fopen('doc.json', 'r');
try {
$parser = new JsonStreamingParser_Parser($stream, $listener);
$parser->parse();
} catch (Exception $e) {
fclose($stream);
throw $e;
}
The JSON2XML Listener would need to implement the Listener interface:
interface JsonStreamingParser_Listener
{
public function start_document();
public function end_document();
public function start_object();
public function end_object();
public function start_array();
public function end_array();
public function key($key);
public function value($value);
}
At runtime, the listener will receive the various events from the parser, e.g. when the parser finds an object, it will send the data to the start_object() method. When it finds an array, it will trigger start_array() and so on. In those methods you'd then delegate the values to the appropriate methods in the XMLWriter, e.g. start_element() and so on.
Disclaimer: I am not affiliated with the author, nor have I used the tool before. I picked this library because the API looked sufficiently simple to illustrate how to use an event based JSON parser.
Related
So, after doing some studying I have successfully managed to parse some XML that I'm getting via Guzzle via simplexml_load_string. The issue is then when I then subsequently try to dispatch a job for each of the children using the following code I get a "Serialization of 'SimpleXMLElement' is not allowed" error.
$parent = $this->getParent($keywords);
foreach ($parent->children() as $child) {
dispatch(new ProcessChild($event, true), $this->otherVar);
}
So to try and fix this I can use the following trick to convert the XML into an array;
json_decode(json_encode($child))
however, while this does mean I can send the data to the new job, it does mean that, as far as I can work out, I have no way to access the #attributes. An alternative would be something along the lines of the following;
// ParentJob
$parent = $this->getParent($keywords);
foreach ($parent->children() as $child) {
dispatch(new ProcessChild($child->asXML, true), $this->otherVar);
}
// ChildJob
public function __construct($xml, $otherVar)
{
$this->xml = simplexml_load_string($xml);
$this->otherVar = $otherVar;
}
however it still throws a serialization error on the dispatch for some reason that I cannot work out, since it sould only be sending raw XML and not an object.
So my main question is what would be the correct way to pass and child SimpleXMLObject to a job in Laravel 5.3 ?
(short of something like looping through all the nodes/attributes and building my own collection from them)
Converting the XML into JSON that way means loosing data. I suggest keeping the XML if possible.
SimpleXMLElement::asXML() is a method. Do not forget the brackets.
$parent = $this->getParent($keywords);
foreach ($parent->children() as $child) {
dispatch(new ProcessChild($child->asXML(), true), $this->otherVar);
}
Calling it as a property means that SimpleXML tries to interpret it as a child element node. This means it will be an (empty) SimpleXMLElement.
Here is a small example showing the behavior:
$node = new SimpleXMLElement('<foo/>');
var_dump($node->asXml);
var_dump($node->asXml->getName());
var_dump($node->asXml());
Output:
object(SimpleXMLElement)#2 (0) {
}
string(0) ""
string(29) "<?xml version="1.0"?>
<foo/>
"
The SimpleXmlElement can be converted to array as follows:
$xml = <<<'XML'
<root>
<x a="a1">1</x>
<y b="b2">2</y>
<z>3</z>
</root>
XML;
$xe = simplexml_load_string($xml);
$a = $xe->xpath('*');
$a = array_map(function ($e) {
$item = (array) $e;
$item['nodeName'] = $e->getName();
return $item;
}, $a);
// Now `$a` is an array (serializable object)
echo json_encode($a, JSON_PRETTY_PRINT);
Output
[
{
"#attributes": {
"a": "a1"
},
"0": "1",
"nodeName": "x"
},
{
"#attributes": {
"b": "b2"
},
"0": "2",
"nodeName": "y"
},
{
"0": "3",
"nodeName": "z"
}
]
Note, you can get the string value of a SimpleXmlElement by casting it to string:
$item['value'] = (string) $e;
Since xpath method supports relative XPath expressions, the asterisk should work even with namespaced XMLs. Consider using the DOM extension, as it is much more flexible than SimpleXML. In particular, its DOMXPath class allows to register namespaces and use the registered identifiers in the XPath expressions:
$xpath->registerNamespace('myprefix', 'http://example.com/ns');
$xpath->query('/root/myprefix:*');
As it turns out the entire reason it was not working was due to me using simplexml_load_string() on the child jobs constructor, which was turning it into a simpleXMLElement before the job was actually serialized and pushed onto the queue. the correct way to do it was to parse the XML string on the handle method, which is done after the job has been pulled from the queue for actual processing.
Now this works I can simply dispatch the child job with $child->asXML, and parse it when the job is actually being processed, meaning I can still use all the nifty simpleXML features such as attributes().
Example ParentJob:
foreach ($parent->children() as $child) {
dispatch(new ProcessChild($event, true), $this->otherVar);
}
Example ChildJob:
protected $xml;
protected $otherVar;
public function __construct($xml, $otherVar)
{
$this->xml = $xml;
$this->otherVar = $otherVar;
}
public function handle()
{
$child = simplexml_load_string($this->xml);
$attributes = $child->attributes();
}
I am currently working with the Amazon MWS to integrate some features into wordpress via a plugin. I am using the client libraries provided by amazon found here:
https://developer.amazonservices.com/api.html?group=bde§ion=reports&version=latest
Using these client libraries and the sample php files included I have set up my plugin to make two API calls. The first is requestReport
public function requestInventoryReport() {
AWI_Amazon_Config::defineCredentials(); // Defines data for API Call
$serviceUrl = "https://mws.amazonservices.com";
$config = array (
'ServiceURL' => $serviceUrl,
'ProxyHost' => null,
'ProxyPort' => -1,
'MaxErrorRetry' => 3,
);
$service = new MarketplaceWebService_Client(
AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY,
$config,
APPLICATION_NAME,
APPLICATION_VERSION);
$request = new MarketplaceWebService_Model_RequestReportRequest();
$request->setMerchant(MERCHANT_ID);
$request->setReportType('_GET_MERCHANT_LISTINGS_DATA_');
self::invokeRequestReport($service, $request);
}
private function invokeRequestReport(MarketplaceWebService_Interface $service, $request) {
try {
$response = $service->requestReport($request);
if ($response->isSetRequestReportResult()) {
// Print Out Data
}
} catch (MarketplaceWebService_Exception $ex) {
// Print Out Error
}
}
and the second is getReportRequestList which has code similar to the first function. I am able to run these functions without any errors. The issue that I am having is that $response->isSetRequestReportResult() returns false. From my understanding and looking into the response object, this would suggest that the response object does not have the result. (Upon printing out the response object I can see that the FieldValue of the result array is NULL.) The call, however, does not throw an error but neither does it have the result.
I did some digging through the code and found that the result does actually get returned from the api call but never gets set to the return object when the library attempts to parse it from XML. I've tracked the error down to this block of code (This code is untouched by me and directly from the amazon mws reports library).
private function fromDOMElement(DOMElement $dom)
{
$xpath = new DOMXPath($dom->ownerDocument);
$xpath->registerNamespace('a', 'http://mws.amazonaws.com/doc/2009-01-01/');
foreach ($this->fields as $fieldName => $field) {
$fieldType = $field['FieldType'];
if (is_array($fieldType)) {
if ($this->isComplexType($fieldType[0])) {
// Handle Data
} else {
// Handle Data
}
} else {
if ($this->isComplexType($fieldType)) {
// Handle Data
} else {
$element = $xpath->query("./a:$fieldName/text()", $dom);
$data = null;
if ($element->length == 1) {
switch($this->fields[$fieldName]['FieldType']) {
case 'DateTime':
$data = new DateTime($element->item(0)->data,
new DateTimeZone('UTC'));
break;
case 'bool':
$value = $element->item(0)->data;
$data = $value === 'true' ? true : false;
break;
default:
$data = $element->item(0)->data;
break;
}
$this->fields[$fieldName]['FieldValue'] = $data;
}
}
}
}
}
The data that should go into the RequestReportResult exists at the beginning of this function as a node in the dom element. The flow of logic takes it into the last else statement inside the foreach. The code runs its query and returns $element however $element->length = 13 in my case which causes it to fail the if statement and never set the data to the object. I have also looked into $element->item(0) to see what was in it and it appears to be a dom object itself matching the original dom object but with a bunch of empty strings.
Now, I'm new to working with the MWS and my gut feeling is that I am missing a parameter somewhere in my api call that is messing up how the data is returned and is causing this weird error, but I'm out of ideas at this point. If anyone has any ideas or could point me in the right direction, I would greatly appreciate it.
Thanks for your time!
** Also as a side note, Amazon Scratchpad does return everything properly using the same parameters that I am using in my code **
These works for me, check if you are missing anything.
For RequestReportRequest i am doing this:
$request = new MarketplaceWebService_Model_RequestReportRequest();
$marketplaceIdArray = array("Id" => array($pos_data['marketplace_id']));
$request->setMarketplaceIdList($marketplaceIdArray);
$request->setMerchant($pos_data['merchant_id']);
$request->setReportType($this->report_type);
For GetReportRequestList i am doing this:
$service = new MarketplaceWebService_Client($pos_data['aws_access_key'], $pos_data['aws_secret_access_key'], $pos_data['config'], $pos_data['application_name'], $pos_data['application_version']);
$report_request = new MarketplaceWebService_Model_GetReportRequestListRequest();
$report_request->setMerchant($pos_data["merchant_id"]);
$report_type_request = new MarketplaceWebService_Model_TypeList();
$report_type_request->setType($this->report_type);
$report_request->setReportTypeList($report_type_request);
$report_request_status = $this->invokeGetReportRequestList($service, $report_request, $report_requestID);
Is there any way to convert json to xml in PHP? I know that xml to json is very much possible.
If you're willing to use the XML Serializer from PEAR, you can convert the JSON to a PHP object and then the PHP object to XML in two easy steps:
include("XML/Serializer.php");
function json_to_xml($json) {
$serializer = new XML_Serializer();
$obj = json_decode($json);
if ($serializer->serialize($obj)) {
return $serializer->getSerializedData();
}
else {
return null;
}
}
It depends on how exactly you want you XML to look like. I would try a combination of json_decode() and the PEAR::XML_Serializer (more info and examples on sitepoint.com).
require_once 'XML/Serializer.php';
$data = json_decode($json, true)
// An array of serializer options
$serializer_options = array (
'addDecl' => TRUE,
'encoding' => 'ISO-8859-1',
'indent' => ' ',
'rootName' => 'json',
'mode' => 'simplexml'
);
$Serializer = &new XML_Serializer($serializer_options);
$status = $Serializer->serialize($data);
if (PEAR::isError($status)) die($status->getMessage());
echo '<pre>';
echo htmlspecialchars($Serializer->getSerializedData());
echo '</pre>';
(Untested code - but you get the idea)
Crack open the JSON with json_decode, and traverse it to generate whatever XML you want.
In case you're wondering, there is no canonical mapping between JSON and XML, so you have to write the XML-generation code yourself, based on the needs of your application.
I combined the two earlier suggestions into:
/**
* Convert JSON to XML
* #param string - json
* #return string - XML
*/
function json_to_xml($json)
{
include_once("XML/Serializer.php");
$options = array (
'addDecl' => TRUE,
'encoding' => 'UTF-8',
'indent' => ' ',
'rootName' => 'json',
'mode' => 'simplexml'
);
$serializer = new XML_Serializer($options);
$obj = json_decode($json);
if ($serializer->serialize($obj)) {
return $serializer->getSerializedData();
} else {
return null;
}
}
A native approch might be
function json_to_xml($obj){
$str = "";
if(is_null($obj))
return "<null/>";
elseif(is_array($obj)) {
//a list is a hash with 'simple' incremental keys
$is_list = array_keys($obj) == array_keys(array_values($obj));
if(!$is_list) {
$str.= "<hash>";
foreach($obj as $k=>$v)
$str.="<item key=\"$k\">".json_to_xml($v)."</item>".CRLF;
$str .= "</hash>";
} else {
$str.= "<list>";
foreach($obj as $v)
$str.="<item>".json_to_xml($v)."</item>".CRLF;
$str .= "</list>";
}
return $str;
} elseif(is_string($obj)) {
return htmlspecialchars($obj) != $obj ? "<![CDATA[$obj]]>" : $obj;
} elseif(is_scalar($obj))
return $obj;
else
throw new Exception("Unsupported type $obj");
}
Another option would be to use a JSON streaming parser.
Using a streamer parser would come in handy if you want to bypass the intermediate object graph created by PHP when using json_decode. For instance, when you got a large JSON document and memory is an issue, you could output the XML with XMLWriter directly while reading the document with the streaming parser.
One example would be https://github.com/salsify/jsonstreamingparser
$writer = new XMLWriter;
$xml->openURI('file.xml');
$listener = new JSON2XML($writer); // you need to write the JSON2XML listener
$stream = fopen('doc.json', 'r');
try {
$parser = new JsonStreamingParser_Parser($stream, $listener);
$parser->parse();
} catch (Exception $e) {
fclose($stream);
throw $e;
}
The JSON2XML Listener would need to implement the Listener interface:
interface JsonStreamingParser_Listener
{
public function start_document();
public function end_document();
public function start_object();
public function end_object();
public function start_array();
public function end_array();
public function key($key);
public function value($value);
}
At runtime, the listener will receive the various events from the parser, e.g. when the parser finds an object, it will send the data to the start_object() method. When it finds an array, it will trigger start_array() and so on. In those methods you'd then delegate the values to the appropriate methods in the XMLWriter, e.g. start_element() and so on.
Disclaimer: I am not affiliated with the author, nor have I used the tool before. I picked this library because the API looked sufficiently simple to illustrate how to use an event based JSON parser.
I have a bunch of PHP web services that construct JSON objects and deliver them using json_encode.
This works fine but I now have a requirement that the web services can also deliver in XML, depending on a given parameter.
I want to stay away from PEAR XML if possible, and hopefully find a simple solution that can be implemented with SimpleXML.
Can anyone give me any advice?
Thanks
You can create an associative array using json_decode($json,true) and try the following function to convert to xml.
function assocArrayToXML($root_element_name,$ar)
{
$xml = new SimpleXMLElement("<?xml version=\"1.0\"?><{$root_element_name}></{$root_element_name}>");
$f = function($f,$c,$a) {
foreach($a as $k=>$v) {
if(is_array($v)) {
$ch=$c->addChild($k);
$f($f,$ch,$v);
} else {
$c->addChild($k,$v);
}
}
};
$f($f,$xml,$ar);
return $xml->asXML();
}
// usage
$data = json_decode($json,true);
echo assocArrayToXML("root",$data);
Is there any way to convert json to xml in PHP? I know that xml to json is very much possible.
If you're willing to use the XML Serializer from PEAR, you can convert the JSON to a PHP object and then the PHP object to XML in two easy steps:
include("XML/Serializer.php");
function json_to_xml($json) {
$serializer = new XML_Serializer();
$obj = json_decode($json);
if ($serializer->serialize($obj)) {
return $serializer->getSerializedData();
}
else {
return null;
}
}
It depends on how exactly you want you XML to look like. I would try a combination of json_decode() and the PEAR::XML_Serializer (more info and examples on sitepoint.com).
require_once 'XML/Serializer.php';
$data = json_decode($json, true)
// An array of serializer options
$serializer_options = array (
'addDecl' => TRUE,
'encoding' => 'ISO-8859-1',
'indent' => ' ',
'rootName' => 'json',
'mode' => 'simplexml'
);
$Serializer = &new XML_Serializer($serializer_options);
$status = $Serializer->serialize($data);
if (PEAR::isError($status)) die($status->getMessage());
echo '<pre>';
echo htmlspecialchars($Serializer->getSerializedData());
echo '</pre>';
(Untested code - but you get the idea)
Crack open the JSON with json_decode, and traverse it to generate whatever XML you want.
In case you're wondering, there is no canonical mapping between JSON and XML, so you have to write the XML-generation code yourself, based on the needs of your application.
I combined the two earlier suggestions into:
/**
* Convert JSON to XML
* #param string - json
* #return string - XML
*/
function json_to_xml($json)
{
include_once("XML/Serializer.php");
$options = array (
'addDecl' => TRUE,
'encoding' => 'UTF-8',
'indent' => ' ',
'rootName' => 'json',
'mode' => 'simplexml'
);
$serializer = new XML_Serializer($options);
$obj = json_decode($json);
if ($serializer->serialize($obj)) {
return $serializer->getSerializedData();
} else {
return null;
}
}
A native approch might be
function json_to_xml($obj){
$str = "";
if(is_null($obj))
return "<null/>";
elseif(is_array($obj)) {
//a list is a hash with 'simple' incremental keys
$is_list = array_keys($obj) == array_keys(array_values($obj));
if(!$is_list) {
$str.= "<hash>";
foreach($obj as $k=>$v)
$str.="<item key=\"$k\">".json_to_xml($v)."</item>".CRLF;
$str .= "</hash>";
} else {
$str.= "<list>";
foreach($obj as $v)
$str.="<item>".json_to_xml($v)."</item>".CRLF;
$str .= "</list>";
}
return $str;
} elseif(is_string($obj)) {
return htmlspecialchars($obj) != $obj ? "<![CDATA[$obj]]>" : $obj;
} elseif(is_scalar($obj))
return $obj;
else
throw new Exception("Unsupported type $obj");
}
Another option would be to use a JSON streaming parser.
Using a streamer parser would come in handy if you want to bypass the intermediate object graph created by PHP when using json_decode. For instance, when you got a large JSON document and memory is an issue, you could output the XML with XMLWriter directly while reading the document with the streaming parser.
One example would be https://github.com/salsify/jsonstreamingparser
$writer = new XMLWriter;
$xml->openURI('file.xml');
$listener = new JSON2XML($writer); // you need to write the JSON2XML listener
$stream = fopen('doc.json', 'r');
try {
$parser = new JsonStreamingParser_Parser($stream, $listener);
$parser->parse();
} catch (Exception $e) {
fclose($stream);
throw $e;
}
The JSON2XML Listener would need to implement the Listener interface:
interface JsonStreamingParser_Listener
{
public function start_document();
public function end_document();
public function start_object();
public function end_object();
public function start_array();
public function end_array();
public function key($key);
public function value($value);
}
At runtime, the listener will receive the various events from the parser, e.g. when the parser finds an object, it will send the data to the start_object() method. When it finds an array, it will trigger start_array() and so on. In those methods you'd then delegate the values to the appropriate methods in the XMLWriter, e.g. start_element() and so on.
Disclaimer: I am not affiliated with the author, nor have I used the tool before. I picked this library because the API looked sufficiently simple to illustrate how to use an event based JSON parser.