How to convert XML with multiple prefixes to object - php

We have xml similiar to this:
<?xml version="1.0" encoding="UTF-8"?>
<animal:race>
<animal:dog>
<specification:color>Black</specification>
</animal:dog>
<animal:cat>
<animal:name>Flappie</animal:name>
</animal:cat>
</animal:race>
We tried simplexml_load_file("file.xml",null,null,"animal");
which returns
object(animal:race)#1 (2) {
["dog"]=> object(SimpleXMLElement)#2 (0) {
}
["cat"]=> object(SimpleXMLElement)#2 (1) {
[animal:name] => string "flappie"
}
}
We've seen multiple posts about this but can't seem to get any of this working stripping the prefixes away is not an option cause we need those in a later stage...
Can someone perhaps make a bit of code which converts this to an valid object so that we can adjust it to our own xml.

Related

Get SoapClient to always parse XML tag as an array

I'm using PHP's SoapClient to load the following XML under 2 scenarios
In scenario A, the multiple Sales_Order_Line are returned as an array of stdClass
In scenario B, the single Sales_Order_Line is returned as a stdClass
The XSD schema is provided, but I'm not sure how to make SoapClient follow it.
How can I make it always return as an array even in scenario B?
Scenario A
<Soap:Envelope xmlns:Soap="http://schemas.xmlsoap.org/soap/envelope/">
<Soap:Body>
<ReadMultiple_Result xmlns="urn:microsoft-dynamics-schemas/page/salesorder">
<ReadMultiple_Result>
<SalesOrder>
...
<SalesLines>
<Sales_Order_Line>
</Sales_Order_Line>
<Sales_Order_Line>
</Sales_Order_Line>
<Sales_Order_Line>
</Sales_Order_Line>
</SalesLines>
</SalesOrder>
</ReadMultiple_Result>
</ReadMultiple_Result>
</Soap:Body>
</Soap:Envelope>
Scenario B
<Soap:Envelope xmlns:Soap="http://schemas.xmlsoap.org/soap/envelope/">
<Soap:Body>
<ReadMultiple_Result xmlns="urn:microsoft-dynamics-schemas/page/salesorder">
<ReadMultiple_Result>
<SalesOrder>
...
<SalesLines>
<Sales_Order_Line>
</Sales_Order_Line>
</SalesLines>
</SalesOrder>
</ReadMultiple_Result>
</ReadMultiple_Result>
</Soap:Body>
</Soap:Envelope>
Parse the response with xpath. It returns an array at success.
Update for clarification
The only one XPath expression meets your needs in both scenarios. Let's say you've got a raw xml response $rawResponse. Then you can retrieve array of Sales_Order_Line objects with a code like this:
$xml = simplexml_load_string($rawResponse);
$xml->registerXPathNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
$xml->registerXPathNamespace("salesorder", "urn:microsoft-dynamics-schemas/page/salesorder");
$salesOrderLines = $xml->xpath("/soapenv:Envelope/soapenv:Body/salesorder:ReadMultiple_Result/salesorder:ReadMultiple_Result/salesorder:SalesOrder/salesorder:SalesLines/salesorder:Sales_Order_Line");
var_dump($salesOrderLines);
In scenario A output will be:
array(3) {
[0]=>
object(SimpleXMLElement)#2 (0) {
}
[1]=>
object(SimpleXMLElement)#3 (0) {
}
[2]=>
object(SimpleXMLElement)#4 (0) {
}
}
In scenario B output will be:
array(1) {
[0]=>
object(SimpleXMLElement)#2 (0) {
}
}
Alternative method
If you don't want to deal with the raw response, look at SOAP_SINGLE_ELEMENT_ARRAYS feature.

SimpleXMLElement is removing attributes (php 7.2)

