Access a node value according to the attribute [duplicate] - php

This question already has answers here:
SimpleXML: Selecting Elements Which Have A Certain Attribute Value
(2 answers)
Closed 7 years ago.
I'm having trouble accessing a value from an XML file. Especially I can't extract the FrenchText. All I always get is the value from the GermanText. Does anyone have an idea, how to get to the value of the node containing the french string directly.
Consulting the web, I found this, but it only results in an error
echo $STRUCTURE_ITEM->NAME[#lang="fr"]
Any help would be greatly appreciated.
Here's my code
<?php
$myXMLData = '<?xml version="1.0" encoding="utf-8"?>
<CATALOG>
<CLASSIFICATION>
<STRUCTURE_ITEM>
<KEY>3605</KEY>
<NAME lang="de">GermanText1</NAME>
<NAME lang="fr">FrenchText1</NAME>
<PARENT_ID>worlds</PARENT_ID>
<SORT>0</SORT>
</STRUCTURE_ITEM>
<STRUCTURE_ITEM>
<KEY>3606</KEY>
<NAME lang="de">GermanText2</NAME>
<NAME lang="fr">FrenchText2</NAME>
<PARENT_ID>3605</PARENT_ID>
<SORT>0</SORT>
</STRUCTURE_ITEM>
</CLASSIFICATION>
</CATALOG>';
$xml=simplexml_load_string($myXMLData);
foreach($xml->CLASSIFICATION->children() as $STRUCTURE_ITEM) {
echo $STRUCTURE_ITEM->KEY . ", ";
echo $STRUCTURE_ITEM->NAME . ", ";
echo $STRUCTURE_ITEM->NAME . ", "; // <---- The problem lies here
echo $STRUCTURE_ITEM->PARENT_ID . "<br>";
} ;
?>
EDIT
Thanks for the valuable input. I've tried
$category_name_fr = $xml->xpath('//STRUCTURE_ITEM/NAME[#lang="fr"]');
Now I get the french values, but I get back an array containing all available french text. (FrenchText1, FrenchText2). But I just want the value of the current node.

Let me suggest an approach different from xpath: an inner foreach loop will iterate over all <NAME> and check for the lang attribute:
$lang = "fr";
foreach ($xml->CLASSIFICATION->STRUCTURE_ITEM as $item) {
echo $item->KEY . ", ";
foreach ($item->NAME as $name)
if ($name['lang'] == $lang) echo $name . ", ";
echo $item->PARENT_ID . "<br />";
}
See it in action: https://eval.in/303058
EDIT: If your XML is consistent meaning that de is always first and fr is always second, simply do:
$lang = 1; // 1 = fr, 0 = de
foreach ($xml->CLASSIFICATION->STRUCTURE_ITEM as $item) {
echo $item->KEY . ", ";
echo $item->NAME[$lang] . ", ";
echo $item->PARENT_ID . "<br />";
}
See this in action: https://eval.in/303062

Your xpath formula does something like this:
1. Starts from document root and proceeds in direction called 'descendant-or-self' (shortcut is //)
2. From this context searches in 'children' direction which is default (notice, that direction is not given after /) and looks for element called STRUCTURE_ITEM
3. From updated context (STRUCTURE_ITEM elements), xpath once more seeks for children elements, called NAME
4. Finally all nodes that fits ale checked with additional test (placed in []). Test says - check if there are nodes in 'attributes' direction (shortcut #). Nodes must be named 'fr'.
Because multiple nodes fits, results are concatenated. If you wish to extract NAME elements separately, try to shrink your initial context (don't start from document root [//]).
#edit:
this seems helpful: XPath query return multiple nodes on different levels

Related

How to access an XML nth element's property value and attribute value

Good, I ask again for your help. I have an xml document (http://radiojoven.6te.net/AirPlayHistory.xml) with several songs and I'm trying to create a code so that it can provide me each song individually xml document, but I'm getting find the right method to do with the code provides all the songs at the same time.
Example: I want to just take the music OLLY MURS - KISS ME, as is the 4 music I made this code:
<?php
$xml = simplexml_load_file("http://radiojoven.6te.net/AirPlayHistory.xml");
foreach($xml->Song as $item)
echo $item->Artist['name'][4] . " - " . $item['title'][4] ."<br>";
?>
but appears all the songs and I do not intend it, but rranjar one code that provides me the requested music. Can I help?
Thank you!
Use the following code
Note that element position starts from 0, so if you want 4th element it is the 3rd index
$xml = simplexml_load_file("http://radiojoven.6te.net/AirPlayHistory.xml");
$elementNo = 4;
$elepentPos = $elementNo - 1;
echo $xml->Song[$elepentPos]->Artist['name'] . ' --- ' . $xml->Song[$elepentPos]['title'];
Now to describe this further
Attributes are accessed using []
Elements are accessed using object notation
http://www.w3schools.com/php/php_xml_simplexml_get.asp follow this link to get more information

Parsing XML Attributes by name with PHP5

When trying to parse an XML document in PHP, nothing is returned.
The XML document im trying to use:
http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml
The code I have tried:
$player = simplexml_load_file('http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml');
foreach ($player->PlayerName as $playerInfo) {
echo $playerInfo['firstName'];
}
I have also tried:
$player = simplexml_load_file('http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml');
echo "Name: " . $player->PlayerName[0]['firstName'];
What do I need to change for the attributes to show?
You might try to print_r the whole data youself and finally find what you need:
var_dump($player->Player->PlayerName->Attrib['value']->__toString())
//⇒ string(7) "Daniele"
To list all "values" (firstname, lastname,...) you need list all children and their attributes:
$xml = simplexml_load_file('http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml');
foreach ($xml as $player) {
foreach ($player->PlayerName->children() as $attrib) {
echo $attrib['name'] . ': ' . $attrib['value'] . PHP_EOL;
}
}
Output:
firstName: Daniele
lastName: Viola
commonName: Viola D.
commentaryName:
This does not work, since you are trying to access an attribute and not a node value.
You might also run into problems, because the xml is not "valid" for simple xml. See my blogpost about the issues with parsing xml with php here http://dracoblue.net/dev/gotchas-when-parsing-xml-html-with-php/
If you use my Craur ( https://github.com/DracoBlue/Craur ) library instead, it will look like this:
$xml_string = file_get_contents('http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml');
$craur = Craur::createFromXml($xml_string);
echo $craur->get('Player.PlayerName.Attrib#value'); // works since the first attrib entry is the name
If you want to be sure about the attribute (or select another one) use:
$xml_string = file_get_contents('http://cdn.content.easports.com/media2011/fifa11zoneplayer/25068538/632A0001_10_ZONE_PLAYER_iUa.xml');
$craur = Craur::createFromXml($xml_string);
foreach ($craur->get('Player.PlayerName.Attrib[]') as $attribute)
{
if ($attribute->get('#name') == 'firstName')
{
echo $attribute->get('#value');
}
}

Pulling SimpleXML data Across a Page

I'm sure there's a pretty obvious solution to this problem...but it's alluding me.
I've got an XML feed that I want to pull information from - from only items with a specific ID. Let lets say we have the following XML:
<XML>
<item>
<name>John</name>
<p:id>1</id>
<p:eye>Blue</eye>
<p:hair>Black</hair>
</item>
<item>
<name>Jake</name>
<p:id>2</id>
<p:eye>Hazel</eye>
<p:hair>White</hair>
</item>
<item>
<name>Amy</name>
<p:id>3</id>
<p:eye>Brown</eye>
<p:hair>Yellow</hair>
</item>
<item>
<name>Tammy</name>
<p:id>4</id>
<p:eye>Blue</eye>
<p:hair>Black</hair>
</item>
<item>
<name>Blake</name>
<p:id>5</id>
<p:eye>Green</eye>
<p:hair>Red</hair>
</item>
</xml>
And I want to pull ONLY people with the ID 3 and 1 into specific spots on a page (there will be no double IDs - unique IDs for each item). Using SimpleXML and a forloop I can easily display each ITEM on a page using PHP - with some "if ($item->{'id'} == #)" statements (where # is the ID I'm looking for(, I can also display the info for each ID I'm looking for.
The problem I'm running into is how to distribute the information across the page. I'm trying to pull the information into specific spots on a page my first attempt at distributing the specific fields across the page aren't working as follows:
<html>
<head><title>.</title></head>
<body>
<?php
(SimpleXML code / For Loop for each element here...)
?>
<H1>Staff Profiles</h1>
<h4>Maintenance</h4>
<p>Maintenance staff does a lot of work! Meet your super maintenance staff:</p>
<?php
if($ID == 1) {
echo "Name:".$name."<br/>";
echo "Eye Color:".$eye."<br/>";
echo "Hair Color:".$hair."<br/>";
?>
<h4>Receptionists</h4>
<p>Always a smiling face - meet them here:</p>
<?php
if($ID == 3) {
echo "Name:".$name."<br/>";
echo "Eye Color:".$eye."<br/>";
echo "Hair Color:".$hair."<br/>";
?>
<H4>The ENd</h4>
<?php (closing the four loop) ?>
</body>
</html>
But it's not working - it randomly starts repeating elements on my page (not even the XML elements). My method is probably pretty...rudimentary; so a point in the right direction is much appreciated. Any advice?
EDIT:
New (NEW) XPATH code:
$count = 0;
foreach ($sxe->xpath('//item') as $item) {
$item->registerXPathNamespace('p', 'http://www.example.com/this');
$id = $item->xpath('//p:id');
echo $id[$count] . "\n";
echo $item->name . "<br />";
$count++;
}
use xpath to accomplish this, and write a small function to retrieve a person by id.
function getPerson($id = 0, &$xml) {
return $xml->xpath("//item[id='$id']")[0]; // PHP >= 5.4 required
}
$xml = simplexml_load_string($x); // assume XML in $x
Now, you can (example 1):
echo getPerson(5, $xml)->name;
Output:
Blake
or (example 2):
$a = getPerson(2, $xml);
echo "$a->name has $a->eye eyes and $a->hair hair.";
Output:
Jake has Hazel eyes and White hair.
see it working: http://codepad.viper-7.com/SwLids
EDIT In your HTML, this would probably look like this:
...
<h1>Staff Profiles</h1>
<h4>Maintenance</h4>
<p>Maintenance staff does a lot of work! Meet your super maintenance staff:</p>
<?php
$p = getPerson(4, $xml);
echo "Name: $p->name <br />";
echo "Eye Color: $p->eye <br />";
echo "Hair Color: $p->hair <br />";
?>
no looping required, though.
First thing that popped into my mind is to use a numerical offset (which is zero-based in SimpleXML) as there is a string co-relation between the offset and the ID, the offset is always the ID minus one:
$items = $xml->item;
$id = 3;
$person = $items[$id - 1];
echo $person->id, "\n"; // prints "3"
But that would work only if - and only if - the first element would have ID 1 and then each next element the ID value one higher than it's previous sibling.
Which we could just assume by the sample XML given, however, I somewhat guess this is not the case. So the next thing that can be done is to still use the offset but this time create a map between IDs and offsets:
$items = $xml->item;
$offset = 0;
$idMap = [];
foreach ($items as $item) {
$idMap[$item->id] = $offset;
$offset++;
}
With that new $idMap map, you then can get each item based on the ID:
$id = 3;
$person = $items[$idMap[$id]];
Such a map is useful in case you know that you need that more than once, because creating the map is somewhat extra work you need to do.
So let's see if there ain't something built-in that solves the issue already. Maybe there is some code out there that shows how to find an element in simplexml with a specific attribute value?
SimpleXML: Selecting Elements Which Have A Certain Attribute Value (Reference Question)
Read and take value of XML attributes - Especially because of the answer on how to add the functionality to SimpleXMLElement transparently.
Which leads to the point you could do it as outlined in that answer that shows how it works transparently like this:
$person = $items->attribute("id", $id);
I hope this is helpful.

Getting multiple results from XML (and then discarding certain results based on keyword matching)

Novice PHP programmer here (if I can even call myself that). I am currently working on a way to loop through an XML document to grab every instance of a child object that does not contain a specific keyword and then display the results. I am able to do the aforementioned but only with the first child object that is found.
Examples Follow ...
Example XML There may be more than one instance of the following and I am trying to get the <title> child element from every instance that does not contain the word "Flood" or the phrase "There are no active watches, warnings or advisories".
<entry>
<id>http://alerts.weather.gov/cap/wwaatmget.php?x=OHC095&y=0</id>
<updated>2013-04-16T20:00:01+00:00</updated>
<author>
<name>w-nws.webmaster#noaa.gov</name>
</author>
<title>There are no active watches, warnings or advisories</title>
<link href='http://alerts.weather.gov/cap/wwaatmget.php?x=OHC095&y=0'/>
</entry>
Variables
//Lucas Co. Weather
$xml_string = file_get_contents("../../cache/weather_alerts-lucas.xml");
$weather_alerts_lucas = simplexml_load_string($xml_string);
$lucas_alert = ($weather_alerts_lucas->entry->title);
$no_alert =('There are no active watches, warnings or advisories');
Displaying The Results The first if statement determines whether there should be a scroll bar or not and the second if statement determines if a specific county should display information (there are multiple counties but I am only showing one here for simplicity).
if ("$lucas_alert"=="$no_alert" || fnmatch("*Flood*", $lucas_alert))
{
//Do Nothing.
} else {
echo "<div id='emergency_bar'>";
echo "<span class='scroll_font' ... ...'>";
if ("$lucas_alert"=="$no_alert" || fnmatch("*Flood*", $lucas_alert))
{
//Do Nothing.
} else {
echo "Lucas Co. - " . $lucas_alert . " // ";
}
echo "</span>";
echo "</div>";
}
If I do it the way it is posted above it will only grab the first result and even if it grabbed multiple instances of <title> it would not display anything if one of the instances had the word "Flood" in it.
Thanks in advance. I'm not looking for someone to write code for me, just some direction.
$xml = simplexml_load_string($x); // XML in $x
$no = "There are no active watches, warnings or advisories";
foreach ($xml->xpath("//title") as $title) {
if ($title <> $no && strpos($title,"Flood") === false) echo "$title<br />";
}
see it working: http://codepad.viper-7.com/JVOoke

Filtering specific data from XML using PHP (Last.fm) UPDATE

Update:
Thanks Rambo for the great answer. The only issue that I have now is that it only displays artist information so long as the artists next gig is in the UK. For example, if they're playing in France and THEN the UK - it won't display anything (Or it will display my else message). If their next gig IS in the UK, then it will echo artist information etc. Any idea how to get it to echo only UK information, regardless if they're in another country before hand?
Thank you.
Original Post:
I'm currently creating a website for my final major project. I retrieve data using the Last.fm API using PHP and XML. It's going well so far, but there are a few issues I'm having trouble with. I'm very new to PHP, so I want to use this opportunity to develop some skills.
I want to limit the data to my city or country.
How do I retrieve images from an XML document?
Using the last.fm API, more specifically, the artist.getEvents (http://bit.ly/zYzWo6) - I am able to create a basic search field so that the user can type in an artist name. This is a great step in the right direction, but the problem is, any results outside of my country is irrelevant for my project. Using artist.getEvents doesn't allow any specific parameters such as location - geo.getEvents (http://bit.ly/wpSQwd) does however.
The following is the code used for my basic search:
<?php
$first_bit_of_url = 'http://ws.audioscrobbler.com/2.0/?method=artist.getevents&artist=';
$last_bit_of_url = '&api_key=b25b959554ed76058ac220b7b2e0a026&d';
$artist = ($_GET["artist"]); // gets the information passed by the input form
$query_url = $first_bit_of_url . $artist . $last_bit_of_url ;
$upcoming_gig_data_xml = simplexml_load_file($query_url);
$search_result = $upcoming_gig_data_xml->events->event->artists->artist;
$venue_result = $upcoming_gig_data_xml->events->event->venue->name;
$city_result = $upcoming_gig_data_xml->events->event->venue->location->city;
for ($i = 0; $i < 5; $i++){
echo $search_result . "<br />";
echo $venue_result . ", ";
echo $city_result . "<br />";
} ?>
Secondly how would I go about retrieving an image from, for example, this sample of XML code used in the above context? I've briefly read some articles on Xpath, can I mix the Xpath method with the method I'm using above?
<image size="small">...</image>
<image size="medium">...</image>
<image size="large">...</image>
Hopefully someone can point me in the right direction here, and I appreciate any help given.
Thanks,
Chris.
You could use XPath to get only the event elements with UK venues.
$lfm = simplexml_load_file('http://ws.audioscrobbler.com/2.0/?method=artist.getevents&artist=metallica&api_key=b25b959554ed76058ac220b7b2e0a026&d');
$uk_events = $lfm->xpath('events/event[venue/location/country="United Kingdom"]');
foreach ($uk_events as $event) {
$venue_city = (string) $event->venue->location->city;
$large_pics = array_map('strval', $event->xpath('image[#size="large"]'));
// Do whatever other processing/displaying you like…
}
(See it running.)
for your second question: use simple xml http://www.php.net/manual/de/ref.simplexml.php
for example your xml might be:
<?xml version="1.0" encoding="UTF-8"?>
<images>
<image size="small">1</image>
<image size="medium">2</image>
<image size="large">3</image>
</images>
load the xml file with simple xml and access the nodes like this. This is just a simple example.
$r = simplexml_load_file('test.xml');
foreach($r->image as $img) {
print $img . ' and size is ' . $img['size'] . "<br/>";
}
$smallimg = $upcoming_gig_data_xml->events->event->venue->image['small'];
then you can echo the image out using the img html tag, giving it's src the value of $smallimg
I haven't tested this, but hopefully it'll get you in the right direction
update
for the first point, loop through the xml file and do a match for the country so if it is equal to the united kingdom then process everything, otherwise it will skip it
foreach($upcoming_gig_data_xml->events->event as $event)
{
if($event->location == "United Kingdom")
{
// process everything here
}
}

Categories