Accessing CDATA values in a simplexmliterator - php

I have a simple xml structure:
<?xml version="1.0"?>
<fmxmlsnippet type="FMObjectList">
<Step enable="True" id="141" name="Set Variable">
<Value>
<Calculation><![CDATA[Get(RecordID)]]></Calculation>
</Value>
<Repetition>
<Calculation><![CDATA[1]]></Calculation>
</Repetition>
<Name>$currentRecord</Name>
</Step>
</fmxmlsnippet>
What I'm trying to do is pull out the attribute values for the Step node as well as the CDATA values form the Calculation child node for the Value and Repetition nodes.
I can get the step attribute values without an issue:
$iterator = new SimpleXMLIterator($xml);
for($iterator->rewind() ; $iterator->valid() ; $iterator->next()){
$stepId = $iterator->current()->attributes()->id->__toString();
$stepName = $iterator->current()->attributes()->name->__toString();
$stepEnable= $iterator->current()->attributes()->enable->__toString();
}
But I'm having a problem getting the CDATA. I've tried numerous methods of getting it without success.
I noticed at one point that when I die and var_dump the result of $iterator->current() in my for loop the CDATA doesn't even look like it is recognized:
object(SimpleXMLIterator)#3 (4) {
["#attributes"]=>
array(3) {
["enable"]=>
string(4) "True"
["id"]=>
string(3) "141"
["name"]=>
string(12) "Set Variable"
}
["Value"]=>
object(SimpleXMLIterator)#4 (1) {
["Calculation"]=>
object(SimpleXMLIterator)#6 (0) {
}
}
["Repetition"]=>
object(SimpleXMLIterator)#5 (1) {
["Calculation"]=>
object(SimpleXMLIterator)#6 (0) {
}
}
["Name"]=>
string(14) "$currentRecord"
}
It's like the content of the Calculation nodes is empty.
Is there a way of getting the CDATA?

No, that var_dump() is just misleading, it shows its empty but its it is there. Simply cast it then assign:
for($iterator->rewind() ; $iterator->valid() ; $iterator->next()){
$stepId = $iterator->current()->attributes()->id->__toString();
$stepName = $iterator->current()->attributes()->name->__toString();
$stepEnable= $iterator->current()->attributes()->enable->__toString();
$calculation = (string) $iterator->current()->Value->Calculation;
$repitition = (string) $iterator->current()->Repetition->Calculation;
echo $calculation . '<br/>' . $repitition;
}
Sample Output

Related

Loop through an XML object's field values PHP

I have an XML that looks like this...
<fields>
<field>
<id>86</id>
<source>system</source>
<type>integer</type>
<name>bounce_count</name>
<label>Bounce count</label>
<validation/>
<store-locally>true</store-locally>
<display-in-profile>false</display-in-profile>
<include-in-export>false</include-in-export>
<lma_display>false</lma_display>
<newsletters></newsletters>
</field>
</fields>
I need to loop in through the value of every tag inside <field>. I tried the following (result is the XML), first I got everything within the field tag...
$value = $result->field
$value = simplexml_load_string($value);
If I var_dump() this it looks as so...
object(SimpleXMLElement)#47 (11) { ["id"]=> string(3) "153" ["source"]=> string(6) "client" ["type"]=> string(6) "string" ["name"]=> string(4) "name" ["label"]=> string(4) "Name" ["validation"]=> object(SimpleXMLElement)#48 (0) { } ["store-locally"]=> string(4) "true" ["display-in-profile"]=> string(4) "true" ["include-in-export"]=> string(4) "true" ["lma_display"]=> string(4) "true" ["newsletters"]=> object(SimpleXMLElement)#46 (0) { } }
Next I try looping through the fields inside field
foreach ($value as $single_field) {
echo $single_field;
}
But I get no output. If I var_dump() $single_field I get the same result as above. I need to loop through the values within each field. So for the XML above I need to store the following values...
86
system
integer
bounce_count
Bounce Count
true
false
false
false
How can I do this?
If you are trying to iterate over children of the element, you need to use SimpleXMLElement::children in your foreach loop:
foreach ($value->children() as $single_field) {
echo $single_field . "\n";
}

SimpleXMLElement get non-attribute values

