Alex
Alex

Reputation: 4934

Using XPath to select through namespaces

I've been trying to use XPath in PHP to access an Atom feed from the National Health Service API.

The data looks like this:

<feed xmlns:s="http://syndication.nhschoices.nhs.uk/services" xmlns="http://www.w3.org/2005/Atom">
 <title type="text">NHS Choices - GP Practices Near Postcode - W1T4LB - Within 5km</title>
 <entry>
  <id>http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/27369</id>
  <title type="text">Fitzrovia Medical Centre</title>
  <updated>2011-08-20T22:47:39Z</updated>
  <link rel="self" title="Fitzrovia Medical Centre" href="http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/27369?apikey="/>
  <link rel="alternate" title="Fitzrovia Medical Centre" href="http://www.nhs.uk/ServiceDirectories/Pages/GP.aspx?pid=303A92EF-EC8D-496B-B9CD-E6D836D13BA2"/>
  <content type="application/xml">
   <s:organisationSummary>
    <s:name>Fitzrovia Medical Centre</s:name>
    <s:address>
     <s:addressLine>31 Fitzroy Square</s:addressLine>
     <s:addressLine>London</s:addressLine>
     <s:postcode>W1T6EU</s:postcode>
    </s:address>
    <s:contact type="General">
     <s:telephone>020 7387 5798</s:telephone>
    </s:contact>
    <s:geographicCoordinates>
     <s:northing>182000</s:northing>
     <s:easting>529000</s:easting>
     <s:longitude>-0.140267259415255</s:longitude>
     <s:latitude>51.5224357586293</s:latitude>
    </s:geographicCoordinates>
    <s:Distance>0.360555127546399</s:Distance>
   </s:organisationSummary>
  </content>
 </entry>
</feed>

I'm now using this code to access the node.

<?php   

$feedURL = 'http://v1.syndication.nhschoices.nhs.uk/organisations/pharmacies/postcode/W1T4LB.xml?apikey=&range=5';
$xml = file_get_contents($feedURL);
$sxml = new SimpleXMLElement($xml);
$sxml->registerXPathNamespace('a', 'http://www.w3.org/2005/Atom');
$sxml->registerXPathNamespace('s', 'http://syndication.nhschoices.nhs.uk/services');

$addr = $sxml->xpath('//s:address');

var_dump($addr[0]);

?>

Here $addr is empty but it should have ten entries.

Please can someone explain a good way to print out each <s:addressLine> node contents, and to place the postcode into a var.

I am working with the namespace principle in practice, although this is fairly new to me. Appreciate any information you could convey about learning XPath, and the PHP SimpleXML model.

Appreciate the help.

EDIT -

In seeing the update given below I decided to put my final output code into this:

function doPharmacy($postcode, $request, $prev, $next)
{


    $feedURL = 'http://v1.syndication.nhschoices.nhs.uk/organisations/pharmacies/postcode/' . $postcode . '.xml?apikey=&range=5';
    $xml = file_get_contents($feedURL);
    $sxml = new SimpleXMLElement($xml);
    $sxml->registerXPathNamespace('a', 'http://www.w3.org/2005/Atom');
    $sxml->registerXPathNamespace('s', 'http://syndication.nhschoices.nhs.uk/services');

    ////////////// XPATH \\\\\\\\\\\\\\

    $addrLines = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:content/s:organisationSummary/s:address/s:addressLine');
    $nhsPostcode = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:content/s:organisationSummary/s:address/s:postcode');
    $tel = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:content/s:organisationSummary/s:contact/s:telephone');
    $distance = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:content/s:organisationSummary/s:Distance');
    $title = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:title');
    $link = $sxml->xpath('/a:feed/a:entry[' . $request . ']/a:link[@rel="alternate"]/@href');

    $nhsPostcode = array_pop($nhsPostcode); // always taking first node from set
    $tel = array_pop($tel);
    $distance = (double)array_pop($distance);
    $title = array_pop($title);
    $link = array_pop($link);


    ////////////// OUTPUT: JSON \\\\\\\\\\\\\\

    print '{"addr": "';

    foreach ($addrLines as $addr)
    {
        print $addr . '<br />'; // ok to use HTML tags in JSON
    }

    print '",';

    print '"postcode": "' . $nhsPostcode . '",';

    print '"tel": "' . $tel . '",';

    $num = number_format($distance, 2);

    print '"distance": "' . $num . '",';

    print '"title": "' . $title . '",';

    print '"link": "' . $link . '",';

    $nhsPostcode = urlencode($nhsPostcode);

    print'"mapsURL": "http://maps.googleapis.com/maps/api/staticmap?center=' . $nhsPostcode . '&zoom=15&size=155x137&sensor=false&scale=2",';

    print '"prev": "' . $prev . '",';

    print '"next": "' . $next . '"';

    print '}';
}

?>

Upvotes: 9

Views: 7942

Answers (2)

Igor Parra
Igor Parra

Reputation: 10348

Previous answers have helped me a lot! So here go the complete solution.

    $feedURL = "http://v1.syndication.nhschoices.nhs.uk/organisations/pharmacies/postcode/{$user->postcode}.xml?apikey=XXXX&range=100";
    $xml = simplexml_load_file($feedURL);
    $xml->registerXPathNamespace('s', 'http://syndication.nhschoices.nhs.uk/services');

    $org = $xml->xpath('//s:organisationSummary');
    $i = 0;
    $item = array();
    foreach ($org as $o)
    {
        $aux = $o->xpath('s:name');
        $item[$i]['name'] = (string) $aux[0];

        $addr = $o->xpath('s:address');
        foreach ($addr as $a)
        {
            $aux = '';

            $addressLine = $a->xpath('s:addressLine');
            foreach ($addressLine as $a2)
            {
                $aux .= (string) $a2[0] . "\n";
            }

            $aux2 = $a->xpath('s:postcode');
            if (is_array($aux2))
            {
                $aux .= (string) $aux2[0];
            }

            $item[$i]['address'] = $aux;
        }

        $i ++;
    }

This will provide:

array (
  0 => 
  array (
    'name' => 'Your Local Boots Pharmacy',
    'address' => 'Century House
Station Road
Manningtree
CO11 1AA',
  ),
  1 => 
  array (
    'name' => 'The Pharmacy',
    'address' => 'The Street
East Bergholt
CO7 6SE',
  ), and so on...

Upvotes: 3

brian_d
brian_d

Reputation: 11395

This works for me:

$addr = $sxml->xpath('//s:address');
foreach ($addr as $a) {
    $addressLine = $a->xpath('s:addressLine');
    foreach ($addressLine as $al) {
        echo (string)$al."<br/>";
    }
    $postalCode = $a->xpath('s:postcode');
    foreach ($postalCode as $p) {
        echo (string)$p."<br/>";
    }
}

Which displays:

31 Fitzroy Square
London
W1T6EU

Upvotes: 11

Related Questions