Parse XML using PHP with multiple namespaces - php

I have an xml file which i am having a hard time to correctly parse:
I need to retrieve the Value from the xml file
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:PerfmonCollectCounterDataResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://schemas.cisco.com/ast/soap/">
<ArrayOfCounterInfo soapenc:arrayType="ns1:CounterInfoType[2]" xsi:type="soapenc:Array" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<item xsi:type="ns1:CounterInfoType">
<Name xsi:type="ns1:CounterNameType">\\172.16.106.18\Number of Replicates Created and State of Replication(ReplicateCount)\Number of Replicates Created</Name>
<Value xsi:type="xsd:long">603</Value>
<CStatus xsi:type="xsd:unsignedInt">1</CStatus>
</item>
<item xsi:type="ns1:CounterInfoType">
<Name xsi:type="ns1:CounterNameType">\\172.16.106.18\Number of Replicates Created and State of Replication(ReplicateCount)\Replicate_State</Name>
<Value xsi:type="xsd:long">2</Value>
<CStatus xsi:type="xsd:unsignedInt">1</CStatus>
</item>
</ArrayOfCounterInfo>
</ns1:PerfmonCollectCounterDataResponse>
</soapenv:Body>
</soapenv:Envelope>
Below is the code that I am running:
// fclose($fopen);
// if (file_exists($filename)) {
// $output = simplexml_load_file($filename,null,null,"http://www.w3.org/2001/XMLSchema-instance",true); // I have also tried without the namespaces
;
//$output = simplexml_load_string($xmldata);
//var_dump($output);
//print_r($output);
} else {
echo "File not found";
}
Below is the empty object I am getting:
object (SimpleXMLElement)[3]
SimpleXMLElement Object ( )
Pls help.

That actually is not an empty object, and has methods against it. This is expected. If you did
echo $output->asXML();
You will see your XML string returned.
Now perform methods against this to iterate the data set and retrieve your specific values.
Also, if you're dealing with debugging simplexml frequently, consider this function

Related

simplexml_load_string always fails if there are special characters inside tags

