I load some XML which produces a SimpleXML Object like so (only displaying one Object)
SimpleXMLElement Object
(
[Jobs] => SimpleXMLElement Object
(
[Job] => Array
(
[0] => SimpleXMLElement Object
(
[ID] => J000001
[Name] => Internal Time
[Description] => Use this job to record your internal and non-billable time for activities such as annual leave, sick leave, professional development, staff meetings etc
[Client] => SimpleXMLElement Object
(
[ID] => 8430219
[Name] => Fake Client
)
[ClientOrderNumber] => SimpleXMLElement Object
(
)
[State] => Planned
[StartDate] => 2016-03-21T00:00:00
[DueDate] => 2017-03-21T00:00:00
[InternalID] => 11442733
[Assigned] => SimpleXMLElement Object
(
[Staff] => SimpleXMLElement Object
(
[ID] => 344460
[Name] => Som Name
)
)
)
)
)
)
What I would like to do is create an array of the client Name and client ID. So I am aiming for something like so
[data] => array (
8430219 => Fake Client,
8430343 => Another Client,
etc
)
At the moment, I can get the name in place, but struggling with the id because it says it is an illegal offset type. This is what I have
foreach($oXML->Jobs as $oEntry) {
foreach ($oEntry->Job as $data) {
$jobsArray = array(
$data->Client->ID => $data->Client->Name
);
}
}
How can I create the array based on the SimpleXML Object I have?
Thanks
First of all, to obtain your desired array you can not use this syntax:
$jobsArray = array( $key => $val );
You have to use something like this:
$jobsArray[$key] = $val;
Otherwise, at each repeating loop, your syntax will override precedent array values.
Then, I suggest you to use XPath to simplify entire process. With XPath queries, you can retrieve a set of nodes with only one search.
Assuming you have this XML:
<?xml version="1.0"?>
<Jobs>
<Job>
<Client>
<ID>8430219</ID>
<Name>Fake Client</Name>
</Client>
<Client>
<ID>8430220</ID>
<Name>Fake Client 2</Name>
</Client>
</Job>
<Job>
<Client>
<ID>8430221</ID>
<Name>Fake Client 3</Name>
</Client>
</Job>
</Jobs>
With this xpath query:
$clients = $oXML->xpath( '/Jobs/Job/Client' );
you obtain in $clients all <Client> nodes. Then you can create your array in this way:
$result = array();
foreach( $clients as $client )
{
$result[$client->ID->__toString()] = $client->Name->__toString();
}
This is $result after foreach loop:
Array
(
[8430219] => Fake Client
[8430220] => Fake Client 2
[8430221] => Fake Client 3
)
Note that we have to cast as string single nodes (that are SimpleXML objects): this is absolutely necessary creating keys, otherwise the array assignment fails. You can obtain same result using (string) $client->ID instead of $client->ID->__toString()
Read more about SimpleXMLElement::xpath
Read more about XPath
Related
Test XML:
<?xml version="1.0" encoding="UTF-8"?>
<Transfer>
<ABR recordLastUpdatedDate="20180329" replaced="N">
<ABN status="ACT" ABNStatusFromDate="20000214">80007321682</ABN>
<EntityType>
<EntityTypeInd>PUB</EntityTypeInd>
<EntityTypeText>Australian Public Company</EntityTypeText>
</EntityType>
<MainEntity>
<NonIndividualName type="MN">
<NonIndividualNameText>BLACK CABS COMBINED PTY LTD</NonIndividualNameText>
</NonIndividualName>
<BusinessAddress>
<AddressDetails>
<State>VIC</State>
<Postcode>3166</Postcode>
</AddressDetails>
</BusinessAddress>
</MainEntity>
</ABR>
</Transfer>
PHP Script:
$f='test.xml';
$reader=new XMLReader();
$reader->open($f);
while($reader->read()){
if($reader->nodeType==XMLReader::ELEMENT && $reader->name=='ABR'){
$doc=new DOMDocument('1.0','UTF-8');
$xml=simplexml_import_dom($doc->importNode($reader->expand(),true));
print_r($xml);
}
}
$reader->close();
PHP Output:
SimpleXMLElement Object
(
[#attributes] => Array
(
[recordLastUpdatedDate] => 20180329
[replaced] => N
)
[ABN] => 80007321682
[EntityType] => SimpleXMLElement Object
(
[EntityTypeInd] => PUB
[EntityTypeText] => Australian Public Company
)
[MainEntity] => SimpleXMLElement Object
(
[NonIndividualName] => SimpleXMLElement Object
(
[#attributes] => Array
(
[type] => MN
)
[NonIndividualNameText] => BLACK CABS COMBINED PTY LTD
)
[BusinessAddress] => SimpleXMLElement Object
(
[AddressDetails] => SimpleXMLElement Object
(
[State] => VIC
[Postcode] => 3166
)
)
)
)
The Problem:
The attributes for the ABN element (status and ABNStatusFromDate) are not in the output, even though other attributes are.
Please help me understand why those attributes in particular are missing.
PS - Dummy text so SO doesn't give me warnings about my post being mostly code
Answer: print_r is not meant to be used to display a SimpleXML object.
I can access the attribute directly via $xml->ABN['status'].
I'm using the namecheap API to do some stuff, it's the first time I've used a API and I'm running into a bit of a problem.
This is what I have so far:
$ApiKey = "**********************";
$ApiUser = "*****";
$UserName = "*********";
$ClientIP = "********";
$NamecheapURI = "https://api.namecheap.com/xml.response";
$executionURL = $NamecheapURI."?ApiUser=".$ApiUser."&ApiKey=".$ApiKey."&UserName=".$UserName."&Command=namecheap.domains.check&ClientIp=".$ClientIP."&DomainList=".$domain;
$xml = simplexml_load_file($executionURL);
print_r($xml);
When print $xml I am returned simple XML objects:
SimpleXMLElement Object
(
[#attributes] => Array
(
[Status] => OK
)
[Errors] => SimpleXMLElement Object
(
)
[Warnings] => SimpleXMLElement Object
(
)
[RequestedCommand] => namecheap.domains.check
[CommandResponse] => SimpleXMLElement Object
(
[#attributes] => Array
(
[Type] => namecheap.domains.check
)
[DomainCheckResult] => SimpleXMLElement Object
(
[#attributes] => Array
(
[Domain] => facebook.com
[Available] => false
[ErrorNo] => 0
[Description] =>
[IsPremiumName] => false
[PremiumRegistrationPrice] => 0
[PremiumRenewalPrice] => 0
[PremiumRestorePrice] => 0
[PremiumTransferPrice] => 0
[IcannFee] => 0
[EapFee] => 0
)
)
)
[Server] => PHX01APIEXT03
[GMTTimeDifference] => --5:00
[ExecutionTime] => 0.008
)
My question is beyond this, how do I move forward and pull data from this?
I've tried treating this as an array but I am getting nowhere, when using is_array() to test if it was an array it says it's not which I don't understand...
I apologise if this is a noob question, I am a bit new to this. In short, what do I need to do to pull data from this?
Thanks in advance!
Learning to use SimpleXML is much better than trying to convert it to arrays/json/anything else and simple (hence the name). A quick example...
$response = '<?xml version="1.0" encoding="UTF-8"?>
<CommandResponse Type="namecheap.domains.check">
<DomainCheckResult Domain="facebook.com">
<Element>1234</Element>
<Element>12345</Element>
</DomainCheckResult>
</CommandResponse>';
$xml = simplexml_load_string($response);
echo "DOmain=".$xml->DomainCheckResult['Domain'].PHP_EOL;
foreach ( $xml->DomainCheckResult->Element as $value) {
echo "Value=".(string)$value.PHP_EOL;
}
outputs...
DOmain=facebook.com
Value=1234
Value=12345
You have to adapt this to your own XML, but the idea is that if you want to access an element of an item you use object notation -> and if you need to get an attribute, use array notation [].
So in the above code, the first echo ($xml->DomainCheckResult['Domain']) gets the <DomainCheckResult> element and outputs the Domain attribute.
Then the foreach loop says fetch each <Element> within <DomainCheckResult> and output the value.
I have searched for this and the answers I find seem to say what I thought I understand. Obviously I am missing something. I am confused at the results from the xPath query. I have simplified my problem for a test case to post here.
My real xml has several dataset nodes at different depths. Ultimately, I want to get every dataset element with a given label and then loop over that and get the field values (at different locations (or depths) so I think I need xpath). I can use xpath to get the dataset elements that I want successfully. However, when I then run xpath on that result object, it gets me the fields I want and all the other fields too. I can't figure out why it isn't only returning field1, field2, and field3. When I print_r($value[0]), it shows only the fields I want. But, when I run xpath on $value[0], it returns all fields in the xml doc.
Sample XML
<myxml>
<dataset label="wanteddata" label2="anotherlabel">
<dataitem>
<textdata>
<field label="label1">field1</field>
<field label="label2">field2</field>
<field label="label3">field3</field>
</textdata>
</dataitem>
</dataset>
<dataset label="unwanteddata" label2="unwantedanotherlabel">
<dataitem>
<textdata>
<field label="label4">field4</field>
<field label="label5">field5</field>
<field label="label6">field6</field>
</textdata>
</dataitem>
</dataset>
</myxml>
Here is the test code.
$xmlstring = file_get_contents('simplexml_test.xml');
$xml = simplexml_load_string($xmlstring);
if ($xml === false) {
throw new Exception("Failed to load");
}
$value = $xml->xpath('//dataset[#label="wanteddata"]');
print_r($value[0]->xpath('//field'));
Code Output:
Array
(
[0] => SimpleXMLElement Object
(
[#attributes] => Array
(
[label] => label1
)
)
[1] => SimpleXMLElement Object
(
[#attributes] => Array
(
[label] => label2
)
)
[2] => SimpleXMLElement Object
(
[#attributes] => Array
(
[label] => label3
)
)
[3] => SimpleXMLElement Object
(
[#attributes] => Array
(
[label] => label4
)
)
[4] => SimpleXMLElement Object
(
[#attributes] => Array
(
[label] => label5
)
)
[5] => SimpleXMLElement Object
(
[#attributes] => Array
(
[label] => label6
)
)
)
//field selects all <field> elements within the entire XML document regardless of the context node from which you call that XPath. To make the XPath heed the context node, you need to add a dot (.) at the beginning of the XPath. In XPath, (.) references current context node :
print_r($value[0]->xpath('.//field'));
I am using an API to get a Block FIPS number but I have not been able to target that specific number within the XML file.
I did a print_r() on the xml output and here is what I get
SimpleXMLElement Object ( [#attributes] => Array ( [status] => OK [executionTime] => 6 ) [Block] => SimpleXMLElement Object ( [#attributes] => Array ( [FIPS] => 060730200252015 ) ) [County] => SimpleXMLElement Object ( [#attributes] => Array ( [FIPS] => 06073 [name] => San Diego ) ) [State] => SimpleXMLElement Object ( [#attributes] => Array ( [FIPS] => 06 [code] => CA [name] => California ) ) )
Here is the XML that is being generated
<Response xmlns="http://data.fcc.gov/api" status="OK" executionTime="10">
<Block FIPS="060730200252015"/>
<County FIPS="06073" name="San Diego"/>
<State FIPS="06" code="CA" name="California"/>
</Response>
I have been trying to get the Block FIPS Number like this:
$fccAPI = "http://data.fcc.gov/api/block/2010/find?latitude=$lat&longitude=$lng";
//echo $fccAPI;
$fccXML= simplexml_load_file($fccAPI);
print_r($fccXML);
//Echo FIPS Number
echo $fccXML->FIPS;
Please help me target the Block FIPS number.
You need to use the following:
echo $fccXML->Block[0]['FIPS'];
$fccXML is the root node, the <Response> element. ->Block[0] selects the first Block element, and to access an attribute, use the square brackets notation with the attribute name, i.e. ['FIPS'].
The SimpleXML documentation has numerous examples if you're having trouble with the syntax.
I am getting a SOAP response for a SOAP call perfectly using SOAP ui but when I call the same in php, I am not able to traverse to the desired element(CreditId in this case), which I want.
The following is the SOAP response that I get using SOAP ui :
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header/>
<soap-env:Body>
<n0:getProjectCreditListResponse xmlns:n0="urn:sap-com:document:sap:soap:functions:mc-style">
<EUserGuid>33/XIcx+3/GxWABQVoJXWA==</EUserGuid>
<EtCurrCreditList>
<item>
<PhaseId/>
<CreditcategoryDescrption>Project Information Forms</CreditcategoryDescrption>
<CreditId>CSD1GSP1L-1000008140</CreditId>
</item>
<item>
<PhaseId/>
<CreditcategoryDescrption>Project Information Forms</CreditcategoryDescrption>
<CreditId>CSD1GSP2L-1000008140</CreditId>
</item>
</EtCurrCreditList>
<EtErrorLogInfo/>
</n0:getProjectCreditListResponse>
</soap-env:Body>
</soap-env:Envelope>
Now I have gone through the various similar questions on the site, where it is advised to do like this to get the desired element :
$client = new SoapClient('wsdl file path',array('trace'=>1,'exceptions'=>1);
$res = $client->getCreditFormDataXml(array(input arguments));
$xml = simplexml_load_string($res);
$xml->registerXPathNamespace('soap-env', 'http://schemas.xmlsoap.org/soap/envelope/');
$xml->registerXPathNamespace('n0', 'urn:sap-com:document:sap:soap:functions:mc-style');
foreach ($xml->xpath('//EtCurrCreditList//item//CreditId') as $item)
{
var_dump($item);
}
However I get an error stating that
Warning: simplexml_load_string() expects parameter 1 to be string
I have tried converting the $res variable to a string but it gives an error that
Object of class stdClass could not be converted to string
But if I do var_dump($res), I get output like this :
object(stdClass)[2]
public 'EUserGuid' => string 'ß×!Ì~ßñ±X�PV‚WX' (length=16)
public 'EtCurrCreditList' =>
object(stdClass)[3]
public 'EtErrorLogInfo' =>
object(stdClass)[4]
Why is the code not going to the sub nodes of the EtCurrCreditList so that I can then process it to get the desired value. - Solved
Final Output :
stdClass Object
(
[EUserGuid] => ß×!Ì~ßñ±XPV‚WX
[EtCurrCreditList] => stdClass Object
(
[item] => Array
(
[0] => stdClass Object
(
[PhaseId] =>
[PhaseDescription] =>
[CreditcategoryId] => CSD1GSL-1000008140
[CreditcategoryDescrption] => Project Information Forms
[CreditId] => CSD1GSP1L-1000008140
)
[1] => stdClass Object
(
[PhaseId] =>
[PhaseDescription] =>
[CreditcategoryId] => CSD1GSL-1000008140
[CreditcategoryDescrption] => Project Information Forms
[CreditId] => CSD1GSP2L-1000008140
)
[2] => stdClass Object
(
[PhaseId] =>
[PhaseDescription] =>
[CreditcategoryId] => CSD1GSL-1000008140
[CreditcategoryDescrption] => Project Information Forms
[CreditId] => CSD1GSP3L-1000008140
)
)
)
[EtErrorLogInfo] => stdClass Object
(
)
use echo"<pre>";print_r($res); you will be better able to understand how to traverse or show us the result.
$res=stdClass Object ( [EUserGuid] => ß×!Ì~ßñ±XPV‚WX [EtCurrCreditList] => stdClass Object ( ) [EtErrorLogInfo] => stdClass Object ( ) )
Now use
foreach($res->EUserGuid as $data)
{
//Your logic
}
same for EtCurrCreditList and EtErrorLogInfo and also nested loops if these object contain anything.
updating according to your new output.
you can iterate it like this
$res->EUserGuid=$something;
foreach($res->EtCurrCreditList->item as $eachItem)
{
$eachItem->PhaseId=$your_ wish;
$eachItem->PhaseDescription==$your_ wish;
$eachItem->CreditcategoryId==$your_ wish;
$eachItem->CreditcategoryDescrption==$your_ wish;
$eachItem->CreditId=$your_ wish;
}
and can ignore EtErrorLogInfo