Object to XML changing the names of the nodes - php

I have the object in php like this:
object(stdClass)[16]
public 'L1' =>
object(stdClass)[17]
public 'int' =>
array (size=2)
0 => int 1
1 => int 2
public 'L2' => string '52' (length=2)
public 'R' => string '2' (length=1)
Which i can convert to XML and i get:
<data>
<L1>
<int>
<node>1</node>
<node>2</node>
</int>
</L1>
<L2>52</L2>
<R>2</R>
</data>
The problem is that i want to get rid of the names of the nodes and the node . In the final i want my XML to look like this:
<data>
<param1>1</param1>
<param2>2</param2>
<param3>52</param3>
<param4>2</param4>
</data>
Can anyone suggest the way i can do it ?
Thank you in advance.
Here is the class for creating the XML:
<?php
class XMLSerializer {
// functions adopted from http://www.sean-barton.co.uk/2009/03/turning-an-array-or-object-into-xml-using-php/
public static function generateValidXmlFromObj(stdClass $obj, $node_block='data', $node_name='node') {
$arr = get_object_vars($obj);
return self::generateValidXmlFromArray($arr, $node_block, $node_name);
}
public static function generateValidXmlFromArray($array, $node_block='data', $node_name='node') {
$xml = '<?xml version="1.0" encoding="UTF-8" ?>';
$xml .= '<' . $node_block . '>';
$xml .= self::generateXmlFromArray($array, $node_name);
$xml .= '</' . $node_block . '>';
return $xml;
}
private static function generateXmlFromArray($array, $node_name) {
$xml = '';
if (is_array($array) || is_object($array)) {
foreach ($array as $key=>$value) {
if (is_numeric($key)) {
$key = $node_name;
}
$xml .= '<' . $key . '>' . self::generateXmlFromArray($value, $node_name) . '</' . $key . '>';
}
} else {
$xml = htmlspecialchars($array, ENT_QUOTES);
}
return $xml;
}
}
And the code:
header ("Content-Type:text/xml");
include 'tamo.wss.php';
include 'xml_class.php';
$user = $_GET['id'];
$client = new SoapClient("Wsdl_Service", $options);
$client->__setSoapHeaders(Array(new WsseAuthHeader("login", "password")));
$param['ns1:l0'] = $user;
$encodded = new SoapVar($param, SOAP_ENC_OBJECT);
$result = $client->GetAttributes($encodded);
$xml = XMLSerializer::generateValidXmlFromObj($result->GetAttributesResult->Result->SingleAttribute);
echo $xml;

If I've understood your problem correctly, you can solve it this way.
$index = 1;
$xml = '<data>';
foreach(get_object_vars($result->GetAttributesResult->Result->SingleAttribute) as $value) {
$xml .= '<param' . $index . '>' . $value . '</param' . $index++ . '>';
}
$xml .= '</data>';

Related

How do I generate a SOAP request with recursive capabilities?

As I haven't found a solution for testing my SOAP integration, I have decided to make my own soap requests through POST method. The method sends an XML response which works fine, but I want to make my own array-to-xml parser. So far my code is:
private function parseData($xml, $data) {
foreach ($data as $key => $row) {
var_dump($key, $row);
if (is_array($row) && count($row)) {
return $this->parseData($xml, $row);
}
$xml->addChild($key, $row);
}
return $xml;
}
private function toWSDL($call, $data) {
$root = '<data/>';
$xml = new \SimpleXMLElement($root);
$xml = $this->parseData($xml, $data);
$xmlBody = $xml->asXML();
$open = '<ns1:' . $call . '>';
$close = '</ns1:' . $call . '>';
$xmlBody = str_replace('<data>', $open, $xmlBody);
$xmlBody = str_replace('</data>', $close, $xmlBody);
$xmlBody = str_replace('<?xml version="1.0"?>', '', $xmlBody);
$request_data = <<<EOF
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="https://api.hostedshop.io/service.php">
<SOAP-ENV:Body>
$xmlBody
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
EOF;
return $request_data;
}
but it does not work for a soap function called "Product_Update", which takes in an array like so:
$this->call("Product_Update", [
"ProductData" => [
"Id" => 1,
"Stock" => 2
]
]);
The "ProductData" key with an array does not work with my parseData function, which is why I am writing for help.

