Home > Software design >  Remove Namespace from child Element XML PHP
Remove Namespace from child Element XML PHP

Time:06-26

I am trying to create a xml file using simpleXML.

This is the output that I need:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss xmlns:g="http://base.google.com/ns/1.0" xmlns:c="http://base.google.com/cns/1.0" version="2.0">
  <channel>
    <title>MY XML</title>
    <link>https://example.com</link>
    <updated>2022-06-25T10:10:15</updated>
    <item>
      <g:id>123</g:id>
      <title>abc</title>
      <g:type>vertical</g:type>
      <g:price>0.00</g:price>
    </item>
 </channel>
</rss>

But this is what I am getting when adding third parameter to the addChild func.:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss xmlns:g="http://base.google.com/ns/1.0" xmlns:c="http://base.google.com/cns/1.0" version="2.0">
  <channel>
    <title>MY XML</title>
    <link>https://example.com</link>
    <updated>2022-06-25T10:10:15</updated>
    <item>
      <g:id xmlns:g="id">123</g:id>
      <title>abc</title>
      <g:type xmlns:g="type">vertical</g:type>
      <g:price xmlns:g="price">0.00</g:price>
    </item>
 </channel>
</rss>

I have tried to leave the third parameter empty like this: $xml->addChild('g:id', 123, '');

but it still adds it like this: <g:id xmlns:g="">123</g:id>

any ideas?

CodePudding user response:

You just can create the property and set the value with simpleXML.

$xml = simplexml_load_string($xmlString);
$xml->channel->item->{'g:id'} = '123';

which will append

<g:id>123</g:id>

to the item.

CodePudding user response:

SimpleXMLElement::addChild needs the namespace (if not the default one) in its third parameter.

If the namespace is not yet registered, it will be added. This is what you see in your result.

However you want to add a child element in a specific, existing namespace, namely http://base.google.com/ns/1.0 (it looks like an URL, however in terms of XML it is just a string).

Example:

$namespace = 'http://base.google.com/ns/1.0';
$root->channel->item->addChild('id', '456', $namespace);
# adds child <g:id>456</g:id>

This is what you want: Add the id element in the http://base.google.com/ns/1.0 namespace.

Here the namespace is abbreviated with g, the so called prefix (g:...). You can imagine the element name is {http://base.google.com/ns/1.0}id ("Clark notation").

See the first (root/document) element there you can see which prefix stands for which namespace:

<rss xmlns:g="http://base.google.com/ns/1.0"
     xmlns:c="http://base.google.com/cns/1.0" version="2.0">

This is why by only using id for the element name when adding and telling the correct namespace, it is inserted with the g prefix.

However when you explicitly specify the prefix in the name adding the child element and providing a (new) namespace, the namespace for that elements' prefix will be with the added child because you told SimpleXML to do so (in your case by chance/error not intend, but that is just for explanation what happened).

<g:id xmlns:g="id">123</g:id>
  • Element name is id; with prefix: g:id; with namespace: {id}id
  • Namespace is id
  • Prefix in this (and all its child elements) for it is g

This is why you need to know the namespace of the child element you add and therefore you need to know the (long) namespace name: http://base.google.com/ns/1.0.

To find it, look up to the element that has the xmlns:<prefix> attribute, then see the value:

<rss xmlns:g="http://base.google.com/ns/1.0" ...>
           ^  \___________________________/
           |           namespace
       prefix g   is ---/

When you call addChild() with that namespace, an existing prefix (here: g) will be automatically chosen by SimpleXML.

But if that child element is the first element with that (new to the document) namespace, it will be added as in your case with the xmlns attribute (all attributes starting with xml (case insensitive) are reserved attribute names in XML, same for element names):

<g:price xmlns:g="price">0.00</g:price>

Prefix "g" is namespace "price" for that "price" element (and all its children if there would be any until another child element redefines the namespace of the prefix).

If you're now explicitly looking (in code) for what namespace which prefix is, you can obtain an associative array by calling SimpleXMLElement::getNamespaces:

$namespace = $root->getNamespaces(true)['g'];

However using the real namespace name is much better as the prefix can by anything and may change while the namespace name remains stable. Also its less code to write:

$namespace = 'http://base.google.com/ns/1.0';
$root->channel->item->addChild('id', '456', $namespace);
  • Related