I'm using PHP SoapClient() to consume a web service.
$sc = new SoapClient($uri);
$sc->MyAction($params);
$params would be an array to create the XML below, but I'm unsure how to format the array where there are multiple OrderLine keys.
<args>
<OrderLines>
<OrderLine>
<LineNumber>1</LineNumber>
<Product>Acme Foos</Product>
<Description>Bars and Bazzes</Description>
<Quantity>1</Quantity>
<Price>5.00</Price>
</OrderLine>
<OrderLine>
<LineNumber>2</LineNumber>
<Product>Kittens</Product>
<Description>Very sleepy</Description>
<Quantity>1</Quantity>
<Price>12.00</Price>
</OrderLine>
</OrderLines>
</args>
This is a dump of the array format I'm sending in, unsuccessfully because I'm not sure how to make the array with multiple OrderLine keys.
[args] => Array
(
[OrderLines] => Array
(
[0] => Array
(
[LineNumber] => 1
[Product] => 'Acme Foos'
[Description] => 'Bars and Bazzes'
[Quantity] => 1
[Price] => 5.00
)
[1] => Array
(
[LineNumber] => 2
[Product] => 'Kittens'
[Description] => 'Very sleepy'
[Quantity] => 1
[Price] => 12.00
)
)
)
When sending this to the web service, I am getting the following SoapFault exception:
Server was unable to read request. ---> There is an error in XML
document (2, 1001). ---> Input string was not in a correct format.
This example is correct. The error was not related to the array. It was some other invalid data in my params.
Related
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
I am using curl to get an external XML document and convert it to an array.
The code works almost perfect, apart from one node in the XML isn't being processed and put in my array.
Under <drivers> there is a code for each driver: <driver code="TM1"> but this doesn't get picked up by my array as an #attribute array like the others such as <collection date="20160324">
Does anybody know why this is happening?
XML:
<findit xmlns="http://www.URL.COM" version="8" type="TIX" date="20160323">
<account code="XXXXXX">
<customers>
<customer code="12345">
<status code="0">Success</status>
<logistic-jobs>
<logistic-job id="12345" date="20160324" status="PLA" modified="201603231420">
<number>
<number1>479599</number1>
<number3>11221</number3>
</number>
<collection date="20160324" time="0500">
<name>JOHN SMITH</name>
<address1>UNIT 3 DAVEY ROAD</address1>
<address2>FIELDS END BUSINESS PARK</address2>
<address3>GOLDTHORPE</address3>
<address4>ROTHERHAM</address4>
<address5>S63 0JF</address5>
</collection>
<delivery date="20160324" time="1200">
<address1>EXAMPLE</address1>
<address2>GLENEAFLES FARM</address2>
<address3>GLENEAGLES CLOSE</address3>
<address4>STANWELL, MIDDLESEX</address4>
<address5>TW19 7PD</address5>
</delivery>
<extra>
<address1>45FT C/SIDER</address1>
<address2>No</address2>
<address4>CEMENT</address4>
</extra>
<drivers>
<driver code="TM1">DAVE SMITH (DAYS)</driver>
</drivers>
<load weight="27600.00" volume="0.00">
<pallets full="23" half="0" quarter="0" blue="0" oversize="0"/>
</load>
</logistic-job>
</logistic-jobs>
</customer>
</customers>
</account>
</findit>
PHP:
$job_array = json_decode(json_encode(simplexml_load_string($xml)), true);
if(is_array($job_array['account']['customers']['customer'])) {
// Foreach customer in array
foreach($job_array['account']['customers']['customer'] as $i => $customer) {
// If status is set to success
if($customer['status'] == "Success") {
// For each job
foreach($customer['logistic-jobs']['logistic-job'] as $i => $job) {
echo '<pre>'; print_r($job); echo '</pre>';
}
}
}
}
OUTPUT:
Array
(
[#attributes] => Array
(
[id] => 12345
[date] => 20160324
[status] => PLA
[modified] => 201603231420
)
[number] => Array
(
[number1] => 479599
[number3] => 11221
)
[collection] => Array
(
[#attributes] => Array
(
[date] => 20160324
[time] => 0500
)
[name] => JOHN SMITH
[address1] => UNIT 3 DAVEY ROAD
[address2] => FIELDS END BUSINESS PARK
[address3] => GOLDTHORPE
[address4] => ROTHERHAM
[address5] => S63 0JF
)
[delivery] => Array
(
[#attributes] => Array
(
[date] => 20160324
[time] => 1200
)
[address1] => EXAMPLE
[address2] => GLENEAFLES FARM
[address3] => GLENEAGLES CLOSE
[address4] => STANWELL, MIDDLESEX
[address5] = TW19 7PD
)
[extra] => Array
(
[address1] => 45FT C/SIDER
[address2] => No
[address4] => CEMENT
)
[drivers] => Array
(
[driver] => DAVE SMITH (DAYS)
)
[load] => Array
(
[#attributes] => Array
(
[weight] => 21509.00
[volume] => 0.00
)
[pallets] => Array
(
[#attributes] => Array
(
[full] => 52
[half] => 0
[quarter] => 0
[blue] => 0
[oversize] => 0
)
)
)
)
I have a simple answer for you: don't convert XML to an array. SimpleXML has a really useful API for traversing through the XML document and finding the data you want; throw away the horrible json_decode(json_encode( hack and look at the examples in the PHP manual.
In this case, echo $driver would give you the contents (driver name) and echo $driver['code'] would give you the attribute value; clearly, a plain array can't have that convenient magic, which is why converting to one is giving you problems.
Just remember that print_r will also hide things from you, and you might want a dedicated debugging function.
Here's an example (with live demo) of getting the code and name of each driver:
$job_simple = simplexml_load_string($xml);
if(isset($job_simple->account->customers->customer)) {
// Foreach customer in array
foreach($job_simple->account->customers->customer as $i => $customer) {
// If status is set to success
if((string)$customer->status == "Success") {
// For each job
foreach($customer->{'logistic-jobs'}->{'logistic-job'} as $i => $job) {
echo "<ul>\n";
foreach ( $job->drivers->driver as $driver ) {
echo "<li> {$driver['code']}: $driver\n";
}
echo "</ul>\n";
}
}
}
}
I'm trying to parse some XML data using the php function simplexml_load_string() however it doesn't seem to work on elements where there is text and then further elements. Here is a sample data:
<?xml version="1.0"?>
<KPPRINT>
<CURRENTDATE>31/01/2015</CURRENTDATE>
<CURRENTTIME>21:43</CURRENTTIME>
<HEADER>
<ORDERNUMBER>CHECK NO: 69</ORDERNUMBER>
<CHECKNUMBER>ORDER NO: 16</CHECKNUMBER>
<REFTEXT>Party table</REFTEXT>
<REFTEXT>LOCATION 4</REFTEXT>
</HEADER>
<ITEM>VK Ice<QTY>1</QTY><PRICE>£0.03</PRICE><RECORD>57</RECORD><DEPT number="1" name="Wet Sales"/><GROUP number="5" name="FABs"/><CAT number="1" name="CATEGORY 1"/></ITEM>
<ITEM>Bramble<QTY>1</QTY><PRICE>£0.45</PRICE><RECORD>241</RECORD><DEPT number="1" name="Wet Sales"/><GROUP number="10" name="Cocktails"/><CAT number="1" name="CATEGORY 1"/></ITEM>
<ITEM>Gran Seleccion Rioja<QTY>1</QTY><PRICE>£19.95</PRICE><RECORD>225</RECORD><DEPT number="1" name="Wet Sales"/><GROUP number="9" name="Wines & Champagnes"/><CAT number="1" name="CATEGORY 1"/></ITEM>
<TRAILER>
<CLERK>Tom</CLERK>
<MACHINE>S000-T1</MACHINE>
<TIME>21:43</TIME>
<CONSEC>000023</CONSEC>
</TRAILER>
</KPPRINT>
This is the output I get:
Array
(
[CURRENTDATE] => 31/01/2015
[CURRENTTIME] => 21:43
[HEADER] => Array
(
[ORDERNUMBER] => CHECK NO: 69
[CHECKNUMBER] => ORDER NO: 16
[REFTEXT] => Array
(
[0] => Party table
[1] => LOCATION 4
)
)
[ITEM] => Array
(
[0] => Product 1
[1] => Product 2
[2] => Product 3
)
[TRAILER] => Array
(
[CLERK] => Tom
[MACHINE] => S000-T1
[TIME] => 21:43
[CONSEC] => 000023
)
)
The element text is there but the other elements within are not. Is there a simple option to the function or do I need to parse the whole string manually?
If it doesn't work with simplexml_load_string() you should try work with the data as recursive objects with DOMDocument because it allows to work with children nodes and I think it is not limited.
Please consider reading the following pages:
http://php.net/manual/en/class.domdocument.php
http://www.w3schools.com/php/php_xml_dom.asp
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 have loaded an XML file using
simplexml_load_file($filePath,'SimpleXMLElement', LIBXML_NOCDATA);
And for most of the XML provided it works fine. However, for some of the elements in the XML the attributes are not converted into an '#attributes' array, and are instead missing form the output. Here's a sample:
<UI_DEFINITION>
<EDIT_PERMISSION>testPermission</EDIT_PERMISSION>
<DEFAULT_VALUES>
<display>hidden</display>
<css_class>generic_css_class</css_class>
<title>{tag}</title>
<type>string</type>
<wrapper_format>{value}</wrapper_format>
<full_path>false</full_path>
<mandatory>false</mandatory>
<edit_permission>testPermission</edit_permission>
<max_length>0</max_length>
</DEFAULT_VALUES>
<LOOKUPS>
<DB_LOOKUP name="test3">
<VIEW>???</VIEW>
<ID_FIELD>???</ID_FIELD>
<DESCR_FIELD>???</DESCR_FIELD>
<ORDER>??? asc</ORDER>
</DB_LOOKUP>
<DB_LOOKUP name="test1">
<VIEW>???</VIEW>
<ID_FIELD>???</ID_FIELD>
<DESCR_FIELD>???</DESCR_FIELD>
<ORDER>??? asc</ORDER>
</DB_LOOKUP>
</LOOKUPS>
<AREA internal_name="main_details" title="" display="show">
<FIELD lookup="test1" title="Title">Title</FIELD>
<FIELD title="Name">Given_Name</FIELD>
<FIELD title="Mid. Name(s)">Middle_Names</FIELD>
<FIELD title="Family Name">Family_Name</FIELD>
<FIELD title="Gender">Gender</FIELD>
<FIELD title="Born" type="date">Date_of_Birth</FIELD>
<FIELD max_length="20" title="ID">Unique_Identifier</FIELD>
</AREA>
This gives the following output from print_r (I've added a line break at the bit that's the problem):
SimpleXMLElement Object ( [UI_DEFINITION] => SimpleXMLElement Object ( [EDIT_PERMISSION] => testPermission [DEFAULT_VALUES] => SimpleXMLElement Object ( [display] => hidden [css_class] => generic_css_class [title] => {tag} [type] => string [wrapper_format] => {value} [full_path] => false [mandatory] => false [edit_permission] => testPermission [max_length] => 0 ) [LOOKUPS] => SimpleXMLElement Object ( [DB_LOOKUP] => Array ( [0] => SimpleXMLElement Object ( [#attributes] => Array ( [name] => test3 ) [VIEW] => ??? [ID_FIELD] => ??? [DESCR_FIELD] => ??? [ORDER] => ??? asc ) [1] => SimpleXMLElement Object ( [#attributes] => Array ( [name] => test1 ) [VIEW] => ??? [ID_FIELD] => ??? [DESCR_FIELD] => ??? [ORDER] => ??? asc ) ) )
[AREA] => SimpleXMLElement Object ( [#attributes] => Array ( [internal_name] => main_details [title] => [display] => show ) [FIELD] => Array ( [0] => Title [1] => Given_Name [2] => Middle_Names [3] => Family_Name [4] => Gender [5] => Date_of_Birth [6] => Unique_Identifier ) ) ) )
As you can see, the attributes array is correctly added to most of the elements, but not to the FIELD elements. I've tried renaming them and it didn't seem to make a difference.
EDIT:
I should also add that I've tried surrounding the FIELD tags with a FIELDS tag, also to no avail.
EDIT:
I've simplified the XML hugely, and it still doesn't return anny attributes:
<UI_DEFINITION>
<FIELD lookup="test1" title="Title">Title</FIELD>
</UI_DEFINITION>
produces:
SimpleXMLElement Object ( [UI_DEFINITION] => SimpleXMLElement Object ( [FIELD] => Title ) )
The attributes are accessible, for example:
$obj = simplexml_load_string($xml);
foreach($obj->AREA->FIELD as $field)
{
echo $field->attributes()->title . '<br />';
}
print_r() does not always show the full structure with SimpleXML, but the attributes are there for use.
Sorry it's taken so long to come back and answer this question!
As MrCode suggested, the attributes were accessible. The problem I was in the serialisation of the SimpleXML object into another format. Using printr or json_convert on the while object resulted in the attributes not being available in the cases reported.
I didn't go far enough into this to find a code-based workaround for printing or converting these objects including the problematic cases, I simply worked around it as part of the XML data:
<UI_DEFINITION>
<FIELD lookup="test1" title="Title"><VALUEPATH>Title</VALUEPATH></FIELD>
</UI_DEFINITION>
Addint this extra level into the hierarchy resulted in the attributes being preserved at the top level, and the text value being available correctly at the sub-level.