Unable to Access #attributes in XML Node

I am trying to access the 'field' element in the 'criteria' node in the following XML:
<?xml version="1.0" encoding="utf-8"?>
<result>
<product>
<data>
<field>spr_tech1</field>
<value>S7</value>
<criteria field="xfield_3">
<criteria_list>Green</criteria_list>
<criteria_list>Beige</criteria_list>
</criteria>
</data>
<data>
<field>spr_tech1</field>
<value>S1</value>
<criteria field="xfield_3">
<criteria_list>Red</criteria_list>
<criteria_list>Blue</criteria_list>
<criteria_list>Yellow</criteria_list>
</criteria>
</data>
<data>
<field>spr_tech1</field>
<value>S7</value>
<criteria field="xfield_3">
<criteria_list>Green</criteria_list>
</criteria>
<criteria field="tech_rt2">
<criteria_list>Transistor</criteria_list>
</criteria>
</data>
</product>
</result>
The code below results in the following error:
Fatal error: Call to a member function attributes() on a non-object
function parseXmlFile($filename)
{
$xml = file_get_contents($filename);
$obj = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
$array = json_decode(json_encode($obj), true); // Convert to array}
return $array;
}
$xform = parseXmlFile('transformations.xml');
foreach ($xform['product']['data'] as $data)
{
echo (string)$data['field'] . '<br>';
echo (string)$data['value'] . '<br>';
foreach($data['criteria']->attributes() as $att => $val)
{ echo $att . ' = ' . $val . '<br>'; }
echo $data['criteria']->attributes()->{'field'} . '<br>';
foreach($data['criteria']['criteria_list'] as $att => $val)
{ echo $att . ' = ' . $val . '<br>'; }
echo "-----------------------------------<br>";
}
print "<pre>";
print_r($xform);
print "</pre>";
I've tried a couple methods as you can see in the code, but neither are working for me.
Any help is appreciated!
It seems the problem is when you load the file.
I tried it with file_get_contents() See below
$xml_content = file_get_contents('path to your XML file');
$xform = simplexml_load_string($xml_content);
foreach ($xform['product']['data'] as $data)
{
echo (string)$data['field'] . '<br>';
echo (string)$data['value'] . '<br>';
foreach($data['criteria']->attributes() as $att => $val)
{ echo $att . ' = ' . $val . '<br>'; }
echo $data['criteria']->attributes()->{'field'} . '<br>';
foreach($data['criteria']['criteria_list'] as $att => $val)
{ echo $att . ' = ' . $val . '<br>'; }
echo "-----------------------------------<br>";
}
print "<pre>";
print_r($xform);
print "</pre>";
Your parseXmlFile function can be replaced completely with the built-in simplexml_load_file():
it loads the contents with file_get_contents first; simplexml_load_file vs simplexml_load_string will do this for you
it forces the LIBXML_NO_CDATA option, which is almost certainly not something you need; to get the text contents of a SimpleXML object - including CDATA - you use (string)$node
it converts the incredibly useful SimpleXML object into an array that will be missing half of the data and much worse to work with
To understand SimpleXML, look at the examples in the manual. Elements are accessed with ->name and attributes with ['name'].
So:
$xform = simplexml_load_file('transformations.xml');
foreach ($xform->product->data as $data)
{
echo (string)$data->field . '<br>';
echo (string)$data->value . '<br>';
foreach($data->criteria->attributes() as $att => $val)
{ echo $att . ' = ' . $val . '<br>'; }
echo $data->criteria['field'] . '<br>';
foreach($data->criteria->criteria_list as $number => $node)
{ echo $number . ' = ' . (string)$node . '<br>'; }
echo "-----------------------------------<br>";
}

Convert PHP nested array to XML tree structure

