How to Get Attribute of Node with Namespace Using Simplexml

How to get attribute of node with namespace using SimpleXML?

So I found a way to do it using xpath, is this the best way or is there a way that's consistent with my code in the question? Just out of curiosity.

$source = 'youtube.xml';

// load as file
$youtube = new SimpleXMLElement($source, null, true);
$youtube->registerXPathNamespace('yt', 'http://gdata.youtube.com/schemas/2007');

$count = 0;
foreach($youtube->entry as $item){

//title works
echo $item->title;

$attributes = $item->xpath('//yt:duration/@seconds');
echo $attributes[$count]['seconds'];
$count++;
}

Access attributes of XML node with namespace in PHP

I solved it, I found the solution at http://bytes.com/topic/php/answers/798486-simplexml-how-get-attributes-namespace-xml-vs-preg_

foreach($x->y->z[0]->w->k as $k){                 
$namespaces = $k->getNameSpaces(true);
$xsi = $k->attributes($namespaces['xsi']);

echo $xsi['type'];
}

The getNameSpaces(true) function returns the namespaces of the XML document, then I select the one I'm looking for (xsi) and access the attribute I need like if the attributes is of the namespaces, and not of the $k node. I wish this can help someone else.

SimpleXML namespaces and attributes

Using

->children();

at the end, that is with the default namespace, will give you a child-collection of zero elements. As it's empty, SimpleXML internally runs some optimizations. Accessing it again might lead to errors:

$nodes->attributes()['value']

Warning: main(): Node no longer exists in ...

Perhaps the best way is to just leave it out and name the children you're looking for:

$nodes = ... ->extension->children($namespaces["keyvalue"])->kv;

print_r($nodes[1]->attributes()['value']);

Gives this output:

SimpleXMLElement Object
(
[0] => ZBh5ralfPl
)

As it's the second <kv> element (zero-based) of which the attribute "value" is accessed.

If you want to leave the concrete element out of it, just leave it out (do not add ->children()). The same example:

$nodes = $xml->response->extension->children($namespaces["keyvalue"])->
extension->children($namespaces["keyvalue"]);

print_r($nodes[1]->attributes()['value']);

This gives exactly the same output:

SimpleXMLElement Object
(
[0] => ZBh5ralfPl
)

as there are only <kv> elements as children there, so the numbering does not change.

Hope this helps to shed some light. Perhaps better than all this is to use xpath:

$xml->registerXPathNamespace('kv', $namespaces["keyvalue"]);

var_dump($xml->xpath('//kv:kv[@key = "AUTH"]/@value')[0]);

Which gives:

class SimpleXMLElement#6 (1) {
public $@attributes =>
array(1) {
'value' =>
string(10) "ZBh5ralfPl"
}
}

Xpath is specialized on traversal. This is even better when it comes to namespaces.

SimpleXML children's attributes behaves different with and without namespace

The reason for this is not actually anything to do with SimpleXML, but to do with some surprising details of how XML namespaces work, according to the standard.

In your example, you have a namespace declared with the prefix a, so to declare that an attribute is in that namespace, you must prefix its name with a:, just as you do with elements:

<a:child a:role="daughter"/>

It seems to be a common assumption that an attribute without a namespace prefix is in the same namespace as the element it is on, but that is not the case. The example above is not equivalent to your example:

<a:child role="daughter"/>

Another case you might see is where there is in a default (unprefixed) namespace:

<person xmlns="http://example.com/foo.bar"><child role="daughter" /></person>

Here, the child element is in the http://example.com/foo.bar namespace, but the role attribute still isn't! As discussed in this related question, the relevant section of the XML Namespaces spec includes this statement:

The namespace name for an unprefixed attribute name always has no value.

That is, an attribute with no namespace prefix is never in any namespace, regardless of what the rest of the document looks like.

So, what effect does this have on SimpleXML?

SimpleXML works on the basis of altering the "current namespace" whenever you use the ->children() or ->attributes() methods, and tracking it from then on.

So when you write:

$children = $xml->children('a', true);

or:

