adding a namespace when using SimpleXMLElement
SimpleXML has an unusual quirk where the namespace prefixes are filtered from the root element. I'm not sure why it does this.
However, a workaround I've used has been to basically prefix the prefix, so that the parser only removes the first ones, and leaves the second
$xmlTest = new SimpleXMLElement('<xmlns:ws:Test></xmlns:ws:Test>', LIBXML_NOERROR, false, 'ws', true);
$xmlTest->addAttribute('xmlns:xmlns:ws', 'http://url.to.namespace');
$xmlTest->addAttribute('xmlns:xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
This seems to work for me, though I'm interested to understand why SimpleXML does this exactly.
simple xml add namespaced child
The namespace is the third parameter to addChild()
$item->addChild('id', 'myid', 'http://base.google.com/ns/1.0');
See the documentation for more information.
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.
Unable to add namespace to an attribute with PHP's SimpleXML
If you want to add an attribute from the namespace/prefix i
to $node don't bother declaring the namespace beforehand. Just use the third parameter of addAttribute() to provide the namespace uri for the prefix you're using in the first parameter.
$node = new SimpleXMLElement('<root></root>');
$node->addAttribute("i:somename", "somevalue", 'http://www.w3.org/2001/XMLSchema-instance');
echo $node->asXml();
prints
<?xml version="1.0"?>
<root xmlns:i="http://www.w3.org/2001/XMLSchema-instance" i:somename="somevalue"/>
If the attribute itself isn't needed, you can then remove it with unset()
, leaving the namespace declaration.
unset($node->attributes('i', TRUE)['somename']);
(PHP) Append new XML child element(s) with namespace prefix
The error message can mean that the "xmlns:*" attribute for the namespace is missing in the XML or that the namespace prefix was not registered on the XPath object.
XML Document
Namespaces need to be defined on both sides. The first is the XML itself.
<foo xmlns="urn:foo"/>
or
<f:foo xmlns:f="urn:foo"/>
Are both resolved by the parser to the same namespace and local name. You can read the element name as {urn:foo}:foo
.
Here are several *NS methods on the DOM that allow you to work with a namespace. The namespace is always the URN. The prefix is not relevant for matching.
$node->getElementsByTagNameNS('urn:foo', 'foo')
The prefix is only provided if it is needed as a value. For example to create an element node that uses it.
$document->createElementNS('urn:foo', 'foo'); // no prefix
$document->createElementNS('urn:foo', 'f:foo'); // with prefix "f"
XPath
XPath uses the prefixes in the expressions, too. But it should not read them from the document. Use DOMXpath::registerNamespace()
to register an own prefixes for a specific namespace.
Example
Your question is missing the full XML so I will add a small Atom example. You can see that the namespace is the same in the XML and in the PHP, but the prefix is different:
$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<atom:feed xmlns:atom="http://www.w3.org/2005/Atom">
<atom:title>Example Feed</atom:title>
<atom:entry>
<atom:title>Atom-Powered Robots Run Amok</atom:title>
<atom:id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</atom:id>
</atom:entry>
</atom:feed>
XML;
$dom = new DOMDocument();
$dom->loadXml($xml);
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('a', 'http://www.w3.org/2005/Atom');
var_dump($xpath->evaluate('string(//a:entry/a:title)'));
Output:
string(28) "Atom-Powered Robots Run Amok"
PHP - Namespace Shift in Child Node in SimpleXML Troubleshooting
In the line
$dmd_fields = $xml_report_abbrev_bb[0]->children('dmd', true);
This will mean that $dmd_fields
will be a list of nodes, even though there is only one node in the list - so use $dmd_fields[0]
to reference the <dmd:IndexEntry>
element. As this is the IndexEntry
element, just using this and the list off attributes for that element you can do...
echo '<br>dmd:IndexEntry is...'.$dmd_fields[0]->attributes()['indexKey'].'<br>';
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);
}
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>
Related Topics
PHP Code Formatter/Beautifier and PHP Beautification in General
How to Overload Class Constructor Within Traits in PHP >= 5.4
How to Clear Previously Echoed Items in PHP
PHP Float with 2 Decimal Places: .00
What Is Sapi and When Would You Use It
Get All Records from MySQL Database That Are Within Google Maps .Getbounds
How to Save Changed Simplexml Object Back to File
Token Was Deauthenticated After Trying to Refresh It
Starting with Laravel on Ubuntu
PHP Warning: Unknown: Failed to Open Stream
Php/MySQL Zip Code Proximity Search
Removing Password from Rsa Private Key
Difference Between Float and Double in PHP
How to Document Magic (_Call and _Callstatic) Methods for Ides
How to Stop Gd2 from Washing Away the Colors Upon Resizing Images