Unable to add namespace to an attribute with PHP&#

2019-01-07 23:18发布

问题:

I am creating an Atom feed, when I tried below to add xmlns:i as an attribute -

$node->addAttribute("xmlns:i","http://www.w3.org/2001/XMLSchema-instance"); 

I got this as an output -

i="http://www.w3.org/2001/XMLSchema-instance"

"xmlns:" part was cut off. do I need to escape the :-character? Or is they any other way to add this namespace?

回答1:

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']);


回答2:

If you don’t want to have to add a dummy attribute to your root element, you can declare the namespace manually on it by adding an xmlns attribute for your i prefix:

<root xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

In order to do so, and as hinted in an existing answer (Unable to add Attribute with Namespace Prefix using PHP Simplexml), you have to prefix the new attribute with xmlns: (since the xmlns: namespace prefic is not declared in your document). And since xmlns: is part of the name of that attribute, you therfore need two occurrences of xmlns:

$uri = 'http://www.w3.org/2001/XMLSchema-instance';

$root = new SimpleXMLElement('<root/>');
$root->addAttribute( 'xmlns:xmlns:i', $uri );
                      ######

$child = $root->addChild('foo');
$child->addAttribute( 'xmlns:i:bar', 'baz');
                       ######

echo $root->asXml();

Gives (formatted manually for readability):

<?xml version="1.0"?>
<root xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <foo i:bar="baz"/>
</root>

So this xmlns: prefixing seems to cheat it. Note that if you reload the element after setting that attribute, it is possible to use the namespace uri as well when adding children, and this without specifying the prefix:

$root = new SimpleXMLElement( $root->asXML() );

$child = $root->addChild('foo');
$child->addAttribute( 'i:bar', 'bazy', $uri );
                                       ####

echo $root->asXml();

Gives (again, formatted manually):

<?xml version="1.0"?>
<root xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <foo i:bar="baz"/>
    <foo i:bar="bazy"/>
</root>

This second example seems to be closer to the intended (or at least expected) use.

Note that the only way to do this properly would be to use the more complete (but unfortunately also more complex and more verbose) DOMDocument classes. This is outlined in How to declare an XML namespace prefix with DOM/PHP?.



回答3:

I found this looking for the same thing, and none of the answers really worked for me. So, I tried a different route. If SimpleXML isn't managing namespace correctly, use DOM instead.

So, something like this should work:

$s = new simplexmlelement('<root/>');
$d = dom_import_simplexml($s);
$d->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:i", "http://www.w3.org/2001/XMLSchema-instance");
$s->addChild("bar", "bazy", "http://www.w3.org/2001/XMLSchema-instance");
$f = $s->addChild("foo", "quux");
$f->addAttribute("i:corge", "grault", "http://www.w3.org/2001/XMLSchema-instance");

That will result in this:

<?xml version="1.0"?>
<root xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
   <i:bar>bazy</i:bar>
   <foo i:corge="grault">quux</foo>
</root>