14

I am trying to generate an RSS Google Merchant, using SimpleXML.

The sample given by Google is:

<?xml version="1.0"?>
<rss version="2.0" 
xmlns:g="http://base.google.com/ns/1.0">
<channel>
<title>The name of your data feed</title>
<link>http://www.example.com</link>
<description>A description of your content</description>
<item>
<title>Red wool sweater</title>
<link> http://www.example.com/item1-info-page.html</link>
<description>Comfortable and soft, this sweater will keep you warm on those cold winter nights.</description>
<g:image_link>http://www.example.com/image1.jpg</g:image_link> <g:price>25</g:price> <g:condition>new</g:condition> <g:id>1a</g:id>
</item>
</channel>
</rss>

My code has things like:

$product->addChild("g:condition", 'new');

Which generates:

<condition>new</condition>

I read online that I should instead use:

$product->addChild("g:condition", 'new', 'http://base.google.com/ns/1.0');

Which now generates:

<g:condition xmlns:g="http://base.google.com/ns/1.0">new</g:condition>

This seems very counter-intuitive to me, as now the "xmlns" declaration is on almost EVERY line of my RSS feed intead of just once in the root element.

Am I missing something?

Nathan H
  • 48,033
  • 60
  • 165
  • 247

3 Answers3

16

As @ceejayoz said, you need to add the "http://base.google.com/ns/1.0" namespace to the root node so that SimpleXML knows the namespace has already been declared and doesn't emit a duplicate prefix binding.

I think you may need to read a tutorial on XML Namespaces, because I'm not sure you really understand what the "g:" is doing here.

Here is a more complete example. XML:

$xml = <<<EOT 
<?xml version="1.0"?> 
<rss version="2.0" xmlns:g="http://base.google.com/ns/1.0"> 
  <channel> 
    <title>The name of your data feed</title> 
    <link>http://www.example.com</link> 
    <description>A description of your content</description> 
    <item> 
      <title>Red wool sweater</title> 
      <link> http://www.example.com/item1-info-page.html</link> 
      <description>Comfortable and soft, this sweater will keep you warm on those cold winter nights.</description> 
      <g:image_link>http://www.example.com/image1.jpg</g:image_link> 
      <g:price>25</g:price> 
      <g:id>1a</g:id> 
    </item> 
  </channel> 
</rss> 
EOT 
; 

Code:

$rss = new SimpleXMLElement($xml); 
$NS = array( 
    'g' => 'http://base.google.com/ns/1.0' 
); 
$rss->registerXPathNamespace('g', $NS['g']); 
$product = $rss->channel->item[0]; // example 

// Use the complete namespace. 
// Don't add "g" prefix to element name--what prefix will be used is 
// something SimpleXML takes care of. 
$product->addChild('condition', 'new', $NS['g']); 

echo $rss->asXML(); 

I usually use this pattern to deal with namespaces easily:

$rss = new SimpleXMLElement($xml); 
$NS = array( 
    'g' => 'http://base.google.com/ns/1.0' 
    // whatever other namespaces you want 
); 
// now register them all in the root 
foreach ($NS as $prefix => $name) { 
    $rss->registerXPathNamespace($prefix, $name); 
} 
// Then turn $NS to an object for more convenient syntax 
$NS = (object) $NS; 
// If I need the namespace name later, I access like so: 
$element->addChild('localName', 'Value', $NS->g); 
Francis Avila
  • 31,233
  • 6
  • 58
  • 96
  • 1
    Where you have `new SimpleXMLElement($xml)`, you don't show what $xml is. This is what I ended up using (wasn't working without it): `''` – Kerry Jones Nov 28 '13 at 14:14
  • 1
    Read more carefully: the first code block in the answer starts with `$xml = ` and evaluates to an xml string. – Francis Avila Nov 29 '13 at 19:54
  • Works like a charm ! Thanks for the detailled solution ! – Julien Nov 25 '15 at 10:46
  • I think there's a bit of a misunderstand here. As its name suggests, `registerXPathNamespace` is only used when querying with XPath, and has nothing to do with adding namespaces to a document you're generating. – IMSoP Apr 22 '17 at 14:59
14

This do the trick:

$product->addChild('xmlns:g:condition', 'new'); 
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
Alex Toro
  • 141
  • 1
  • 2
  • 2
    This worked for me, and I feel actually answers the question - the other posts about namespaces and parents nodes having them were great, but my file already had the parent namespace and I just needed to add the "xmlns:" to get it to add the node correctly. Thanks! – Brettins Nov 14 '16 at 23:53
  • 2
    This relies on a quirk (bug) in the XML library which doesn't notice that you've used illegal syntax for the tag name. It's not actually handling the namespace correctly in any way. – IMSoP Apr 22 '17 at 15:05
1

You need to add that namespace to a parent node, preferably the root rss one, so that the child nodes can inherit it instead of having to explicitly specify it each time.

ceejayoz
  • 176,543
  • 40
  • 303
  • 368
  • I tried `$xml = new SimpleXMLElement(''); $xml->addAttribute('version','2.0'); $xml->registerXPathNamespace('g', 'http://base.google.com/ns/1.0');` And then later: `$product->addChild("g:condition", 'new');` It generates: `new` It skips "g:" again ... – Nathan H Jun 10 '10 at 22:31
  • You need to still specify the namespace in the `addChild` calls. It just shouldn't be output because the root node already has it. – ceejayoz Jun 11 '10 at 00:24