I have a XML file, and the layout is such
<author>
<name></name>
<iso></iso>
<price></price>
</author>
I know how it loops. I want to know how I can extract the value of
<name>
Thanks
Jean
[edit]
my apologies, if in the
<author>
<name>
<first_name></first_name>
<last_name></lastname>
</name>
</author>
I want to extract first_name
Use simplexml or similar:
<?php
$string = <<<XML
<author>
<name>
<first_name>John</first_name>
<last_name>Smith</last_name>
</name>
</author>
XML;
$xml = simplexml_load_string($string);
var_dump($xml);
?>
Will output something like this:
object(SimpleXMLElement)#1 (1) {
["name"]=>
object(SimpleXMLElement)#2 (2) {
["first_name"]=>
string(4) "John"
["last_name"]=>
string(5) "Smith"
}
}
And you can access the name like this:
echo $xml->name->first_name; // outputs 'John'
echo $xml->name->last_name; // outputs 'Smith'
Use SimpleXML.
Edit: I see. That wasn't showing up before. Try this:
$xml = simple_xml_load_string([your XML string])
echo $xml->name;
Does that work?
Related
XML WITHOUT NAMEPSACES:
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:g="http://base.google.com/ns/1.0">
<title>XML WITHOUT NAMESPACES</title>
<entry>
<price>67 EUR</price>
</entry>
</feed>
XML WITH NAMEPSACES:
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">
<channel>
<title>XML WITH NAMESPACES</title>
<entry>
<g:price>67.00</g:price>
</entry>
</channel>
</rss>
My code:
$product_nodes = $xml_file->xpath("//*[local-name()='entry']");
section of var_dump:
XML WITHOUT NAMESPACES ON CHILDS:
object(SimpleXMLElement)#374 (12) {
["price"]=>
string(6) "67 EUR"
XML WITH NAMESPACES ON CHILDS:
object(SimpleXMLElement)#376 (0) { } [1]=>
object(SimpleXMLElement)#371 (0) { } [2]=>
How to retrieve nodes with namespace same as nodes without namespace?
I understand this question has been asked, but all solutions I've found have not worked for me.
Given this:
SimpleXMLElement Object
(
[0] =>
<test>
<blah>
<x>
filler
</x>
</blah>
</test>
)
How do I get the <x> value?
I've tried
$doc = simplexml_load_string($xml_response);
print_r($doc->test->blah->x);
print_r($doc[0]->test->blah->x);
print_r($doc->{'0'}->test->blah->x);
print_r((string)$doc->{'0'}->test->blah->x);
print_r((string)$doc[0]->test->blah->x);
print_r((string)$doc->test->blah->x);
Here's the raw xml:
1.0" encoding="UTF-8" ?>
<xxx>
<test>
<blah>
<x>fillertexthere</x>
</blah>
<fillertexthere</Reason>
</test>
</xxx>%
Your SimpleXMLElement contains one string. I think that is because your xml contains < and >.
What you could do is first use htmlspecialchars_decode and then load your string.
(I have removed this line <fillertexthere</Reason> and % from your raw xml)
$xml_response = <<<DATA
<?xml version="1.0" encoding="UTF-8" ?>
<xxx>
<test>
<blah>
<x>fillertexthere</x>
</blah>
</test>
</xxx>
DATA;
$xml_response = htmlspecialchars_decode($xml_response);
var_dump($xml_response);
This will look like:
object(SimpleXMLElement)#1 (1) {
["test"]=>
object(SimpleXMLElement)#2 (1) {
["blah"]=>
object(SimpleXMLElement)#3 (1) {
["x"]=>
string(14) "fillertexthere"
}
}
}
You can echo the value of x like this:
echo $doc->test->blah->x;
I have an XML page called (www.example.com/name.xml)
it contains the bellow elements :
<xml>
<names>
<name id='6' >Name 1 </name>
<name id='7'>Name 2</name>
<name id='8'>Name 3</name>
</names>
</xml>
and here is my PHP script :
<?php
$id='6';
$url = "www.example.come/name.xml";
$xml = simplexml_load_file($url);
$position ="$xml->name id='$id' ";
?>
So how can I get it ?
You can simply use xpath for this:
$id='6';
$xml = simplexml_load_file($url);
$position = (string)$xml->xpath("//name[#id='$id']")[0];
echo $position;
Output:
Name 1
This uses xpath to get the text from the <name> where id is 6. Then it casts the SimpleXMLElement to (string), thus providing the output.
Based on your provided data, you could do it like this:
$id='6';
foreach($xml->names->name as $name) {
if ((string)$name->attributes()->id === $id) {
// here $name will be the element for which the attribute id='6'
var_dump($name);
}
}
Will result in:
object(SimpleXMLElement)#3 (2) {
["#attributes"]=>
array(1) {
["id"]=>
string(1) "6"
}
[0]=>
string(7) "Name 1 "
}
$name is of type SimpleXMLElement and the value you are looking for is in the attributes.
I am writing a class to read content of an xml with mysql-like syntax (of course it not completly the same syntax and I ignore many things, just want to have some basics).
First of all, statements look like:
"SELECT name passwd FROM table WHERE id = 1 and description = 'desc'"
Some basic rules:
There has to be a whitespace between everything
not more than two comparisons after the "WHERE"
I haven't done anything but the method for the select statement.
I explode the statement, sort it into an array and then try to translate it to DOMXpath
It works if there's no WHERE. But I'm struggling with the where-clauses (here what I have done so far:)
statement: "SELECT name pw FROM user WHERE id = '1' and description = 'test or test2'"
the array looks like:
array(4) {
["type"]=>
string(6) "SELECT"
["searchFields"]=>
array(2) {
["searchField0"]=>
string(4) "name"
["searchField1"]=>
string(2) "pw"
}
["tableName"]=>
string(4) "user"
["comparer"]=>
array(2) {
["where0"]=>
array(3) {
["field"]=>
string(2) "id"
["operator"]=>
string(1) "="
["value"]=>
string(3) "'1'"
}
["where1"]=>
array(4) {
["splitTag"]=>
string(3) "and"
["field"]=>
string(11) "description"
["operator"]=>
string(1) "="
["value"]=>
string(15) "'test or test2'"
}
}
}
How I'm trying to convert the statement to Xpath with the following code:
for ($i = 0; $i < count($arrQuery['searchFields']); $i++) {
$conditions = "";
foreach ($arrQuery['comparer'] as $value) {
switch (count($arrQuery['comparer'])) {
case 1:
$conditions .= '//parent::content[#name="'.$value['field'].'" and text()='.$value['value'].']';
break;
case 2:
if (!isset($value['splitTag']) || $value['splitTag'] == "and") {
$conditions .= '//parent::content[#name="'.$value['field'].'" and text()='.$value['value'].']';
break;
} else {
//$conditions .= 'content[#'.$value['field'].' and text()='.$value['value'].']//parent::*';
}
break;
}
}
$xpathquery = '//datarange[#name="'.$arrQuery['tableName'].'"]'.$conditions.'//content[#name="'.$arrQuery['searchFields']['searchField'.$i].'"]';
$nodeList = $this->xpath->query($xpathquery);
foreach ($nodeList as $node) {
$arrContent['searchField'.$i][] = $node->nodeValue;
}
}
My first Point is: if the condition of the if-clause in case 2 is confirmed, the created xpath isn't working (might be problem with parent or my logic)
My second Point is: I still have no idea how to handle the case that the condition doesn't match and $value['splitTag'] is "or". If anyone has a hint how to solve that, I would be very thankfull.
/EDIT: Ok, thanks for the tip, here's an example of my xml:
<database>
<datarange id="user">
<dataset id="0">
<content name="id">
1
</content>
<content name="description">
test or test2
</content>
<content name="name">
Name
</content>
<content name="pw">
Passwort
</content>
</dataset>
<dataset id="1">
<content name="name">
Name2
</content>
<content name="pw">
Passwort2
</content>
</dataset>
</datarange>
<datarange id="config">
<dataset id="0">
<content name="type">
command
</content>
<content name="name">
lab
</content>
<content name="type">
request
</content>
<content name="target">
target_name
</content>
<content name="desc">
Address Book
</content>
</dataset>
</datarange>
</database>
Given your input document and the query: SELECT name pw FROM user WHERE id = '1' and description = 'test or test2 you will have to build yourself the following XPath to get to the dataset node that has the values you need:
//datarange[#id = 'user']/dataset
[normalize-space(content[#name = 'id']) = '1']
[normalize-space(content[#name = 'description']) = 'test or test2']
This one will give you the dataset node that you can then run the following on:
normalize-space(content[#name = 'name'])
to get the name and:
normalize-space(content[#name = 'pw'])
Here's a simple XSLT to test it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="//datarange[#id = 'user']/dataset
[normalize-space(content[#name = 'id']) = '1']
[normalize-space(content[#name = 'description']) = 'test or test2']"/>
</xsl:template>
<xsl:template match="dataset">
name: <xsl:value-of select="normalize-space(content[#name = 'name'])"/>
pwd: <xsl:value-of select="normalize-space(content[#name = 'pw'])"/>
</xsl:template>
</xsl:stylesheet>
When applied to your input document it will produce:
name: Name
pwd: Passwort
You can now factor it into your query SQL-like-to-XPath engine.
One more thing though. If you can upgrade to XPath 2.0 you may want to try the conditional and quantified expressions to make your XPath more query-like. And who knows, maybe you won't need a SQL-like syntax to begin with. Plus there's XQuery.
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