I have the following SimpleXMLElement:
object(SimpleXMLElement)#10 (3) {
["#attributes"]=> array(3) {
["id"]=> string(8) "18022352"
["name"]=> string(14) "The Salmon Man"
["slug"]=> string(14) "the-salmon-man"
}
["bids"]=> object(SimpleXMLElement)#11 (1) {
["price"]=> array(1) {
[0]=> object(SimpleXMLElement)#13 (1) {
["#attributes"]=> array(4) {
["decimal"]=> string(4) "9.60"
["percent"]=> string(5) "10.42"
["backers_stake"]=> string(5) "40.36"
["liability"]=> string(6) "347.00"
}
}
}
}
["offers"]=> object(SimpleXMLElement)#12 (1) {
["price"]=> array(1) {
[0]=> object(SimpleXMLElement)#15 (1) {
["#attributes"]=> array(4) {
["decimal"]=> string(4) "9.20"
["percent"]=> string(5) "10.87"
["backers_stake"]=> string(5) "85.35"
["liability"]=> string(5) "10.41"
}
}
}
}
}
Why does this work:
$horse[0]['name']
but this doesn't:
$horse[0]['bids'] // also tried $horse['bids'] and other ways
I can get the values like below but I was hoping to search the smaller object:
$xml->xpath("//odds/event[#id='$matchid']/market[#slug='to-win']/contract[#name='$pony']/bids"); // $pony == $horse[0]['name']
It's often easier to look at the XML itself, rather than a print_r of the SimpleXML object. In this case, you have something like this:
<horse id="18022352" name="The Salmon Man" slug="the-salmon-man">
<bids>
<price decimal="9.60" percent="10.42" backers_stake="40.36" liability="347.00" />
</bids>
<offers>
<price decimal="9.60" percent="10.42" backers_stake="40.36" liability="347.00" />
</offers>
</horse>
The bids item is an element, and as discussed in the SimpleXML Basic Usage guide, you access child elements with ->foo notation, whereas attributes use ['foo'] notation.
So if $horse is that <horse> element, you need:
$name = $horse['name'];
$bids = $horse->bids;
Note that this is equivalent to explicitly asking for the first child called bids; both forms will work regardless of whether there is actually more than one identically name element:
$bids = $horse->bids[0];
Now there are presumably one or more <price> elements in practice, so you might want to loop over, and then echo each decimal attribute. That would look like this:
foreach ( $horse->bids->price as $price ) {
echo $price['decimal'];
}
Again, this loop will work fine with only one <price>, it will just just loop once. If there is only ever one price, or you only care about the first one, you could just write:
echo $horse->bids->price['decimal'];
Which is equivalent to:
echo $horse->bids[0]->price[0]['decimal'];
Or of course either of these:
echo $horse->bids[0]->price['decimal'];
echo $horse->bids->price[0]['decimal'];
The number of different ways of getting to the same place is one reason I don't recommend relying too much on print_r output. Another is that it sometimes can't display everything that's in the XML, which doesn't mean that data isn't available if you ask for it.

XML multidimentional array php from URL [duplicate]