$children = $xml->children('http://example.com/foo.bar');

the "current namespace" is foo:bar. Subsequent use of the ->childElement or ['attribute'] syntax will look in this namespace - you don't need to call children() again every time - but your unprefixed attributes won't be found there, because they have no namespace.

When you subsequently write:

$attributes = $children->attributes();

this is interpreted the same way as:

$attributes = $children->attributes(null);

So now, the "current namespace" is null. Now when you look for the attributes which have no namespace, you will find them.

SimpleXML - add a new node using a namespace previously declared - how?

<?php
// test document, registrant as first/last element and somewhere in between
$xmlObj = new SimpleXMLElement('<epp>
<domain:create xmlns:domain="urn:someurn">
<domain:name></domain:name>
<domain:registrant></domain:registrant>
<domain:contact></domain:contact>
</domain:create>
<domain:create xmlns:domain="urn:someurn">
<domain:name></domain:name>
<domain:contact></domain:contact>
<domain:registrant></domain:registrant>
</domain:create>
<domain:create xmlns:domain="urn:someurn">
<domain:registrant></domain:registrant>
<domain:name></domain:name>
<domain:contact></domain:contact>
</domain:create>
</epp>');

foreach( $xmlObj->children("urn:someurn")->create as $create ) {
$registrant = $create->registrant;
insertAfter($registrant, 'domain:ns', 'some text');
}
echo $xmlObj->asXML();

function insertAfter(SimpleXMLElement $prevSibling, $qname, $val) {
$sd = dom_import_simplexml($prevSibling);
$newNode = $sd->ownerDocument->createElement($qname, $val);
$newNode = $sd->parentNode->insertBefore($newNode, $sd->nextSibling);
return simplexml_import_dom($newNode);
}

prints

<?xml version="1.0"?>
<epp>
<domain:create xmlns:domain="urn:someurn">
<domain:name/>
<domain:registrant/><domain:ns>some text</domain:ns>
<domain:contact/>
</domain:create>
<domain:create xmlns:domain="urn:someurn">
<domain:name/>
<domain:contact/>
<domain:registrant/><domain:ns>some text</domain:ns>
</domain:create>
<domain:create xmlns:domain="urn:someurn">
<domain:registrant/><domain:ns>some text</domain:ns>
<domain:name/>
<domain:contact/>
</domain:create>
</epp>

SimpleXML access nodes with namespace and subnodes without namespace

The argument to ->children() is always a namespace identifier or local prefix, never the tag name. If these elements were in "no namespace", you would access them with ->children('').

However, the elements with no prefix in this document do not have no namespace - they are in the default namespace, in this case urn:ehd/go/001 (as defined by xmlns="urn:ehd/go/001").

If you use the full namespace identifiers rather than the prefixes (which is also less likely to break if the feed changes), you should be able to access these easily:

$xml = simplexml_load_file($file) or die("Failed to load");   
$ehd = $xml->children('urn:ehd/001')->body;
$gnr_liste = $ehd->children('urn:ehd/go/001')->gnr_liste;
foreach ( $gnr_liste->gnr as $gnr ) {
simplexml_dump($gnr);
}

You might want to give your own names to the namespaces so you don't have to use the full URIs, but aren't dependent on the prefixes the XML is generated with; a common approach is to define constants:

const XMLNS_EHD_MAIN = 'urn:ehd/001';
const XMLNS_EHD_GNR = 'urn:ehd/go/001';

$xml = simplexml_load_file($file) or die("Failed to load");
$ehd = $xml->children(XMLNS_EHD_MAIN)->body;
$gnr_liste = $ehd->children(XMLNS_EHD_GNR)->gnr_liste;
foreach ( $gnr_liste->gnr as $gnr ) {
simplexml_dump($gnr);
}

Simple XML read element namespace attribute

You need to access the attributes with the namespace, this can be done using the attributes() method...

echo $xml->Node[0]->attributes("i",true)['type'];

Using ("i",true) says use the i prefix rather than having to put the URI.



Related Topics



Leave a reply



Submit