Does anybody know why SimpleXMLElement is removing the attributes in my XML??
I have XML data that looks like this (note the translation "language" attribute):
<events>
<event id="d8f17143-0c67-48aa-a7f1-003a5ddbd28f">
<details>
<names>
<translation language="en">English title</translation>
<translation language="de">German title</translation>
</names>
</details>
</event>
</events>
I run it through SimpleXmlElement like so:
$xmlConvertedData = new \SimpleXMLElement($xml);
I dump out the data and it looks like so:
object(SimpleXMLElement)#958 (2) {
["#attributes"]=>
array(1) {
["Index"]=>
string(1) "1"
}
["Events"]=>
object(SimpleXMLElement)#956 (1) {
["Event"]=>
array(1) {
[0]=>
object(SimpleXMLElement)#959 (1) {
["Details"]=>
object(SimpleXMLElement)#826 (13) {
["Names"]=>
object(SimpleXMLElement)#834 (1) {
["Translation"]=>
array(2) {
[0]=>
string(32) "English title"
[1]=>
string(33) "German title"
}
}
}
}
}
}
}
...notice "translation" no longer has a "language" attribute, just an ID number 0 and 1. I need to know the attribute value because the XML does not always show the same language first.
(I edited the shortened the sample code to one record, so please ignore the #958 part)
Do not use any of the print_r() or var_dump() on a SimpleXML object, this will abbreviate the output as there is potentially a lot of it. If you want to check the document loaded use asXML()...
echo $xmlConvertedData->asXML();
or to output the one elements language...
echo $xmlConvertedData->event[0]->details->names->translation['language'];
( You also need to correct the last element of the sample - </events>)

var_dump for DOMNodeList printout unexpected result

I have a xml file I load it as the following:
//$file the file system path of the xml file
function getTopicsList($file){
$doc = new DOMDocument();
$doc->load( $file );
var_dump($doc->getElementsByTagName('topic'));
return $doc->getElementsByTagName('topic');
}
The loaded xml file contents is something like the following:
<?xml version="1.0" encoding="UTF-8"?>
<topics>
<topic>
<title>Title1</title>
<keywords>"Some Keys"</keywords>
</topic>
<topic>
<title>The Title</title>
<keywords>Another Key</keywords>
</topic>
<topic>
<title>A Title</title>
<keywords>Key two</keywords>
</topic>
</topics>
The var_dump() in the above code just printout limited information such as:
object(DOMNodeList)#30 (1) {
["length"]=>
int(3)
}
I expected that it should print at least the properties of that object i.e the xml tags and its values. I tried to use other functions such as print_r() and var_export() but there is no details I want.
No, this is node list. You can iterate it with foreach or access nodes using the item() method.
Node lists are used at different places, getElementsByTagName() is one, another is the $childNodes property. Xpath expressions return node lists, too.
Be aware that the nodes can be not only elements but several node types. Like text, cdata section or attribute.
You can use var_dump() to dump a single node. This works with PHP >= 5.3.11 or >= 5.4.1.
$dom = new DOMDocument();
$dom->loadXML('<foo/>');
var_dump($dom->documentElement);
Output:
object(DOMElement)#2 (18) {
["schemaTypeInfo"]=>
NULL
["tagName"]=>
string(3) "foo"
["textContent"]=>
string(0) ""
["baseURI"]=>
string(1) "/"
["localName"]=>
string(3) "foo"
["prefix"]=>
string(0) ""
["ownerDocument"]=>
...

PHP SimpleXMLElement XPath Selection

I have the following XML: http://pastebin.com/QiRK72BK
which is generated in response to a REST query. My code is very simple:
$xml = simplexml_load_file($url);
var_dump($xml->getName());
var_dump($xml->xpath("serverDetail/apiEnv"));
in an attempted test case. The first var_dump reveals that the XML file is indeed being loaded:
string(21) "GeneralSearchResponse"
However, the second var_dump puzzles me. I feel it should definitely match some data, but instead I get
array(0) { }
I've also tried the xpath "/serverDetail/apiEnv" "//apiEnv" and "/" and always get an empty array. Am I misunderstanding xpath or perhaps missing some initialization step?
Your XML is using a namespace:
$xml->registerXPathNamespace('u', 'urn:types.partner.api.shopping.com');
var_dump($xml->xpath("//u:serverDetail/u:apiEnv"));
Output:
array(1) {
[0]=>
object(SimpleXMLElement)#2 (1) {
[0]=>
string(7) "sandbox"
}
}
Edit: Dirty workaround, might be helpful though:
$xml = simplexml_load_file($url);
$xmlStr = str_replace("xmlns", "ns", $xml->asXML());
$xml = new SimpleXMLElement($xmlStr);
var_dump($xml->xpath("serverDetail/apiEnv"));

