XML Selecting data by attribute in PHP [duplicate] - php

This question already has answers here:
SimpleXML: Selecting Elements Which Have A Certain Attribute Value
(2 answers)
Closed 2 years ago.
I'm not sure if this is even the best way to do this, but I have the following XML document and using PHP I need to select the data that comes in stage one
<?xml version="1.0" encoding="utf-8"?>
<stages>
<stage name="one">
<watch>
<story>
<title>title</title>
<copy>text goes here</copy>
</story>
</watch>
</stage>
<stage name="two">
<watch>
<story>
<title>title 2</title>
<copy>text goes here</copy>
</story>
</watch>
</stage>
</stages>
This PHP returns NULL
$xml = simplexml_load_file("content/content.xml");
var_dump($xml->stage->name['one']);

Stage does not have a name property, it is an attribute.
One option is to use xpath and get the stage(s) where the attribute value for key name is one:
/stages/stage[#name='one']
For example
$xml = simplexml_load_file("content/content.xml");
$result = $xml->xpath("/stages/stage[#name='one']");
var_dump($result);
Output
array(1) {
[0]=>
object(SimpleXMLElement)#2 (2) {
["#attributes"]=>
array(1) {
["name"]=>
string(3) "one"
}
["watch"]=>
object(SimpleXMLElement)#3 (1) {
["story"]=>
object(SimpleXMLElement)#4 (2) {
["title"]=>
string(5) "title"
["copy"]=>
string(14) "text goes here"
}
}
}
}

Perhaps the XML should be like this instead
<stages>
<one>
<watch>
<story>
<title>title</title>
<copy>text goes here</copy>
</story>
</watch>
</one>
<two>
<watch>
<story>
<title>title 2</title>
<copy>text goes here</copy>
</story>
</watch>
</two>
</stages>

Related

SimpleXMLElement is removing attributes (php 7.2)

Does anybody know why SimpleXMLElement is removing the attributes in my XML??
I have XML data that looks like this (note the translation "language" attribute):
<events>
<event id="d8f17143-0c67-48aa-a7f1-003a5ddbd28f">
<details>
<names>
<translation language="en">English title</translation>
<translation language="de">German title</translation>
</names>
</details>
</event>
</events>
I run it through SimpleXmlElement like so:
$xmlConvertedData = new \SimpleXMLElement($xml);
I dump out the data and it looks like so:
object(SimpleXMLElement)#958 (2) {
["#attributes"]=>
array(1) {
["Index"]=>
string(1) "1"
}
["Events"]=>
object(SimpleXMLElement)#956 (1) {
["Event"]=>
array(1) {
[0]=>
object(SimpleXMLElement)#959 (1) {
["Details"]=>
object(SimpleXMLElement)#826 (13) {
["Names"]=>
object(SimpleXMLElement)#834 (1) {
["Translation"]=>
array(2) {
[0]=>
string(32) "English title"
[1]=>
string(33) "German title"
}
}
}
}
}
}
}
...notice "translation" no longer has a "language" attribute, just an ID number 0 and 1. I need to know the attribute value because the XML does not always show the same language first.
(I edited the shortened the sample code to one record, so please ignore the #958 part)
Do not use any of the print_r() or var_dump() on a SimpleXML object, this will abbreviate the output as there is potentially a lot of it. If you want to check the document loaded use asXML()...
echo $xmlConvertedData->asXML();
or to output the one elements language...
echo $xmlConvertedData->event[0]->details->names->translation['language'];
( You also need to correct the last element of the sample - </events>)

var_dump for DOMNodeList printout unexpected result

I have a xml file I load it as the following:
//$file the file system path of the xml file
function getTopicsList($file){
$doc = new DOMDocument();
$doc->load( $file );
var_dump($doc->getElementsByTagName('topic'));
return $doc->getElementsByTagName('topic');
}
The loaded xml file contents is something like the following:
<?xml version="1.0" encoding="UTF-8"?>
<topics>
<topic>
<title>Title1</title>
<keywords>"Some Keys"</keywords>
</topic>
<topic>
<title>The Title</title>
<keywords>Another Key</keywords>
</topic>
<topic>
<title>A Title</title>
<keywords>Key two</keywords>
</topic>
</topics>
The var_dump() in the above code just printout limited information such as:
object(DOMNodeList)#30 (1) {
["length"]=>
int(3)
}
I expected that it should print at least the properties of that object i.e the xml tags and its values. I tried to use other functions such as print_r() and var_export() but there is no details I want.
No, this is node list. You can iterate it with foreach or access nodes using the item() method.
Node lists are used at different places, getElementsByTagName() is one, another is the $childNodes property. Xpath expressions return node lists, too.
Be aware that the nodes can be not only elements but several node types. Like text, cdata section or attribute.
You can use var_dump() to dump a single node. This works with PHP >= 5.3.11 or >= 5.4.1.
$dom = new DOMDocument();
$dom->loadXML('<foo/>');
var_dump($dom->documentElement);
Output:
object(DOMElement)#2 (18) {
["schemaTypeInfo"]=>
NULL
["tagName"]=>
string(3) "foo"
["textContent"]=>
string(0) ""
["baseURI"]=>
string(1) "/"
["localName"]=>
string(3) "foo"
["prefix"]=>
string(0) ""
["ownerDocument"]=>
...

