So, this is a follow-up question to my previous question that was solved, here's the link to it:
using data from child element to select data in other element using simplexml in php
thanks to #RomanPerekhrest for solving this.
I have this piece of php code using simplexml to read my xml file
<?php
$xml = simplexml_load_file('../uploads/reports/report.xml');
$hits = $xml->xpath("results/hits/#rule_id");
$ruleIds = array_map(function($v){ // getting search path for each needed rule
return "profile_info/rules/rule[#id='". (string)$v. "']";
}, $hits);
foreach ($xml->xpath(implode(" | ", $ruleIds)) as $rule) {
echo '<div id="name">'. $rule->display_name .'</div>'.
'<div id="comment">'. $rule->display_comment .'</div>';
}
?>
again, thanks to #RomanPerekhrest for coming up with this.
This piece of code works fine with my simplified xml-file I created to illustrate my problems in my previous questions, but when I apply it, it doesn't seem to render.
I've found the reason why, in my root element there are some xmlns attributes that cause my xml not to load. When I manually remove these attributes, everything works as expected. (I will not the list the entire xml document, since it is 8500+ lines long)
Here is the root element with the attributes:
<report xsi:schemaLocation="http://www.callassoftware.com/namespace/pi4 pi4_results_schema.xsd" xmlns="http://www.callassoftware.com/namespace/pi4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
I need a way to bypass in php. Seeing as these xml files are generated by other software and the lack of settings in this generation, I cannot simply make these xml files without these attributes.
Thanks
Your XML has default namespace declared at the root element, which descendant elements without prefix inherit implicitly :
xmlns="http://www.callassoftware.com/namespace/pi4"
To reference element in default namespace, you need to map a prefix to the default namespace URI, and then use that prefix in your XPath :
//register prefix 'd' to reference default namespace URI
$xml->registerXPathNamespace('d', 'http://www.callassoftware.com/namespace/pi4');
//use the prefix to reference elements in the default namespace
$hits = $xml->xpath("d:results/d:hits/#rule_id");
$ruleIds = array_map(function($v){ // getting search path for each needed rule
return "d:profile_info/d:rules/d:rule[#id='". (string)$v. "']";
}, $hits);
Related
I want to read some data of the latest video on a youtube channel.
So i load the feed with simplexml_load_file and after that I use XPath for navigate to nodes.
$xmlFeed = simplexml_load_file("https://www.youtube.com/feeds/videos.xml?channel_id=UCo0bvu1jzU4WpHS3FglLU8g");
echo $xmlFeed->xpath("//entry[1]/title")[0];
echo $xmlFeed->xpath("//entry[1]/link")[0];
echo $xmlFeed->xpath("//entry[1]/id")[0];
I tried multiple style of XPath and it never work, I also tried to use DOMDocument and DOMXPath classes and it didn't work.
I use similar code for a wordpress rss and all works fine.
What am I wrong?
As per SimpleXMLElement::xpath's doc page's first comment:
To run an xpath query on an XML document that has a namespace, the
namespace must be registered with
SimpleXMLElement::registerXPathNamespace() before running the query.
If the XML document namespace does not include a prefix, you must make
up an arbitrary one, and then use it in your query.
You should therefore do this:
$xmlFeed = simplexml_load_file('https://www.youtube.com/feeds/videos.xml?channel_id=UCo0bvu1jzU4WpHS3FglLU8g');
foreach ($xmlFeed->getDocNamespaces() as $prefix => $namespace) {
$xmlFeed->registerXPathNamespace($prefix ?: 'default', $namespace);
}
echo $xmlFeed->xpath('//default:entry[1]/default:title')[0];
echo $xmlFeed->xpath('//default:entry[1]/default:link')[0];
echo $xmlFeed->xpath('//default:entry[1]/default:id')[0];
Note: feel free to use something shorter than default if it's inconvenient.
I am trying to change a value in an xml file using php. I am loading the xml file using php into an object like this..
if(file_exists('../XML/example.xml')) {
$example = simplexml_load_file('../XML/example.xml');
}
else {
exit ("can't load the file");
}
Then once it is loaded I am changing values within tags, by assigning them the contents of another variable, like this...
$example->first_section->second_section->third_section->title = $var['data'];
Then once I've made the necessary changes the file is saved. So far this process is working well, but have now hit a stumbling block.
I want to change a value within a particular tag in my xml file, which has an id. In the XML file it looks like this.
<first_section>
<second_section>
<third_section id="2">
<title>Mrs</title>
</third_section>
</second_section>
</first_section>
How can I change this value using similar syntax to what I've been using?
doing..
$example->first_section->second_section->third_section id="2" ->title = $var['data']
doesn't work as the syntax is wrong.
I've been scanning through stack overflow, and all over the net for an example of doing it this way but come up empty.
Is it possible to target and change a value in an xml like this, or do I need to change the way I am amending this file?
Thanks.
Some dummy code as your provided XML is surely not the original one.
$xml = simplexml_load_file('../XML/example.xml');
$section = $xml->xpath("//third_section[#id='2']")[0];
// runs a query on the xml tree
// gives always back an array, so pick the first one directly
$section["id"] = "3";
// check if it has indeed changed
echo $xml->asXML();
As #Muhammed M. already said, check the SimpleXML documentation for more information. Check the corresponding demo on ideone.com.
Figured it our after much messing around. Thanks to your contributions I indeed needed to use Xpath. However the reason it wasn't working for me was because I wasn't specifying the entire path for the node I wanted to edit.
For example, after loading the xml file into an object ($xml):
foreach($xml->xpath("/first_section/second_section/third_section[#id='2']") as $entry ) {
$entry->title = "mr";
}
This will work, because the whole path to the node is included in the parenthesis.
But in our above examples eg:
foreach($xml->xpath("//third_section[#id='2']" as $entry ) {
$entry->title = "mr";
}
This wouldn't work, even though it was my understanding that the double // will make it drill down, and I assumed that xpath will search the whole xml structure and return where id=2. It appears after spending hours testing this isn't the case. You must include the entire path to the node. As soon as I did that it worked.
Also on a side note. $section = $xml->xpath("//third_section[#id='2']")[0];
IS incorrect syntax. You don't need to specify the index "[0]" at the end. Including it flags up Dreamweavers syntax checker. And ignoring Dreamweaver and uploading anyway breaks the code. All you need is..
$section = $xml->xpath(" entire path to node in here [#id='2']");
Thanks for helping and suggesting xpath. It works very well... once you know how to use it.
I try to validate this document in PHP using DOMdocument's schemaValidate:
<?xml version="1.0" encoding="UTF-8"?> <works xmlns="http://pbn.nauka.gov.pl/-/ns/bibliography" pbn-unit-id="1388"><article><title>Mukowiscydoza</title></article></works>
by using $domDocument->schemaValidate('pbn-report.xsd')
Link to XSD:
https://pbn.nauka.gov.pl/help/images/files/pbn-report.xsd.zip
... and I always get an error
Error 1871: Element 'article': This element is not expected. Expected
is one of ( {http://pbn.nauka.gov.pl/-/ns/bibliography}article,
{http://pbn.nauka.gov.pl/-/ns/bibliography}book,
{http://pbn.nauka.gov.pl/-/ns/bibliography}chapter ). on line 0
For me it is incomprehensible. Why do I get an error when I pointed out the default namespace?
Solved.
It turns out that when you create a DOMDocument, when you add an Element every time you need to give Namespace. When generating a document (saveXML) will not make any difference, but if you run schemaValidate, the validator checks DOMDocument object, and not the generated XML.
In other words this code:
$domDocument = new DOMDocument('1.0', "UTF-8");
$domWorks = $domDocument->createElementNS("http://pbn.nauka.gov.pl/-/ns/bibliography",'works');
$domWorksId = $domDocument->createAttribute('pbn-unit-id');
$domWorksId->value = PBNID;
$domWorks->appendChild($domWorksId);
$domDocument->appendChild($domWorks);
$domArticle = $domDocument->createElement('article');
$domArticle->appendChild($domDocument->createElement('title','Mukowiscydoza'));
$domWorks->appendChild($domArticle);
echo htmlentities($domDocument->saveXML());
generates the same XML as this code
$domDocument = new DOMDocument('1.0', "UTF-8");
$domWorks = $domDocument->createElementNS("http://pbn.nauka.gov.pl/-/ns/bibliography",'works');
$domWorksId = $domDocument->createAttribute('pbn-unit-id');
$domWorksId->value = PBNID;
$domWorks->appendChild($domWorksId);
$domDocument->appendChild($domWorks);
$domArticle = $domDocument->createElementNS("http://pbn.nauka.gov.pl/-/ns/bibliography",'article');
$domArticle->appendChild($domDocument->createElementNS("http://pbn.nauka.gov.pl/-/ns/bibliography",'title','Mukowiscydoza'));
$domWorks->appendChild($domArticle);
echo htmlentities($domDocument->saveXML());
But if you check schema
$domDocument->schemaValidate('pbn-report.xsd');
, the first code will return an error.
Strange ...
Strange ...
Well not really. As long as the document is in memory, the information about the namespace(s) with the elements is preserved.
In that case the two different methods / parameter here really make a difference even if you don't see a difference in the generated XML (afterwards):
// null namespace
$domArticle = $domDocument->createElement('article');
// vs. concrete namespace
$domArticle = $domDocument->createElementNS(
'http://pbn.nauka.gov.pl/-/ns/bibliography', 'article'
);
You then serialize the document (what you describe as "generates the same XML") as XML and you then load that XML back into memory. Then the elements with no namespace aren't within the null namespace any longer because they inherit their namespace from their parent element.
So you must differ between the document and it's elements in memory (DOM) and in the serialized form (string, file).
You can have similar effects when you do XSLT transformations. So if you experience something strange, it's worth to consider that the document in memory is not representing what you first think even it creates similar - or even exact same - looking XML ;)
Try to put the xmlns inside the article element , then try again.
xmlns="http://pbn.nauka.gov.pl/-/ns/bibliography"
I need to traverse a dbpedia's xml resource file to get the abstract and some other basic information like formation year and budget.
An example for this would be the US EPA.(the bottom of the page has links to different data formats of the same file)
I only need the first rdf:Description namespace of the xml file. A snippet of the code
$xml_result = file_get_contents($xml_url);
$xml_data = simplexml_load_string($xml_result);
$namespaces = $xml_data->getNamespaces(true);
//print_r($namespaces);
$current = $xml_data->children($namespaces['rdf']);
This only gets me the rdf elements inside the first rdf:Description. how do i get access to other elements like the dbpedia-owl namespace elements inside the Description element ?
You can use multiple namespaces, see https://stackoverflow.com/a/13350242/865201
Without testing it, I think you can use something like
$xml_data->children($namespaces['rdf'])->Description->children($namespaces['dbpedia-owl'])->anotherElement;
I'm trying to get the $xml->entry->yt:statistics->attributes()->viewCount attribute, and I've tried some stuff with SimpleXML, and I can't really get it working!
Attempt #1
<?php
$xml = simplexml_load_file("http://gdata.youtube.com/feeds/api/videos?author=Google");
echo $xml->entry[0]->yt:statistics['viewCount'];
?>
Attempt #2
<?php
$xml = simplexml_load_file("http://gdata.youtube.com/feeds/api/videos?author=Google");
echo $xml->entry[0]->yt:statistics->attributes()->viewCount;
?>
Both of which return blank, though SimpleXML is working, I tried to get the feed's title, which worked!
Any ideas?
I've looked at loads of other examples on SO and other sites, but somehow this isn't working? does PHP recognize the ':' to be a cut-off, or am I just doing something stupid?
Thank you, any responses greatly appreciated!
If you just want to get the viewcount of a youtube video then you have to specify the video ID. The youtube ID is found in each video url. For example "http://www.youtube.com/watch?v=ccI-MugndOU" so the id is ccI-MugndOU. In order to get the viewcount then try the code below
$sample_video_ID = "ccI-MugndOU";
$JSON = file_get_contents("http://gdata.youtube.com/feeds/api/videos?q={$sample_video_ID}&alt=json");
$JSON_Data = json_decode($JSON);
$views = $JSON_Data->{'feed'}->{'entry'}[0]->{'yt$statistics'}->{'viewCount'};
echo $views;
I would use the gdata component from the zend framework. Is also available as a separate module, so you don't need to use the whole zend.
The yt: prefix marks that element as being in a different "XML namespace" from the rest of the document. You have to tell SimpleXML to switch to that namespace using the ->children() method.
The line you were attempting should actually look like this:
echo (string)$xml->entry[0]->children('yt', true)->statistics->attributes(NULL)->viewCount;
To break this down:
(string) - this is just a good habit: you want the string contents of the attribute, not a SimpleXML object representing it
$xml->entry[0] - as expected
->children('yt', true) - switch to the namespace with the local alias 'yt'
->statistics - as expected
->attributes(NULL) - technically, the attribute "viewCount" is back in the default namespace, because it is not prefixed with "yt:", so we have to switch back in order to see it
->viewCount - running ->attributes() gives us nothing but attributes, which are accessed with ->foo not ['foo']