PHP, SimpleXML arrays. Unanticipated casting of an array to a string

Sample XML:
<root>
<ratings>3</ratings>
<ratings>5</ratings>
<ratings>7</ratings>
</root>
The following code is the basis for my small application, it works as would be expected:
<?php
// $xml is some simplexml object
sizeof($xml->ratings); //3
foreach($xml->ratings as $rating){
echo($rating->value."::"); //this echoes all 3 rating values: 3::5::7
}
?>
This next code, which I would normally consider to be equivalent is not. And I have no idea why:
<?php
// $xml is some simplexml object
$ratings = $xml->ratings;
sizeof($ratings); //3, all is well so far
foreach($ratings as $rating){
echo($rating."::");
/*this echoes a never-ending list of ratings,
looking like 3::5::5::5::5::5::5::5...... */
}
?>
My feeling is that the assignment operator is casting the array of simplexml objects (ratings objects) as something odd, but have no clue as to how.
Other little hints:
var_dump($xml);
/* Output is:
object(SimpleXMLElement)#7 (1) {
["ratings"]=>
array(3) {
[0]=>
string(1) "3"
[1]=>
string(1) "5"
[2]=>
string(1) "7"
}
}
*/
var_dump($ratings);
/* Output is:
object(SimpleXMLElement)#6 (1) {
[0]=>
string(1) "3"
}
*/
Your echos are not the same:
echo($rating."::");
should be
echo($rating->value."::");
Ok, cleaning up some of my own work. After attempting to simplify my issue more, I was not able to prove it. After messing with the actual code, I assume this means I have some sort of mutating object elsewhere in my app that is going bonkers and creating weird results in this xml parsing. Sorry for the confusion and needless question (I guess this proves why i'm trying to refactor some of my complexity out of this app).
As a parting gift, here is the test-suite of code that I used (from simple to more realistic) that I used to prove that all worked as advertised:
<?php
$string = <<<XML
<?xml version='1.0'?>
<root>
<ratings>3</ratings>
<ratings>5</ratings>
<ratings>7</ratings>
</root>
XML;
$xml = simplexml_load_string($string);
var_dump($xml);
echo("Size:".sizeof($xml->ratings)."\n");
foreach($xml->ratings as $rating){
echo($rating."::");
}
echo("\n"."------"."\n");
$ratings = $xml->ratings;
echo("Size:".sizeof($ratings)."\n");
foreach($ratings as $rating){
echo($rating."::");
}
echo("\n\n\n\n"."||||New Example||||"."\n\n\n\n");
$stringthree = <<<XML
<root attr1="val" attr2="desc">
<field-one>val</field-one>
<elm-two attr-name="foo">elmTwoVal1</elm-two>
<elm-three>elmThreeVal1</elm-three>
<elm-two attr-name="bar">elmTwoVal2</elm-two>
<elm-three>elmThreeVa2</elm-three>
<elm-two attr-name="bear">elmTwoVal3</elm-two>
<elm-three>elmThreeVal3</elm-three>
</root>
XML;
$xmlthree = simplexml_load_string($stringthree);
var_dump($xmlthree);
echo("Size:".sizeof($xmlthree->{'elm-two'})."\n");
foreach($xmlthree->{'elm-two'} as $elm){
echo($elm."::");
}
echo("\n"."------"."\n");
$elmMain = $xmlthree->{'elm-two'};
echo("Size:".sizeof($elmMain)."\n");
foreach($elmMain as $elm){
echo($elm."::");
}
?>

Categories