PHP xpath result as array of strings

I have seen several similar questions, but not found the exact answer I'm looking for. It may be that the functionality I am looking for does not exist.
If I do an xpath query that results in an array of objects, but each object only holds one value, a string, I'd like to quickly convert that into an array of strings. Obviously I can do a foreach on the object and push the string value onto a new array, but if there is a built in function I'm not thinking of, please let me know.
example:
array(3) {
[0]=>
object(SimpleXMLElement)#24 (1) {
[0]=>
string(20) "Network Media Player"
}
[1]=>
object(SimpleXMLElement)#25 (1) {
[0]=>
string(12) "Music Player"
}
[2]=>
object(SimpleXMLElement)#26 (1) {
[0]=>
string(8) "Juke Box"
}
}
I'd like that to become
array('Network Media Player','Music Player','Juke Box')
Here's my test :
<pre><?php
$xml = "<data>
<item>
<value>Network Media Player</value>
</item>
<item>
<value>Music Player</value>
</item>
<item>
<value>Jukebox Player</value>
</item>
</data>";
$sx = simplexml_load_string($xml);
print_r($sx);
print_r(explode("|",implode("|",$sx->xpath("//data/item/value"))));
?></pre>
and here's the result : http://codepad.org/ZkaWpzMc

Using PHP to manage GMail Mail Filter XML Files