I'm receiving from an external web service an XML like this(notice the colons inside the tags)
XML
<?xml version='1.0' encoding='UTF-8'?>
<env:Envelope
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns0="http://webservice/CrsWSApi.wsdl/types/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<ns0:callwsapiResponseElement>
<ns0:result>
<ns0:object>BTS</ns0:object>
<ns0:receiver>BTSV45_ANSF_PROD-AKITT</ns0:receiver>
<ns0:sender>ANSFP</ns0:sender>
<ns0:data>
{"name":"Paolo", "cognome": Rossi, "data":"18/11/1983"}
</ns0:data>
<ns0:errormess xsi:nil="true"/>
<ns0:errorcode xsi:nil="true"/>
<ns0:status>1</ns0:status>
<ns0:sessionid>akit1</ns0:sessionid>
</ns0:result>
</ns0:callwsapiResponseElement>
</env:Body>
</env:Envelope>
if I use this function to parse it
PHP
$xml = simplexml_load_string($xml) or die("Error: Cannot create object");
I'm always receiving Error. If I remove the colons it works as expected. Is there a way to make this works without manipulating the colons before the xml parsing?
I want to avoid this manipulation
PHP
$xml = preg_replace(['/env:/','/:env(?!=)/','/ns0:/','/:ns0(?!=)/'], '', $myXMLData);
UPDATE
I already accepted a good answer that is reporting usefull links on how namespases are working in an xml environment, but I want to share another way to obtain the same result without parsing an xpath expression. We can you use the children method of simple_xml_loadstring specifiying the second parameter as a namespace.
fot the above code you can obtain the ns0:data content with this code(before you have to change the if statement as the accepted answer)
PHP
$xml = simplexml_load_string($response);
echo $xml
->children("env", true)
->Body
->children("ns0", true)
->callwsapiResponseElement
->result
->data;
// This will print
{"name":"Paolo", "cognome": Rossi, "data":"18/11/1983"}
For me it is very curious the return of simplexml_load_string (https://www.php.net/manual/en/function.simplexml-load-string.php):
This function may return Boolean false, but may also return a non-Boolean value which evaluates to false. Please read the section on Booleans for more information. Use the === operator for testing the return value of this function.
And indeed if you replace the or die:
<?php
$response = <<<XML
<?xml version='1.0' encoding='UTF-8'?>
<env:Envelope
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns0="http://webservice/CrsWSApi.wsdl/types/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<ns0:callwsapiResponseElement>
<ns0:result>
<ns0:object>BTS</ns0:object>
<ns0:receiver>BTSV45_ANSF_PROD-AKITT</ns0:receiver>
<ns0:sender>ANSFP</ns0:sender>
<ns0:data>
{"name":"Paolo", "cognome": Rossi, "data":"18/11/1983"}
</ns0:data>
<ns0:errormess xsi:nil="true"/>
<ns0:errorcode xsi:nil="true"/>
<ns0:status>1</ns0:status>
<ns0:sessionid>akit1</ns0:sessionid>
</ns0:result>
</ns0:callwsapiResponseElement>
</env:Body>
</env:Envelope>
XML;
$xml = simplexml_load_string($response);
if($xml===false){
die();
}
echo $xml->xpath("//*[local-name() = 'receiver']")[0];
Results:
BTSV45_ANSF_PROD-AKITT
You can then use the namespaces to find your data. This is a nice post Reference - How do I handle Namespaces (Tags and Attributes with a Colon in their Name) in SimpleXML?

SOAP XML inside XML - How to parse with PHP

I've searched in the SO, but nothing what i've found could help me.
I'm doing system integration with JadLog, a freight service.
When I pass the query with the product and delivery variables, an XML is returned.
Return example:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<valorarResponse xmlns="">
<ns1:valorarReturn xmlns:ns1="http://jadlogEdiws">
<?xml version="1.0" encoding="utf-8" ?>
<string xmlns="http://www.jadlog.com.br/JadlogEdiWs/services">
<Jadlog_Valor_Frete>
<versao>1.0</versao>
<Retorno>1458,62</Retorno>
<Mensagem>Valor do Frete</Mensagem>
</Jadlog_Valor_Frete>
</string>
</ns1:valorarReturn>
</valorarResponse>
</soapenv:Body>
</soapenv:Envelope>
So, there is more than one XML declaration, right?
The only value that I need is the value of the freight, wich is in the tag Retorno. I this case, 1458,62.
What am I trying to do:
$your_xml_response = file_get_contents($url_project);
$clean_xml = str_replace('soapenv:', '', $your_xml_response);
$xml = simplexml_load_string($clean_xml);
var_dump($xml);
What its returns:
object(SimpleXMLElement)[1]
public 'Body' =>
object(SimpleXMLElement)[2]
public 'valorarResponse' =>
object(SimpleXMLElement)[3]
If I try to echo $xml->Retorno, it Returns empty.
How can I get the value of tag Retorno?
When I tried to load your XML string with simplexml_load_string it causes me
There is more than one XML declaration which is not allowed in xml . That's why I used regular express to get Retorno value. May be its not the best approach to use regex on XML but you can make a try.
<?php
$re = '/<Retorno>(.*?)<\/Retorno>/m';
$str = '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<valorarResponse xmlns="">
<ns1:valorarReturn xmlns:ns1="http://jadlogEdiws">
<?xml version="1.0" encoding="utf-8" ?>
<string xmlns="http://www.jadlog.com.br/JadlogEdiWs/services">
<Jadlog_Valor_Frete>
<versao>1.0</versao>
<Retorno>1458,62</Retorno>
<Mensagem>Valor do Frete</Mensagem>
</Jadlog_Valor_Frete>
</string>
</ns1:valorarReturn>
</valorarResponse>
</soapenv:Body>
</soapenv:Envelope>
';
preg_match_all($re, $str, $matches);
//debugging
print '<pre>';
print_r($matches);
print '<pre>';
//result
echo $matches[0][0];

Read Array Object from WSDL XML with namespaces in PHP

I am sending array of objects in XML Soap response from my java code to php in my project using WSDL.
I want to store values in php for my project.
I tried many ways but couldn't able to find how to parse my xml and read values. I am not export in xml area.
Please anyone help me for read my values from values.
My SOAP Response Body:
<soapenv:envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" \xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:body>
<getactualtimerequestresponse xmlns="http://impl.sample.com">
<getactualtimereturn>.
<ns1:projectlist xmlns:ns1="http://response.sample.com">
<item>
<ns2:userid xmlns:ns2="http://request.sample.com">4</ns2:userid>
<ns3:username xmlns:ns3="http://request.sample.com">Manoj Arun</ns3:username>
</item>
<item>
<ns5:userid xmlns:ns5="http://request.sample.com">5</ns5:userid>
<ns6:username xmlns:ns6="http://request.sample.com">Sethu Raman</ns6:username>
</item>
</ns1:projectlist>
<ns10:message xsi:nil="true" xmlns:ns10="http://response.sample.com"></ns10:message>
</getactualtimereturn>
</getactualtimerequestresponse>
</soapenv:body>
</soapenv:envelope>
projectList is my object created in java.
In PHP:
I tried to read like below but i didn't got anything.
foreach($xml->xpath('//ns:projectList') as $header)
{
foreach($header->item as $userIds)
{
echo $userIds->xpath('//ns:userId');
}
}
Thanks in advance...
$client = new SoapClient('http://url.com?wsdl');
$params = array(Java arg => php value);
$result = $client->Function( $parms );
print_r($result);

PHP - how to parse STRING field (remove some XML elements and all namespaces)?

I really need a help about string parsing in field containg not valid XML value.
I will display current value with target value to put in string field.
I have a field $xmlString with this value (elements are NOT in the SEPERATE lines but in the SAME line; it is web service response so I do not have impact on response only on later parsing):
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv=" http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<p:queryBillingAccountResponse xmlns:p="http://www.ibm.com">
<ns0:customerAccount xmlns:ns0=" http://www.ibm.com/2009">
<ComponentCustomerAccount>
<Name>ADSL 4</Name>
<CharacteristicValue>
<Characteristic>
<Name>Balance</Name>
</Characteristic>
<Value>0.0</Value>
</CharacteristicValue>
<AccountStatus>Paid</AccountStatus>
</ComponentCustomerAccount>
</ns0:customerAccount>
</p:queryBillingAccountResponse>
</soapenv:Body>
</soapenv:Envelope>
I want this output if this is possible:
<queryBillingAccountResponse>
<customerAccount>
<ComponentCustomerAccount>
<Name>ADSL 4</Name>
<CharacteristicValue>
<Characteristic>
<Name>Balance</Name>
</Characteristic>
<Value>0.0</Value>
</CharacteristicValue>
<CharacteristicValue>
<AccountStatus>Paid</AccountStatus>
</ComponentCustomerAccount>
</customerAccount>
</queryBillingAccountResponse>
So you will notice that I do not have first three lines (although they are not really seperate lines) and last two lines and I do not have namespaces defined for queryBilling AccountResponse and customer Account. I want these elements without namespace to be in string field. For both on start and end tag. I really need this output. How to parse this? I tried with SimpleXMLElement but could not parse it.
Thank you for your help
Updated output which can not be parsed by $xml = simplexml_load_string($text);
<<<XML
<?xml version="1.0" encoding="utf-8"?>
<Envelope>
<Body>
<queryBillingAccountResponse>
<customerAccount>
<ComponentCustomerAccount>
<Name>ADSL 4</Name>
<CharacteristicValue>
<Characteristic>
<Name>Balance</Name>
</Characteristic>
<Value>0.0</Value>
</CharacteristicValue>
<AccountStatus>Paid</AccountStatus>
</ComponentCustomerAccount>
</customerAccount>
</queryBillingAccountResponse>
</Body>
</Envelope>
XML>
In order to have a xml code that SimpleXML can understand, and since you don't need the namespaces declarations, the following code cleans the code before to apply it to simplexml_load_string
<?php
// if the XML comes from a file (or just assign the $text string)
$text = file_get_contents('myfile.xml');
$text = preg_replace('/(<\s*)\w+:/','$1',$text); // removes <xxx:
$text = preg_replace('/(<\/\s*)\w+:/','$1',$text); // removes </xxx:
$text = preg_replace('/\s+xmlns:[^>]+/','',$text); // removes xmlns:...
// the code should be clean enough for SimpleXML to parse it
$xml = simplexml_load_string($text);
// view the XML (and process it afterwards...)
print_r($xml);
To put the sample XML in a string (instead of a file)
<?php
$text = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv=" http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<p:queryBillingAccountResponse xmlns:p="http://www.ibm.com">
<ns0:customerAccount xmlns:ns0=" http://www.ibm.com/2009">
<ComponentCustomerAccount>
<Name>ADSL 4</Name>
<CharacteristicValue>
<Characteristic>
<Name>Balance</Name>
</Characteristic>
<Value>0.0</Value>
</CharacteristicValue>
<AccountStatus>Paid</AccountStatus>
</ComponentCustomerAccount>
</ns0:customerAccount>
</p:queryBillingAccountResponse>
</soapenv:Body>
</soapenv:Envelope>
XML;
$text = preg_replace('/(<\s*)\w+:/','$1',$text); // removes <xxx:
$text = preg_replace('/(<\/\s*)\w+:/','$1',$text); // removes </xxx:
$text = preg_replace('/\s+xmlns:[^>]+/','',$text); // removes xmlns:...
// the code should be clean enough for SimpleXML to parse it
$xml = simplexml_load_string($text);
// view the XML (and process it afterwards...)
print_r($xml);
To access elements, use -> (and [xx] for arrays), e.g
echo echo $xml->Body->queryBillingAccountResponse->customerAccount->ComponentCustomerAccount->Name . "\n";
will display
ADSL 4
SimpleXML doc

Parse XML in PHP by specific attribute

I need to get <name> and <URL> tag's value where subtype="mytype".How can do it in PHP?
I want document name and test.pdf path in my result.
<?xml version="1.0" encoding="UTF-8"?>
<test>
<required>
<item type="binary">
<name>The name</name>
<url visibility="restricted">c:/temp/test/widget.exe</url>
</item>
<item type="document" subtype="mytype">
<name>document name</name>
<url visiblity="visible">c:/temp/test.pdf</url>
</item>
</required>
</test>
Use SimpleXML and XPath, eg
$xml = simplexml_load_file('path/to/file.xml');
$items = $xml->xpath('//item[#subtype="mytype"]');
foreach ($items as $item) {
$name = (string) $item->name;
$url = (string) $item->url;
}
PHP 5.1.2+ has an extension called SimpleXML enabled by default. It's very useful for parsing well-formed XML like your example above.
First, create a SimpleXMLElement instance, passing the XML to its constructor. SimpleXML will parse the XML for you. (This is where I feel the elegance of SimpleXML lies - SimpleXMLElement is the entire library's sole class.)
$xml = new SimpleXMLElement($yourXml);
Now, you can easily traverse the XML as if it were any PHP object. Attributes are accessible as array values. Since you're looking for tags with specific attribute values, we can write a simple loop to go through the XML:
<?php
$yourXml = <<<END
<?xml version="1.0" encoding="UTF-8"?>
<test>
<required>
<item type="binary">
<name>The name</name>
<url visibility="restricted">c:/temp/test/widget.exe</url>
</item>
<item type="document" subtype="mytype">
<name>document name</name>
<url visiblity="visible">c:/temp/test.pdf</url>
</item>
</required>
</test>
END;
// Create the SimpleXMLElement
$xml = new SimpleXMLElement($yourXml);
// Store an array of results, matching names to URLs.
$results = array();
// Loop through all of the tests
foreach ($xml->required[0]->item as $item) {
if ( ! isset($item['subtype']) || $item['subtype'] != 'mytype') {
// Skip this one.
continue;
}
// Cast, because all of the stuff in the SimpleXMLElement is a SimpleXMLElement.
$results[(string)$item->name] = (string)$item->url;
}
print_r($results);
Tested to be correct in codepad.
Hope this helps!
You can use the XML Parser or SimpleXML.

Categories