9

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 '}';
}

?>
3
  • 1
    Unless you are uncertain about whether the XML will retain its namespaces, there's no particular need to register them. $sxml->xpath('//s:address'); will still work. Can't really hurt though. Commented Aug 22, 2011 at 16:14
  • Deleted first comment about internal server error. This was probably due to missing apikey. ;-) When I copy your example XML it works fine though. Commented Aug 22, 2011 at 16:18
  • Official php documentation gives an example on how to deal with namespaces php.net/manual/en/simplexmlelement.registerxpathnamespace.php Commented Jan 25, 2021 at 8:53

2 Answers 2

11

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
Sign up to request clarification or add additional context in comments.

2 Comments

This actually printed each address n (10) times. I just used array_pop to get the s:address SimpleXML object. Thanks though, really helped :)
@ alexw can you please explain how do you used array_pop?
3

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...

1 Comment

Hi NomikOS... I also did an update to this, please have a look, appreciate comments... nice job on that btw. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.