I have looked through several posts on how to parse the namespace of a Google Analytics api feed (XML file) in PHP. They all say I need to have this bit of code:
$properties = $item->children('http://schemas.google.com/analytics/2009');
(or something similar, all involving the URL "http://schemas.google.com/analytics/2009")
The problem is, however, that I then get an error:
Fatal error: Call to a member function children() on a non-object
It seems as though the schemas.google.com doesn't exist, but I can't find where I need to be pointing to... any ideas?
Here's a snippet of the XML file I'm working with:
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dxp="http://schemas.google.com/analytics/2009" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/">
<id>https://www.googleapis.com/analytics/v2.4/data?ids=ga:42418300&dimensions=ga:pagePath&metrics=ga:pageviews,ga:uniquePageviews&sort=-ga:pageviews&start-date=2013-12-01&end-date=2014-01-01&max-results=10</id>
<updated>2014-01-03T22:21:31.057Z</updated>
<title type="text">Google Analytics Data for View (Profile) xxxxxxx</title>
<link rel="self" type="application/atom+xml" href="https://www.googleapis.com/analytics/v2.4/data?ids=ga:42418300&dimensions=ga:pagePath&metrics=ga:pageviews,ga:uniquePageviews&sort=-ga:pageviews&start-date=2013-12-01&end-date=2014-01-01&max-results=10"/>
<link rel="next" type="application/atom+xml" href="https://www.googleapis.com/analytics/v2.4/data?ids=ga:42418300&dimensions=ga:pagePath&metrics=ga:pageviews,ga:uniquePageviews&sort=-ga:pageviews&start-date=2013-12-01&end-date=2014-01-01&start-index=11&max-results=10"/>
<author>
<name>Google Analytics</name>
</author>
<generator>Google Analytics</generator>
<openSearch:totalResults>4826</openSearch:totalResults>
<openSearch:startIndex>1</openSearch:startIndex>
<openSearch:itemsPerPage>10</openSearch:itemsPerPage>
<dxp:aggregates>
<dxp:metric name="ga:pageviews" type="integer" value="166656"/>
<dxp:metric name="ga:uniquePageviews" type="integer" value="132895"/>
</dxp:aggregates>
<dxp:containsSampledData>false</dxp:containsSampledData>
<dxp:dataSource>
<dxp:property name="ga:profileId" value="xxxxxxxx"/>
<dxp:property name="ga:webPropertyId" value="UA-xxxxxxxx-1"/>
<dxp:property name="ga:accountName" value="Provost"/>
<dxp:tableId>ga:42418300</dxp:tableId>
<dxp:tableName>WebSite</dxp:tableName>
</dxp:dataSource>
<dxp:endDate>2014-01-01</dxp:endDate>
<dxp:startDate>2013-12-01</dxp:startDate>
<entry>
<id>https://www.googleapis.com/analytics/v2.4/data?ids=ga:42418300&ga:pagePath=/&start-date=2013-12-01&end-date=2014-01-01</id>
<updated>2014-01-03T22:21:31.057Z</updated>
<title type="text">ga:pagePath=/</title>
<link rel="alternate" type="text/html" href="http://www.google.com/analytics"/>
<dxp:dimension name="ga:pagePath" value="/"/>
<dxp:metric name="ga:pageviews" type="integer" value="38197"/>
<dxp:metric name="ga:uniquePageviews" type="integer" value="29385"/>
</entry>
<entry>
<id>https://www.googleapis.com/analytics/v2.4/data?ids=ga:42418300&ga:pagePath=/page2/&start-date=2013-12-01&end-date=2014-01-01</id>
<updated>2014-01-03T22:21:31.057Z</updated>
<title type="text">ga:pagePath=/page2/</title>
<link rel="alternate" type="text/html" href="http://www.google.com/analytics"/>
<dxp:dimension name="ga:pagePath" value="/page2/"/>
<dxp:metric name="ga:pageviews" type="integer" value="13964"/>
<dxp:metric name="ga:uniquePageviews" type="integer" value="10974"/>
</entry>
Here's my PHP so far:
<?php
//ini_set('auto_detect_line_endings',TRUE);
$xml = simplexml_load_file("/ga-feed.xml");
$namespaces = $xml->getNamespaces(true);
foreach ($xml->entry as $key => $value) {
$value->registerXPathNamespace('dxp', 'http://schemas.google.com/analytics/2009');
echo $value->xpath('dxp:metric[pageviews]') . "<br />\n";
}
?>
Eventually, I need to be able run some calculations with pageviews and unique pageviews (show top 4 sites, a 5th "Other" would be a combination of all the other sites) against the overall pageviews/unique pageviews. Am I at least going in a semi-correct direction?
Update: In my foreach, I removed "feed" so it just says $xml->entry as $key, and now it will display a list of text:
Array
Array
Array
Array
Array
Array
Array
Array
Array
Array
Not quite what I'm looking for... but progress? lol
Related
I'm using PHP to process XML information. How can I get from the XML youtube the video id?
Question I have:
> $vid['title'] = $video->title; $vid['date'] = $video->updated;
that works. Only I also want to be getting the video id
$vid['id'] = ?
I use this XML as example , off course I use the real feed.
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns:yt="http://www.youtube.com/xml/schemas/2015" xmlns:media="http://search.yahoo.com/mrss/" xmlns="http://www.w3.org/2005/Atom">
<link rel="self" href="http://www.youtube.com/feeds/videos.xml?channel_id=UCCXoCcu9Rp7NPbTzIvogpZg&orderby=published"/>
<id>yt:channel:UCCXoCcu9Rp7NPbTzIvogpZg</id>
<yt:channelId>UCCXoCcu9Rp7NPbTzIvogpZg</yt:channelId>
<title>Fox Business</title>
<link rel="alternate" href="https://www.youtube.com/channel/UCCXoCcu9Rp7NPbTzIvogpZg"/>
<author>
<name>Fox Business</name>
<uri>https://www.youtube.com/channel/UCCXoCcu9Rp7NPbTzIvogpZg</uri>
</author>
<published>2008-02-04T12:35:54+00:00</published>
<entry>
<id>yt:video:yt9cwC3bySI</id>
<yt:videoId>yt9cwC3bySI</yt:videoId>
(1) For <yt:videoId> You can try:
$vid['id'] = $video->{'yt:videoId'}
(2) For <id> You can try:
$vid['title'] = $video->id;
Or else finally try...
Read not as XML but as a string of text. Use the PHP String functions to extract the text that exists between the <yt:videoId> and </yt:videoId>.
I am implementing Youtube push notification and implemented webhook. Youtube gives updates in the form of atom feed. My problem is i can't parse that feed.
This is the XML:
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:yt="http://www.youtube.com/xml/schemas/2015">
<link rel="hub" href="https://pubsubhubbub.appspot.com" />
<link rel="self" href="https://www.youtube.com/xml/feeds/videos.xml?channel_id=UCaNoTnXcQQt3ody_cLZSihw" />
<title>YouTube video feed</title>
<updated>2018-03-01T07:21:59.144766801+00:00</updated>
<entry>
<id>yt:video:vNQyYJqFopE</id>
<yt:videoId>vNQyYJqFopE</yt:videoId>
<yt:channelId>UCaNoTnXcQQt3ody_cLZSihw</yt:channelId>
<title>Test Video 4</title>
<link rel="alternate" href="https://www.youtube.com/watch?v=vNQyYJqFopE" />
<author>
<name>Testing</name>
<uri>https://www.youtube.com/channel/UCaNoTnXcQQt3ody_cLZSihw</uri>
</author>
<published>2018-03-01T07:21:48+00:00</published>
<updated>2018-03-01T07:21:59.144766801+00:00</updated>
</entry>
<?php
$xml = '<?xml versio......';
$obj = simplexml_load_string($xml);
echo '<pre>';print_r($obj);echo '</pre>';
Screenshot
How to get the value of yt:videoId element. I am new to PHP, if I did anything wrong please correct me.
It seems the XML elements containing the yt namespace (e.g. <yt:videoId>) are not being parsed by simplexml_load_string. I don't know why but in your case the video id is also present in the <id> element you just need to extract the last value or simply cut of yt:video: in front of it. That is at least an easy workaround.
Also it works if you use a direct XPath to the <yt:videoId> element like this:
echo $obj->xpath('//yt:videoId')[0];
// output: vNQyYJqFopE
XPath always returns an array so you need to get the first element with [0].
Try this (updated)
$str = $obj->entry->id;
echo substr($str, strpos($str, "video:")+ 6);
Get the channel
$chan = $obj->entry->author->uri;
echo substr($chan , strpos($chan , "channel/")+ 8);
I have a valid XML file (generated from SharePoint) which looks like this (in browser):
Sample XML File
<?xml version="1.0" encoding="utf-8"?>
<feed xml:base="https://www.example.com/_api/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml">
<id>9913f043-xxxx-xxxx-xxxx-xxxx-xxxx</id>
<title />
<updated>2017-05-23T06:08:01Z</updated>
<entry m:etag=""23"">
<id>Web/Lists(guid'13306095-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx')/Items(1)</id>
<category term="SP.Data.XXXXXXXXXXXXXXXXXXXXX" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" href="Web/Lists(guid'13306095-xxxx-xxxx-xxxx-xxxx-xxxx')/Items(1)" />
<title />
<updated>2017-05-23T06:08:01Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:FileSystemObjectType m:type="Edm.Int32">0</d:FileSystemObjectType>
<d:Id m:type="Edm.Int32">1</d:Id>
<d:ContentTypeId>0x0100B6A3B67BE96F724682CCDC8FBE9D70C2</d:ContentTypeId>
<d:Title m:null="true" />
<d:Topic>How to google?</d:Topic>
<d:Cats m:type="Collection(Edm.Int32)">
<d:element>1</d:element>
<d:element>2</d:element>
<d:element>3</d:element>
<d:element>4</d:element>
<d:element>5</d:element>
<d:element>6</d:element>
<d:element>7</d:element>
</d:Cats>
</m:properties>
</content>
</entry>
<entry>
.
.
</entry>
<entry>
.
.
</entry>
</feed>
(Note: I cut off some repeated nodes here, because it is so long.)
Clearly, we have inner nodes <content type="application/xml"> which also contain data inside.
The Problem (When parsing with PHP)
In PHP, i used this codes to parse (trying to extract it):
$xml = simplexml_load_file("data.xml");
foreach ($xml->entry as $item) {
echo $item->updated . PHP_EOL; // <--- This works!
print_r($item->content); // <--- This doesn't work as expected.
}
.. and then, it is giving me these:
2017-05-23T06:08:01Z
SimpleXMLElement Object
(
[#attributes] => Array
(
[type] => application/xml
)
)
2017-05-23T06:08:01Z
SimpleXMLElement Object
(
[#attributes] => Array
(
[type] => application/xml
)
)
.
.
Question (Help!)
How do i extract (get) the actual data inside those <content type="application/xml"> nodes, please?
Please help. Thank you in advance.
The elements below "content" have a namespace (d:...). I had the same problem a while ago. This should help:
$xml = simplexml_load_file("data.xml");
foreach ($xml->entry as $item) {
echo $item->updated . PHP_EOL;
$ns = $item->content->children('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata');
print_r($ns->properties);
}
I updated the code. I'm shure print_r($ns->properties) doesn't show the complete sub-elements ... because they are from another namspace. I guess you can then do this:
$nsd = $ns->properties->children("http://schemas.microsoft.com/ado/2007/08/dataservices");
and proccced with the result.
In your example namespaces can be found in the document element:
xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
(use the URL between the quotation marks)
d: and m: are used in the document to reference these namespaces.
EDIT: There is another namespace involved. Didn't recognize that. The solution can be atapted. I changed the code a bit.
I had a very similar issue. I was finally able to get my example working with this.
function pre($array){
echo "<pre>";
print_r($array);
echo "</pre>";
}
$record[$count]['id'] = $id->id;
$xmlData = utf8_encode(file_get_contents("https://ucf.uscourts.gov/odata.svc/Creditors(guid'81044f71-fb3c-11e5-ac5b-0050569d488e')"));
$xml = new SimpleXMLElement($xmlData);
$properties = $xml->content->children('http://schemas.microsoft.com/ado/2007/08/dataservices/metadata');
$fields = $properties->properties->children("http://schemas.microsoft.com/ado/2007/08/dataservices");
pre($fields);
$key = (string)$fields->Key;
$lastName = (string)$fields->LastName;
echo $key. "<br />";
echo $lastName. "<br />";
You would need to replace the Url in file_get_contents, the Key variable and LastName variable with you namespace values that you are looking for and I like to use a pre function to have things show easier. You can remove this part. Hopes this helps someone.
I am trying to output a xml file to an array thats then outputted to screen. The xml file loads I know it loads because as I can output entry > Id but I can not access its child nodes. I need the data located in.
content > s:organisationSummay
content > s:organisationSummay > s:address
content > s:organisationSummay > s:geographicCoordinates
how would I access the the data located in s:organisationSummay ,s:address, s:geographicCoordinates so I can getElementsByTagName for each items in that child node.
$doc2 = new DOMDocument();
$url = 'http://v1.syndication.nhschoices.nhs.uk/organisations/'.$_POST['ServiceType'].'/postcode/'.$_POST['PostCode'].'.xml?apikey=??&range=50';
echo $url;
$doc2->load($url);
$arrFeeds = array();
foreach ($doc2->getElementsByTagName('entry') as $node)
{
echo $node->getElementsByTagName($content->'s:name');
$itemRSS = array (
'PracticeName' => $organisationSummary->getElementsByTagName('s:name')->item(0)->nodeValue
);
array_push($arrFeeds, $itemRSS);
}
<?xml version="1.0" encoding="utf-8"?>
<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 - ls1- Within 50km</title>
<id>http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/postcode/bd164jt?range=50</id>
<rights type="text">© Crown Copyright 2009</rights>
<updated>2012-07-06T10:24:46+01:00</updated>
<category term="Search"/>
<logo>http://www.nhs.uk/nhscwebservices/documents/logo1.jpg</logo>
<author>
<name>NHS Choices</name>
<uri>http://www.nhs.uk</uri>
<email>webservices#nhschoices.nhs.uk</email>
</author>
<link rel="self" type="application/xml" title="NHS Choices - GP Practices Near Postcode - ;ls1 - Within 50km" href="http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/postcode/ls1?apikey=??&range=50"/>
<link rel="first" type="application/xml" title="first" length="1000" href="http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/postcode/ls1?apikey=??&range=50&page=1"/>
<link rel="next" type="application/xml" title="next" length="1000" href="http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/postcode/Ls1?apikey=??&range=50&page=2"/>
<link rel="last" type="application/xml" title="last" length="1000" href="http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/postcode/LS1?apikey=??&range=50&page=10"/>
<link rel="alternate" title="NHS Choices - Find and choose services - GP Practices" href="http://www.nhs.uk/ServiceDirectories/pages/ServiceSearch.aspx?ServiceType=GP"/>
<s:SearchCoords>439300,411100</s:SearchCoords>
<entry>
<id>http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/1</id>
<title type="text">Medical Practice</title>
<updated>2012-07-06T09:24:46Z</updated>
<link rel="self" title="Medical Practice" href="http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/1?apikey=??"/>
<link rel="alternate" title="Medical Practice" href="http://www.nhs.uk/ServiceDirectories/Pages/GP.aspx?pid=1"/>
<content type="application/xml">
<s:organisationSummary>
<s:name>Medical Practice</s:name>
<s:address>
<s:addressLine>Health Care Centre</s:addressLine>
<s:addressLine>2</s:addressLine>
<s:addressLine>Town</s:addressLine>
<s:addressLine>Yorkshire</s:addressLine>
<s:postcode>?</s:postcode>
</s:address>
<s:contact type="General">
<s:telephone>5558383</s:telephone>
</s:contact>
<s:geographicCoordinates>
<s:northing>438880</s:northing>
<s:easting>411444</s:easting>
<s:longitude>-1.82821202227791</s:longitude>
<s:latitude>53.996218047559</s:latitude>
</s:geographicCoordinates>
<s:Distance>0.5</s:Distance>
</s:organisationSummary>
</content>
</entry>
<entry>
<id>http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/2</id>
<title type="text">Surgery</title>
<updated>2012-07-06T09:24:46Z</updated>
<link rel="self" title="Surgery" href="http://v1.syndication.nhschoices.nhs.uk/organisations/gppractices/1?apikey=??"/>
<link rel="alternate" title="Surgery" href="http://www.nhs.uk/ServiceDirectories/Pages/GP.aspx?pid=2"/>
<content type="application/xml">
<s:organisationSummary>
<s:name>Surgery</s:name>
<s:address>
<s:addressLine>Healthcare Centre</s:addressLine>
<s:addressLine>Kings</s:addressLine>
<s:addressLine>Town</s:addressLine>
<s:postcode>?</s:postcode>
</s:address>
<s:contact type="General">
<s:telephone>555555</s:telephone>
<s:email>Email</s:email>
</s:contact>
<s:geographicCoordinates>
<s:northing>78421</s:northing>
<s:easting>484100</s:easting>
<s:longitude>-1.828987402220691</s:longitude>
<s:latitude>53.987218047559</s:latitude>
</s:geographicCoordinates>
<s:Distance>0.5</s:Distance>
</s:organisationSummary>
</content>
</entry>
</feed>
This is a namespaced document, so you need to use the proper namespace methods, e.g. DOMDocument::getElementsByTagNameNS.
In addition, there is so much wrong with your loop that I suspect you're either not including all the code or you really misunderstand how DOMDocument works.
$NS = array(
's' => "http://syndication.nhschoices.nhs.uk/services",
'atom' => "http://www.w3.org/2005/Atom",
);
$entries = array();
foreach ($doc2->getElementsByTagNameNS($NS['s'], 'organisationSummary') as $node)
{
$entries[] = array(
'name' => trim($node->getElementsByTagNameNS($NS['s'], 'name')->item(0)->textContent),
'address' => keyByElementName($node->getElementsByTagNameNS($NS['s'], 'address')->item(0)),
'geographicCoordinates' => keyByElementName($node->getElementsByTagNameNS($NS['s'], 'geographicCoordinates')->item(0)),
);
}
function keyByElementName(DOMNode $node)
{
$elem = array();
foreach ($node->childNodes as $child) {
if ($child->nodeType===XML_ELEMENT_NODE) {
$elem[$child->localName] = trim($child->textContent);
}
}
return $elem;
}
However, consider using DOMXPath or SimpleXML, as these will be easier than dom traversal.
I have the following XML code that I'm trying to parse, but I'm sure of how to traverse some of the data in PHP:
<entry>
<id>http://data.treasury.gov:8001/Feed.svc/DailyTreasuryYieldCurveRateData(5360)</id>
<title type="text"></title>
<updated>2011-06-09T20:15:18Z</updated>
<author>
<name />
</author>
<link rel="edit" title="DailyTreasuryYieldCurveRateDatum" href="DailyTreasuryYieldCurveRateData(5360)" />
<category term="TreasuryDataWarehouseModel.DailyTreasuryYieldCurveRateDatum" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<content type="application/xml">
<m:properties>
<d:Id m:type="Edm.Int32">5360</d:Id>
<d:NEW_DATE m:type="Edm.DateTime">2011-06-01T00:00:00</d:NEW_DATE>
<d:BC_1MONTH m:type="Edm.Double">0.04</d:BC_1MONTH>
<d:BC_3MONTH m:type="Edm.Double">0.05</d:BC_3MONTH>
<d:BC_6MONTH m:type="Edm.Double">0.11</d:BC_6MONTH>
<d:BC_1YEAR m:type="Edm.Double">0.18</d:BC_1YEAR>
<d:BC_2YEAR m:type="Edm.Double">0.44</d:BC_2YEAR>
<d:BC_3YEAR m:type="Edm.Double">0.74</d:BC_3YEAR>
<d:BC_5YEAR m:type="Edm.Double">1.6</d:BC_5YEAR>
<d:BC_7YEAR m:type="Edm.Double">2.28</d:BC_7YEAR>
<d:BC_10YEAR m:type="Edm.Double">2.96</d:BC_10YEAR>
<d:BC_20YEAR m:type="Edm.Double">3.83</d:BC_20YEAR>
<d:BC_30YEAR m:type="Edm.Double">4.15</d:BC_30YEAR>
<d:BC_30YEARDISPLAY m:type="Edm.Double">4.15</d:BC_30YEARDISPLAY>
</m:properties>
</content>
</entry>
I can only get so far as
entry->content
As the following throws an error for having a colon:
entry->content->m:properties
How do I access what's inside content such as d:NEW_DATE?
In SimpleXML you can use the children('prefix', true) and attributes('prefix', true) functions to access namespaced content.
entry->content->children('m', true)->properties
or to access d:NEW_DATE
entry->content->children('m', true)->properties->children('d', true)->NEW_DATE
or one step further to access the m:type attribute
entry->content->children('m', true)->properties->children('d', true)->NEW_DATE->attributes('m', true)->type
You can use the SimpleXml's functions
SimpleXML
But my fav class is DOMDocument