This question already has answers here:
SimpleXML: Working with XML containing namespaces
(3 answers)
Closed 7 years ago.
I'm breaking my head about this one for 2 days now, so I hope someone can help a bit.
I need the data from an XML, which I get from a website. The code returns a nice list, but missing the time array (array from an array).
Here is my code :
<?php
$url="http://publications.elia.be/Publications/Publications/WindForecasting.v1.svc/GetForecastGraphDataXml?beginDate=2015-05-13&endDate=2015-05-18&isOffshore=&isEliaConnected=";
echo $url;
$sxml = Simplexml_load_file($url);
var_dump($sxml);
foreach ($sxml ->ForecastGraphItems ->WindForecastingGraphItem as $type){
echo 'FC ';
echo $type ->Forecast."<br>";
echo 'LF ';
echo $type ->LoadFactor."<br>";
echo 'DT ';
echo $type ->Time[0]->DateTime;
echo "<br>";
echo 'BID ';
echo $type ->Bid."<br>";
echo 'RA ';
echo $type ->RunningAverage."<br>";
echo "<br>";
}
?>
The XML format I get when I just manualy open the website looks like the code below, or check the Original website : http://publications.elia.be/Publications/Publications/WindForecasting.v1.svc/GetForecastGraphDataXml?beginDate=2015-05-13&endDate=2015-05-18&isOffshore=&isEliaConnected=
<?xml version="1.0" encoding="UTF-8"?>
<WindForecastingGraphDataResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Elia.PublicationService.DomainInterface.WindForecasting.v1">
<ErrorMessage i:nil="true"/>
<ForecastGraphItems>
<WindForecastingGraphItem>
<Bid>No</Bid>
<Forecast>571.45</Forecast>
<LoadFactor>0.32</LoadFactor>
<RunningAverage>585.59</RunningAverage>
<Time xmlns:a="http://schemas.datacontract.org/2004/07/System">
<a:DateTime>2015-05-12T22:00:00Z</a:DateTime>
<a:OffsetMinutes>120</a:OffsetMinutes>
</Time>
</WindForecastingGraphItem>
<WindForecastingGraphItem>
<Bid>No</Bid>
<Forecast>562.95</Forecast>
<LoadFactor>0.32</LoadFactor>
<RunningAverage>578.47</RunningAverage>
<Time xmlns:a="http://schemas.datacontract.org/2004/07/System">
<a:DateTime>2015-05-12T22:15:00Z</a:DateTime>
<a:OffsetMinutes>120</a:OffsetMinutes>
</Time>
Now, when I var_dump($sxml), or if I try to get it, the sub-array for TIME dropped out... (see below).
I also tried foreach ($sxml ->ForecastGraphItems ->WindForecastingGraphItem -> Time as $time){ ..., which worked, but returned empty.
Anyone can help why the 'time' array is empty ?
Below the echo for var_dump:
http://publications.elia.be/Publications/Publications/WindForecasting.v1.svc/GetForecastGraphDataXml?beginDate=2015-05-13&endDate=2015-05-18&isOffshore=&isEliaConnected=object(SimpleXMLElement)#1
(2) { ["ErrorMessage"]=> object(SimpleXMLElement)#2 (0) { }
["ForecastGraphItems"]=> object(SimpleXMLElement)#3 (1) {
["WindForecastingGraphItem"]=> array(481) { [0]=>
object(SimpleXMLElement)#4 (5) { ["Bid"]=> string(2) "No"
["Forecast"]=> string(6) "571.45" ["LoadFactor"]=> string(4) "0.32"
["RunningAverage"]=> string(6) "585.59" ["Time"]=>
object(SimpleXMLElement)#485 (0) { } } [1]=>
object(SimpleXMLElement)#5 (5) { ["Bid"]=> string(2) "No"
["Forecast"]=> string(6) "562.95" ["LoadFactor"]=> string(4) "0.32"
["RunningAverage"]=> string(6) "578.47" ["Time"]=>
object(SimpleXMLElement)#485 (0) { } }
As michi has commented already you'd like to get access to elements that are in a different namespace:
<Time xmlns:a="http://schemas.datacontract.org/2004/07/System">
<a:DateTime>2015-05-12T22:15:00Z</a:DateTime>
<a:OffsetMinutes>120</a:OffsetMinutes>
</Time>
The <Time> element sets for all it's children the prefix a to the XML namespace http://schemas.datacontract.org/2004/07/System. You're interested in two children with that prefix:
<a:DateTime>2015-05-12T22:15:00Z</a:DateTime>
<a:OffsetMinutes>120</a:OffsetMinutes>
You do this by telling the namespace of the children you're interested in, e.g. for the first element:
$type ->Time->children('http://schemas.datacontract.org/2004/07/System')->DateTime;
And that's it already.
Now to correct some misunderstanding:
that is not an array
yes, var_dump told you it's one, but it's just not.
var_dump and print_r are not particular useful with SimpleXMLElement as they lie about it. SimpleXMLElement::asXML() shows you the concrete XML, always.

Accessing a specific XML-node with PHP using SimpleXML

lately i ran into a problem using simplexml.
What i want to do is to get a value of a nested node that occurs multiple times.
The xml looks somewhat like this:
<response>
<album id="123">
[...]
<duration>
<value format="seconds">2576</value>
<value format="mm:ss">42:56</value>
<value format="hh:mm:ss">00:42:56</value>
<value format="xs:duration">PT42M56S</value>
</duration>
[...]
</album>
</response>
Specifically i need the value of the the <value format="hh:mm:ss"> node.
So I have a reference to the object that looks somewhat like this:
$this->webservice->album->duration->value;
Now, if i var_dump this the result will be:
object(SimpleXMLElement)#117 (5) {
["#attributes"]=> array(1) {
["format"]=> string(7) "seconds"
}
[0]=> string(4) "2576"
[1]=> string(5) "42:56"
[2]=> string(8) "00:42:56"
[3]=> string(8) "PT42M56S"
}
I do not understand this output, since it takes the format-attribute of the first node (seconds) and continues with the node-values in the array, while ignoring the format-attribute of the following nodes completely.
Furthermore, if i do the following:
$this->webservice->album->duration->value[2];
it results in:
object(SimpleXMLElement)#108 (1) {
["#attributes"]=> array(1) {
["format"]=> string(8) "hh:mm:ss"
}
}
where i haven't got a value to address at all.
I tried to use xpath instead too in the following way:
$this->webservice->album->duration->xpath('value[#format="hh:mm:ss"]');
which results in:
array(1) {
[0]=> object(SimpleXMLElement)#116 (1) {
["#attributes"]=> array(1) {
["format"]=> string(8) "hh:mm:ss"
}
}
}
So my question is:
What am I doing wrong? xD
Thanks in advance for any helpful advice :)
Your mistake is in trusting var_dump too completely, rather than trying to use the elements based on the examples in the manual.
On your first attempt, you accessed $duration_node->value; this can be used in a few different ways:
if you iterate over it with foreach($duration_node->value as $value_node), you get each of the <value> elements in turn
you can access specific elements by index, e.g. $duration_node->value[2] for the 3rd element
if you treat it as a single element, SimpleXML assumes you want the first element, i.e. echo $duration_node->value is the same as echo $duration_node->value[0]
Your second example worked fine - it found the <value> element with an attribute format="hh:mm:ss". The xpath() method always returns an array, so you need to check that it's not empty then look at the first element.
Once you have the right element, accessing its text content is as simple as casting it to a string ((string)$foo), or passing it to something that always needs a string (e.g. echo).
So this would work:
$xpath_results = $this->webservice->album->duration->xpath('value[#format="hh:mm:ss"]');
if ( count($xpath_results) != 0 ) {
$value = (string)$xpath_results[0];
}
As would this:
foreach ( $this->webservice->album->duration->value as $value_node ) {
if ( $value_node['format'] == 'hh:mm:ss' ) {
$value = (string)$value_node;
break;
}
}