Since GMail made it possible to import and export the mail filters, I'd like to manage the XML files that are exported from GMail using a PHP script, as there are some known issues with the number of characters in the search filters.
I've found the simplexml_load_file function in PHP, and have performed var_dump() against the performed export, but I now don't seem to be able to access the apps namespace within the generated XML file.
Following various pages within the PHP manual, I created this very simple script to read the XML out so I can start to process out the filters I've already created. Unfortunately, it seems to be missing the key parts!
<pre><?php
if(file_exists("mailfilters.xml")) {
$xml = simplexml_load_file("mailfilters.xml");
$namespaces = $xml->getNamespaces(true);
foreach ($namespaces as $prefix => $ns) {
$xml->registerXPathNamespace($prefix, $ns);
}
var_dump($xml);
} else {
die("Failed to open filter file");
}
?></pre>
This returns this data (extract)
["entry"]=>
array(268) {
[0]=>
object(SimpleXMLElement)#3 (5) {
["category"]=>
object(SimpleXMLElement)#271 (1) {
["#attributes"]=>
array(1) {
["term"]=>
string(6) "filter"
}
}
["title"]=>
string(11) "Mail Filter"
["id"]=>
string(45) "tag:mail.google.com,2008:filter:1284991916868"
["updated"]=>
string(20) "2010-10-28T11:59:31Z"
["content"]=>
object(SimpleXMLElement)#272 (0) {
}
}
[1]=>
object(SimpleXMLElement)#4 (5) {
["category"]=>
object(SimpleXMLElement)#272 (1) {
["#attributes"]=>
array(1) {
["term"]=>
string(6) "filter"
}
}
["title"]=>
string(11) "Mail Filter"
["id"]=>
string(45) "tag:mail.google.com,2008:filter:1284991925003"
["updated"]=>
string(20) "2010-10-28T11:59:31Z"
["content"]=>
object(SimpleXMLElement)#271 (0) {
}
}
Here's an extract from the XML file I have downloaded today, used to create the above output:
<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>
<title>Mail Filters</title>
<id>tag:mail.google.com,2008:filters:1284991916868,...,1287734777820</id>
<updated>2010-10-28T11:59:31Z</updated>
<author>
<name>My Name</name>
<email>my#email.addr.es</email>
</author>
<entry>
<category term='filter'></category>
<title>Mail Filter</title>
<id>tag:mail.google.com,2008:filter:1284991916868</id>
<updated>2010-10-28T11:59:31Z</updated>
<content></content>
<apps:property name='from' value='an#email.addr.es'/>
<apps:property name='shouldArchive' value='true'/>
<apps:property name='shouldTrash' value='true'/>
</entry>
<entry>
<category term='filter'></category>
<title>Mail Filter</title>
<id>tag:mail.google.com,2008:filter:1284993579743</id>
<updated>2010-10-28T11:59:31Z</updated>
<content></content>
<apps:property name='subject' value='Some Relevant Subject'/>
<apps:property name='label' value='MyCoolLabel'/>
<apps:property name='shouldArchive' value='true'/>
</entry>
Other "apps:property" verbs include:
<apps:property name='hasTheWord' value=''/>
<apps:property name='shouldAlwaysMarkAsImportant' value=''/>
<apps:property name='doesNotHaveTheWord' value=''/>
I recently had a similar "problem". I was trying to get the email signature settings from Google Apps. This is what the XML Response from Google looks like:
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:apps='http://schemas.google.com/apps/2006'>
<id>https://apps-apis.google.com/a/feeds/emailsettings/2.0/domain.com/user/signature</id>
<updated>2012-08-31T15:22:03.067Z</updated>
<link rel='self' type='application/atom+xml' href='https://apps-apis.google.com/a/feeds/emailsettings/2.0/domain.com/user/signature?xoauth_requestor_id=user#domain.com'/>
<link rel='edit' type='application/atom+xml' href='https://apps-apis.google.com/a/feeds/emailsettings/2.0/domain.com/user/signature?xoauth_requestor_id=user#domain.com'/>
<apps:property name='signature' value='Here goes the email signature...'/>
</entry>
I was not able the get the attributes out of apps:property using the SimpleXMLElement. This is how i finally solved it:
// create SimpleXMLElement with XML input as string
$xml = new SimpleXMLElement($feed);
// get namespace from XML
$namespaces = $xml->getNamespaces(true);
// get children (using namespace) and attributes
$appsProperty = $xml->children($namespaces['apps'])->attributes();
// attributes are now stored in array $appsProperty and can be accessed
echo "Name: ".$appsProperty['name'];
echo "Signature: ".$appsProperty['value'];
Jon,
Full marks for linking this from Facebook, I'd never have seen it otherwise and I was doing EXACTLY this yesterday, for a google service!! I think using XPath is a bit of a red herring, let me show you how to access the elements and hopefully it will give you enough information for what you want to do.
You're on the right tracks with $xml->getNamespaces() - but don't iterate there, you need to use the namespace you want on the parent element you want. Firstly, you need to work only on the entry elements, since these are the majority, you might as well loop and check if you have the right one:
foreach($xml as $tag) {
if($tag->getName() == "entry") {
// awesome, do Cool Stuff (TM) here
}
}
Now, you have the element in the $tag variable, and you want the apps: namespaced child elements. So do this:
$tag->getChildren($namespaces['apps']);
When you iterate over that collection, you will see the info you wanted.
HTH,
Lorna