I have following array in PHP and I want to convert it equivalent XML.
$data = array("VitalParameters"=>array(
"Details"=>array(
"PID"=>1234,
"OPID"=>1345
),
"Parameters"=>array(
"HR"=>112,
"SPO2"=>0
)));
Following XML tree structure which I expected
<VitalParameters>
<Details>
<PID>1234</PID>
<OPID>1345</OPID>
</Details>
<Parameters>
<HR>112</HR>
<SPO2>0</SPO2>
</Parameters>
</VitalParameters>
I tried many things but no luck. If any more information required just comment I will give additional information.
Try Array2XML (http://www.lalit.org/lab/convert-php-array-to-xml-with-attributes) this has worked for me.
$data = array(
"Details" => array(
"PID" => 1234,
"OPID" => 1345
),
"Parameters" => array(
"HR" => 112,
"SPO2" => 0
));
$xml = Array2XML::createXML('VitalParameters', $data)->saveXML();
echo $xml;
Output
<?xml version="1.0" encoding="UTF-8"?>
<VitalParameters>
<Details>
<PID>1234</PID>
<OPID>1345</OPID>
</Details>
<Parameters>
<HR>112</HR>
<SPO2>0</SPO2>
</Parameters>
</VitalParameters>
This should get you on the right track, probably needs some tuning though (especially in regards to keys aswell as checking if $arr is an array/object in the first place... this was just to show you how to recursively iterate them and print the data).
Also, this snippet does not support XML attributes in any way.
<?php
$data = array("VitalParameters"=>array(
"Details"=>array(
"PID"=>1234,
"OPID"=>1345
),
"Parameters"=>array(
"HR"=>112,
"SPO2"=>0
)));
function ArrayToXML($arr, $depth = 0, $maxDepth = 2)
{
$retVal = "";
foreach($arr as $key => $value)
{
$retVal .= "<" . $key . ">";
$type = gettype($value);
switch($type)
{
case "array":
case "object":
if ($depth < $maxDepth)
$retVal .= ArrayToXml($value, $depth+1, $maxDepth);
else
$retVal .= $type; // Depth >= MaxDepth, not iterating here
break;
case "boolean":
$retVal .= $value ? "true" : "false";
break;
case "resource":
$retVal .= "Resource";
case "NULL":
$retVal .= "NULL";
default:
$retVal .= $value;
}
$retVal .= "</" . $key . ">\n";
}
return $retVal;
}
echo ArrayToXML($data);
?>
Output:
<VitalParameters><Details><PID>1234</PID>
<OPID>1345</OPID>
</Details>
<Parameters><HR>112</HR>
<SPO2>0</SPO2>
</Parameters>
</VitalParameters>
I got solution like following code
<?php
header("Content-type: text/xml; charset=utf-8");
$data = array(
"Details" => array(
"PID" => "1234","OPID" => "1345"
),"Parameters" => array(
"HR" => "112","SPO2" => "0"
)
);
$domtree = new DOMDocument('1.0', 'UTF-8');
/* create the root element of the xml tree */
$xmlRoot = $domtree->createElement("VitalParameters");
/* append it to the document created */
$xmlRoot = $domtree->appendChild($xmlRoot);
foreach ($data as $key=>$value)
{
$currentElement= $domtree->createElement($key);
$currentElement= $xmlRoot->appendChild($currentElement);
if(is_array($value))
{
foreach ($value as $k=>$v)
{
$currentElement->appendChild($domtree->createElement($k,$v));
}
}
}
/* get the xml printed */
echo $domtree->saveXML();
Output
<VitalParameters>
<Details>
<PID>1234</PID>
<OPID>1345</OPID>
</Details>
<Parameters>
<HR>112</HR>
<SPO2>0</SPO2>
</Parameters>
</VitalParameters>
I think - this is must be more simple:
function arrayToXML($arr){
$s = "";
foreach($arr as $k => $v){
if(is_array($v)){$v = arrayToXML($v);}
$s .= "<{$k}>{$v}</{$k}>";
}
return $s;}

Using XPath to select through namespaces

I've been trying to use XPath in PHP to access an Atom feed from the National Health Service API.
The data looks like this:
<feed xmlns:s="http://syndication.nhschoices.nhs.uk/services" xmlns="http://www.w3.org/2005/Atom">
<title type="text">NHS Choices - GP Practices Near Postcode - W1T4LB - Within 5km</title>
<entry>
<id>http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/27369</id>
<title type="text">Fitzrovia Medical Centre</title>
<updated>2011-08-20T22:47:39Z</updated>
<link rel="self" title="Fitzrovia Medical Centre" href="http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/27369?apikey="/>
<link rel="alternate" title="Fitzrovia Medical Centre" href="http://www.nhs.uk/ServiceDirectories/Pages/GP.aspx?pid=303A92EF-EC8D-496B-B9CD-E6D836D13BA2"/>
<content type="application/xml">
<s:organisationSummary>
<s:name>Fitzrovia Medical Centre</s:name>
<s:address>
<s:addressLine>31 Fitzroy Square</s:addressLine>
<s:addressLine>London</s:addressLine>
<s:postcode>W1T6EU</s:postcode>
</s:address>
<s:contact type="General">
<s:telephone>020 7387 5798</s:telephone>
</s:contact>
<s:geographicCoordinates>
<s:northing>182000</s:northing>
<s:easting>529000</s:easting>
<s:longitude>-0.140267259415255</s:longitude>
<s:latitude>51.5224357586293</s:latitude>
</s:geographicCoordinates>
<s:Distance>0.360555127546399</s:Distance>
</s:organisationSummary>
</content>
</entry>
</feed>
I'm now using this code to access the node.
<?php
$feedURL = 'http://v1.syndication.nhschoices.nhs.uk/organisations/pharmacies/postcode/W1T4LB.xml?apikey=&range=5';
$xml = file_get_contents($feedURL);
$sxml = new SimpleXMLElement($xml);
$sxml->registerXPathNamespace('a', 'http://www.w3.org/2005/Atom');
$sxml->registerXPathNamespace('s', 'http://syndication.nhschoices.nhs.uk/services');
$addr = $sxml->xpath('//s:address');
var_dump($addr[0]);
?>
Here $addr is empty but it should have ten entries.
Please can someone explain a good way to print out each <s:addressLine> node contents, and to place the postcode into a var.
I am working with the namespace principle in practice, although this is fairly new to me. Appreciate any information you could convey about learning XPath, and the PHP SimpleXML model.
Appreciate the help.
EDIT -
In seeing the update given below I decided to put my final output code into this:
function doPharmacy($postcode, $request, $prev, $next)
{
$feedURL = 'http://v1.syndication.nhschoices.nhs.uk/organisations/pharmacies/postcode/' . $postcode . '.xml?apikey=&range=5';
$xml = file_get_contents($feedURL);
$sxml = new SimpleXMLElement($xml);
$sxml->registerXPathNamespace('a', 'http://www.w3.org/2005/Atom');
$sxml->registerXPathNamespace('s', 'http://syndication.nhschoices.nhs.uk/services');
////////////// XPATH \\\\\\\\\\\\\\
$addrLines = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:content/s:organisationSummary/s:address/s:addressLine');
$nhsPostcode = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:content/s:organisationSummary/s:address/s:postcode');
$tel = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:content/s:organisationSummary/s:contact/s:telephone');
$distance = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:content/s:organisationSummary/s:Distance');
$title = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:title');
$link = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:link[#rel="alternate"]/#href');
$nhsPostcode = array_pop($nhsPostcode); // always taking first node from set
$tel = array_pop($tel);
$distance = (double)array_pop($distance);
$title = array_pop($title);
$link = array_pop($link);
////////////// OUTPUT: JSON \\\\\\\\\\\\\\
print '{"addr": "';
foreach ($addrLines as $addr)
{
print $addr . '<br />'; // ok to use HTML tags in JSON
}
print '",';
print '"postcode": "' . $nhsPostcode . '",';
print '"tel": "' . $tel . '",';
$num = number_format($distance, 2);
print '"distance": "' . $num . '",';
print '"title": "' . $title . '",';
print '"link": "' . $link . '",';
$nhsPostcode = urlencode($nhsPostcode);
print'"mapsURL": "http://maps.googleapis.com/maps/api/staticmap?center=' . $nhsPostcode . '&zoom=15&size=155x137&sensor=false&scale=2",';
print '"prev": "' . $prev . '",';
print '"next": "' . $next . '"';
print '}';
}
?>
This works for me:
$addr = $sxml->xpath('//s:address');
foreach ($addr as $a) {
$addressLine = $a->xpath('s:addressLine');
foreach ($addressLine as $al) {
echo (string)$al."<br/>";
}
$postalCode = $a->xpath('s:postcode');
foreach ($postalCode as $p) {
echo (string)$p."<br/>";
}
}
Which displays:
31 Fitzroy Square
London
W1T6EU
Previous answers have helped me a lot! So here go the complete solution.
$feedURL = "http://v1.syndication.nhschoices.nhs.uk/organisations/pharmacies/postcode/{$user->postcode}.xml?apikey=XXXX&range=100";
$xml = simplexml_load_file($feedURL);
$xml->registerXPathNamespace('s', 'http://syndication.nhschoices.nhs.uk/services');
$org = $xml->xpath('//s:organisationSummary');
$i = 0;
$item = array();
foreach ($org as $o)
{
$aux = $o->xpath('s:name');
$item[$i]['name'] = (string) $aux[0];
$addr = $o->xpath('s:address');
foreach ($addr as $a)
{
$aux = '';
$addressLine = $a->xpath('s:addressLine');
foreach ($addressLine as $a2)
{
$aux .= (string) $a2[0] . "\n";
}
$aux2 = $a->xpath('s:postcode');
if (is_array($aux2))
{
$aux .= (string) $aux2[0];
}
$item[$i]['address'] = $aux;
}
$i ++;
}
This will provide:
array (
0 =>
array (
'name' => 'Your Local Boots Pharmacy',
'address' => 'Century House
Station Road
Manningtree
CO11 1AA',
),
1 =>
array (
'name' => 'The Pharmacy',
'address' => 'The Street
East Bergholt
CO7 6SE',
), and so on...

how i can use SAX parser

This is what the result should look like when i parse it through a SAX parser
http://img13.imageshack.us/img13/6950/75914446.jpg
This is the XML source code from which i need to generate the display:
<orders>
<order>
<count>37</count>
<price>49.99</price>
<book>
<isbn>0130897930</isbn>
<title>Core Web Programming Second Edition</title>
<authors>
<count>2</count>
<author>Marty Hall</author>
<author>Larry Brown</author>
</authors>
</book>
</order>
<order>
<count>1</count>
<price>9.95</price>
<yacht>
<manufacturer>Luxury Yachts, Inc.</manufacturer>
<model>M-1</model>
<standardFeatures oars="plastic" lifeVests="none">false</standardFeatures>
</yacht>
</order>
<order>
<count>3</count>
<price>22.22</price>
<book>
<isbn>B000059Z4H</isbn>
<title>Harry Potter and the Order of the Phoenix</title>
<authors>
<count>1</count>
<author>J.K. Rowling</author>
</authors>
</book>
</order>
i really have no clue how to code the functions but i have just set up the parser
$xmlParser = xml_parser_create("UTF-8");
xml_parser_set_option($xmlParser, XML_OPTION_CASE_FOLDING, false);
xml_set_element_handler($xmlParser, 'startElement', 'endElement');
xml_set_character_data_handler($xmlParser, 'HandleCharacterData');
$fileName = 'orders.xml';
if (!($fp = fopen($fileName, 'r'))){
die('Cannot open the XML file: ' . $fileName);
}
while ($data = fread($fp, 4096)){
$parsedOkay = xml_parse($xmlParser, $data, feof($fp));
if (!$parsedOkay){
print ("There was an error or the parser was finished.");
break;
}
}
xml_parser_free($xmlParser);
function startElement($xmlParser, $name, $attribs)
{
}
function endElement($parser, $name)
{
}
function HandleCharacterData($parser, $data)
{
}
A SimpleXML-based recursive approach might be a simpler solution perhaps, something like:
$xml = new SimpleXmlElement($data);
function renderTree(SimpleXmlElement $xml, $depth = 0) {
$output = str_repeat('| ', $depth);
$output .= '+-' . $xml->getName();
if (sizeof($xml->attributes()) > 0) {
$attrs = '';
foreach ($xml->attributes() as $key => $value) {
$attrs .= $key . '=' . $value . ', ';
}
$output .= ' (' . trim($attrs, ', ') . ')';
}
$output .= ': ' . (string)$xml;
$output .= '<br />';
if ($xml->count() > 0) {
foreach ($xml->children() as $child) {
$output .= renderTree($child, $depth + 1);
}
}
return $output;
}
echo renderTree($xml);
Would render the tree as in the example image.

Categories