PHP XML echoing values contained in a node

I have a XML doc , with 3 article nodes in it , each Article contains :
Title
Image
Link
I need to be able to get the Title/Image/Link values out of the node.
$query = $xpath->query('//section/article');
foreach($query as $currentArticle => $artContents):
print_r( $artContents->title);
endforeach
;
This doesn't work for me, I can use ->NodeName and nodeValue , but they dont drill down enough , just display 'article' or the whole contents respectively.
To explain more:
My XML is
<article>
<title>Title </title>
<link>http:// </link>
<img>image src </img>
</article>
and the output I require is :
Title->Title
Link->http://
etc.
Update to explain :
foreach($query as $articles):
foreach($articles->childNodes as $childNode) {
if ($childNode->nodeType === XML_ELEMENT_NODE) {
$stored = $childNode->nodeValue;
array_push( $availAds, $stored );
}
}
endforeach;
is what I currently have thanks to Gordon . This though makes my array look like:
//previous values:
}
["1300884672_071.jpg"]=>
array(3) {
["image"]=>
string(30) "1300884672_071.jpg"
["title"]=>
string(6) "secind title"
["link"]=>
string(10) "grtgrtgrtg"
}
["1300884618_071.jpg"]=>
array(3) {
["image"]=>
string(30) "1300884618_071.jpg"
["title"]=>
string(5) "first title"
["link"]=>
string(10) "http://www.google.com"
}
//updated values that
[0]=>
string(6) "My Title"
[1]=>
string(89) "/1300961550.jpg"
[2]=>
string(22) "rtherherhgerg thursada"
[3]=>
string(20) "custome 222222222222"
I obviously need my array to be consistent, but cannot work out how to do that. Thanks for your patience.
Bob
I'm still not sure what the problem is, but are you looking for
foreach($query as $articles):
foreach($articles->childNodes as $childNode) {
if ($childNode->nodeType === XML_ELEMENT_NODE) {
printf("%s->%s%s", $childNode->nodeName, $childNode->nodeValue, PHP_EOL);
}
}
}
This will iterate over all the DOMElement nodes that are direct children of the $article nodes returned in the query and print them in this format "nodename->nodevalue" and a newline.

Categories