PHP, SimpleXML arrays. Unanticipated casting of an array to a string

Sample XML:
<root>
<ratings>3</ratings>
<ratings>5</ratings>
<ratings>7</ratings>
</root>
The following code is the basis for my small application, it works as would be expected:
<?php
// $xml is some simplexml object
sizeof($xml->ratings); //3
foreach($xml->ratings as $rating){
echo($rating->value."::"); //this echoes all 3 rating values: 3::5::7
}
?>
This next code, which I would normally consider to be equivalent is not. And I have no idea why:
<?php
// $xml is some simplexml object
$ratings = $xml->ratings;
sizeof($ratings); //3, all is well so far
foreach($ratings as $rating){
echo($rating."::");
/*this echoes a never-ending list of ratings,
looking like 3::5::5::5::5::5::5::5...... */
}
?>
My feeling is that the assignment operator is casting the array of simplexml objects (ratings objects) as something odd, but have no clue as to how.
Other little hints:
var_dump($xml);
/* Output is:
object(SimpleXMLElement)#7 (1) {
["ratings"]=>
array(3) {
[0]=>
string(1) "3"
[1]=>
string(1) "5"
[2]=>
string(1) "7"
}
}
*/
var_dump($ratings);
/* Output is:
object(SimpleXMLElement)#6 (1) {
[0]=>
string(1) "3"
}
*/
Your echos are not the same:
echo($rating."::");
should be
echo($rating->value."::");
Ok, cleaning up some of my own work. After attempting to simplify my issue more, I was not able to prove it. After messing with the actual code, I assume this means I have some sort of mutating object elsewhere in my app that is going bonkers and creating weird results in this xml parsing. Sorry for the confusion and needless question (I guess this proves why i'm trying to refactor some of my complexity out of this app).
As a parting gift, here is the test-suite of code that I used (from simple to more realistic) that I used to prove that all worked as advertised:
<?php
$string = <<<XML
<?xml version='1.0'?>
<root>
<ratings>3</ratings>
<ratings>5</ratings>
<ratings>7</ratings>
</root>
XML;
$xml = simplexml_load_string($string);
var_dump($xml);
echo("Size:".sizeof($xml->ratings)."\n");
foreach($xml->ratings as $rating){
echo($rating."::");
}
echo("\n"."------"."\n");
$ratings = $xml->ratings;
echo("Size:".sizeof($ratings)."\n");
foreach($ratings as $rating){
echo($rating."::");
}
echo("\n\n\n\n"."||||New Example||||"."\n\n\n\n");
$stringthree = <<<XML
<root attr1="val" attr2="desc">
<field-one>val</field-one>
<elm-two attr-name="foo">elmTwoVal1</elm-two>
<elm-three>elmThreeVal1</elm-three>
<elm-two attr-name="bar">elmTwoVal2</elm-two>
<elm-three>elmThreeVa2</elm-three>
<elm-two attr-name="bear">elmTwoVal3</elm-two>
<elm-three>elmThreeVal3</elm-three>
</root>
XML;
$xmlthree = simplexml_load_string($stringthree);
var_dump($xmlthree);
echo("Size:".sizeof($xmlthree->{'elm-two'})."\n");
foreach($xmlthree->{'elm-two'} as $elm){
echo($elm."::");
}
echo("\n"."------"."\n");
$elmMain = $xmlthree->{'elm-two'};
echo("Size:".sizeof($elmMain)."\n");
foreach($elmMain as $elm){
echo($elm."::");
}
?>

Categories