Forum Moderators: coopster

Message Too Old, No Replies

Getting attribute from XML

using simplexml_load_string

         

csdude55

4:15 am on Jan 23, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I have an XML section that looks like this:

<data foo="bar">
<time-layout foo="bar">
<start-valid-time period-name="Tonight">2020-01-22T18:00:00-05:00</start-valid-time>
<start-valid-time period-name="Tomorrow">2020-01-23T06:00:00-05:00</start-valid-time>
</time-layout>
</data>


I've been using this to convert the XML to an array:

$xml = simplexml_load_string($contents);
$json = json_encode($xml);
$xml_array = json_decode($json, TRUE);


But simplexml_load_string() loses the period-name value that I'd really like to keep! I just end up with:

SimpleXMLElement Object
(
[@attributes] => Array
(
[version] => 1.0
)

[data] => Array
(
[0] => SimpleXMLElement Object
(
[time-layout] => Array
(
[0] => SimpleXMLElement Object
(
[@attributes] => Array
(
[time-coordinate] => local
[summarization] => 12hourly
)

[start-valid-time] => Array
(
[0] => 2020-01-22T18:00:00-05:00
[1] => 2020-01-23T06:00:00-05:00



Can you guys think of a way that I can get the period-name value? The only idea I've had is to use preg_match_all() to manually rip $contents, then create my own array index for it.

w3dk

1:04 am on Jan 24, 2020 (gmt 0)

10+ Year Member Top Contributors Of The Month



But simplexml_load_string() loses the period-name value


It's not simplexml_load_string() that loses the attribute, but the conversion to JSON. The "problem" is that you can't necessarily treat the SimpleXMLElement "Object" as an ordinary object - under the hood it's a resource. You can't simply dump the object as a string, consequently it is non-serializable. All the information is there, you just need to interact with it as a SimpleXMLElement object.

For example, to access the "start-valid-time" elements with the "period-name" attributes in your example you can do something like:


$contents = <<<EOF
<data foo="bar">
<time-layout foo="bar">
<start-valid-time period-name="Tonight">2020-01-22T18:00:00-05:00</start-valid-time>
<start-valid-time period-name="Tomorrow">2020-01-23T06:00:00-05:00</start-valid-time>
</time-layout>
</data>
EOF;

$XML = simplexml_load_string($contents);
$result = array();
foreach ($XML->{'time-layout'}->{'start-valid-time'} as $startValidTime) {
$result[] = array (
// Cast to "string" to extract values
'period-name' => (string) $startValidTime['period-name'],
'value' => (string) $startValidTime,
);
}
print_r($result);


Will output:


Array
(
[0] => Array
(
[period-name] => Tonight
[value] => 2020-01-22T18:00:00-05:00
)

[1] => Array
(
[period-name] => Tomorrow
[value] => 2020-01-23T06:00:00-05:00
)
)

csdude55

1:44 am on Jan 24, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I appreciate the help, @w3dk! But unfortunately, when I simply do this:

$xml = simplexml_load_string($contents);
print_r($xml);


there's nothing called period-name in the results. For whatever reason, it's disappearing after simplexml_load_string(), regardless of JSON.

Could it be my version of PHP?

Here's the entire array for [time-layout] so you can see:

           [time-layout] => Array
(
[0] => SimpleXMLElement Object
(
[@attributes] => Array
(
[time-coordinate] => local
[summarization] => 12hourly
)

[layout-key] => k-p12h-n14-1
[start-valid-time] => Array
(
[0] => 2020-01-23T20:00:00-05:00
[1] => 2020-01-24T06:00:00-05:00
[2] => 2020-01-24T18:00:00-05:00
[3] => 2020-01-25T06:00:00-05:00
[4] => 2020-01-25T18:00:00-05:00
[5] => 2020-01-26T06:00:00-05:00
[6] => 2020-01-26T18:00:00-05:00
[7] => 2020-01-27T06:00:00-05:00
[8] => 2020-01-27T18:00:00-05:00
[9] => 2020-01-28T06:00:00-05:00
[10] => 2020-01-28T18:00:00-05:00
[11] => 2020-01-29T06:00:00-05:00
[12] => 2020-01-29T18:00:00-05:00
[13] => 2020-01-30T06:00:00-05:00
)
)

w3dk

1:50 pm on Jan 24, 2020 (gmt 0)

10+ Year Member Top Contributors Of The Month



when I simply do this:

$xml = simplexml_load_string($contents);
print_r($xml);


there's nothing called period-name in the results.


Yes, that is expected. Because you can't simply print_r() a SimpleXMLElement object and expect to see the entire contents of that object. (That might work for "simple" XML files, but not here.) When you call print_r() on an object, PHP must convert that object into a human-readable string representation. This is not always possible with "complex" objects. SimpleXMLElement is one such object.

For example, once you've created a SimpleXMLElement object with simplexml_load_string(), try calling the asXML() method and look at the result - you should see the complete XML document (regenerated from the data as read in).

The code I posted above is a working example using the XML snippet you posted - it reads the "period-name" attribute and value from the nested "start-valid-time" elements and returns an array. Exactly how you return that information is up to you. (I think in your other thread you were wanting to index by the value - timestamp?)

csdude55

9:09 pm on Jan 25, 2020 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Well slap my butt and call me Sally... you're right :-) I had copied your exact code earlier and just came back with an empty array, but I must have made a typo or something because it worked like you said this time!

That's awesome, I really appreciate it!

So I've been playing around, and using your code as a jumping point I was able to really simplify a lot of what I had done. So now I've gotten to:

foreach ($XML->{'data'}->{0}->{'time-layout'}->{0}->{'start-valid-time'} as $key) {

// 2020-01-25T15:00:00-05:00 => This Afternoon
echo "$key => {$key['period-name']}\n";

$thisDate = strtotime($key);

// $forecast['period-name']['0123456789'] => 'This Afternoon'
$forecast['period-name'][$thisDate] = $key['period-name'];
}

print_r($forecast);


The echo statement prints exactly what I'm expecting:

2020-01-25T15:00:00-05:00 => This Afternoon

but print_r($forecast) doesn't; instead, I'm getting:

[period-name] => Array
(
[1579982400] => SimpleXMLElement Object
(
[0] => This Afternoon
)


I don't understand, why does $forecast['period-name'] print as a string, but the value is set as an object?

I found that if I set it like this, though, then it sets as a string:

$forecast['period-name'][$thisDate] = "{$key['period-name']}";


Why?

w3dk

5:44 pm on Jan 28, 2020 (gmt 0)

10+ Year Member Top Contributors Of The Month



Because internally $key['period-name'] is a SimpleXMLElement object. To access the value of that element/attribute, you need to cast it to a "string". You could call it a quirk of SimpleXML. Although internally, there's probably a __toString() magic method that does the "magic".

When you echo the value or surround the variable in double quotes (variable parsing) then PHP implicitly type converts the variable to a string.

However, it is preferable to use explicit type casting when possible, as in my code above. For example:


$thisDate = strtotime((string) $key);
$forecast['period-name'][$thisDate] = (string) $key['period-name'];


This is a more deliberate action, easier to read, less chance of bugs etc.