Search code examples
phpsimplexmlxml-namespaces

How to use namespaces when writing XML file with SimpleXML


I’m writing a Google products RSS feed with SimpleXML in PHP. I’ve got my products coming from the database and creating the RSS file fine, but having problems when it comes to namespaces.

I’ve Googled and search Stack Overflow and come across dozens of posts of how to parse XML feeds containing namespaces, but my issue is actually authoring an XML file with a namespace.

Here is what the file should look like:

<?xml version="1.0" encoding="UTF-8" ?>
<rss version ="2.0" xmlns:g="http://base.google.com/ns/1.0">
    <!-- content -->
</rss>

And here is my code:

<?php

$xml = new SimpleXMLElement('<rss></rss>');
$xml->addAttribute('version', '2.0');

$xml->addChild('channel');
$xml->channel->addChild('title', 'Removed');
$xml->channel->addChild('description', 'Removed');
$xml->channel->addChild('link', 'Removed');

foreach ($products as $product) {
    $item = $xml->channel->addChild('item');
    $item->addChild('title', htmlspecialchars($product['title']));
    $item->addChild('description', htmlspecialchars($product['title']));
    $item->addChild('link', $product['url']);
    $item->addChild('id', $product['product_id']);
    $item->addChild('price', $product['price_latest']);
    $item->addChild('brand', $product['range']);
    $item->addChild('condition', 'new');
    $item->addChild('image_link', $product['image']);
}

How do I introduce the g namespace, both the xmlns declaration in the root rss element, and then as a prefix for id, price, brand, condition and image_link in each item element?


Solution

  • Here is an example of how to do this using DOM:

    <?php
    
        $nsUrl = 'http://base.google.com/ns/1.0';
    
        $doc = new DOMDocument('1.0', 'UTF-8');
    
        $rootNode = $doc->appendChild($doc->createElement('rss'));
        $rootNode->setAttribute('version', '2.0');
        $rootNode->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:g', $nsUrl);
    
        $channelNode = $rootNode->appendChild($doc->createElement('channel'));
        $channelNode->appendChild($doc->createElement('title', 'Removed'));
        $channelNode->appendChild($doc->createElement('description', 'Removed'));
        $channelNode->appendChild($doc->createElement('link', 'Removed'));
    
        foreach ($products as $product) {
            $itemNode = $channelNode->appendChild($doc->createElement('item'));
            $itemNode->appendChild($doc->createElement('title'))->appendChild($doc->createTextNode($product['title']));
            $itemNode->appendChild($doc->createElement('description'))->appendChild($doc->createTextNode($product['title']));
            $itemNode->appendChild($doc->createElement('link'))->appendChild($doc->createTextNode($product['url']));
            $itemNode->appendChild($doc->createElement('g:id'))->appendChild($doc->createTextNode($product['product_id']));
            $itemNode->appendChild($doc->createElement('g:price'))->appendChild($doc->createTextNode($product['price_latest']));
            $itemNode->appendChild($doc->createElement('g:brand'))->appendChild($doc->createTextNode($product['range']));
            $itemNode->appendChild($doc->createElement('g:condition'))->appendChild($doc->createTextNode('new'));
            $itemNode->appendChild($doc->createElement('g:image_link'))->appendChild($doc->createTextNode($product['image']));
        }
    
        echo $doc->saveXML();
    

    See it working