使用SimpleXML编写XML文件时如何使用命名空间

时间:2012-12-20 11:34:32

标签: php simplexml xml-namespaces

我正在使用PHP中的SimpleXML编写Google产品RSS源。我的产品来自数据库并且创建了RSS文件,但是在命名空间方面存在问题。

我已经Google搜索并搜索了Stack Overflow并发现了很多关于如何解析包含命名空间的XML Feed的帖子,但我的问题实际上是 authoring 带有命名空间的XML文件。

以下是文件应该的样子:

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

这是我的代码:

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

如何在g元素的xmlns元素中引入rss命名空间,然后作为id的前缀,price每个brand元素中都有conditionimage_linkitem

3 个答案:

答案 0 :(得分:10)

可以使用SimpleXMLElement接口完成。恕我直言,这是最狡猾的订单的黑客,但它现在有效。

关键是它的工作原理如下,现在,但可能无法继续工作。因此,我不会推荐@DaveRandom接受的答案。相反,我在这里包含这个答案,以便将来的其他人可以阅读这个并节省一些时间来搜索SimpleXMLElement的方法,并且只使用DaveRandom基于DOM的方法: - )

您可以“欺骗”解析器,阻止它从您的元素名称中删除g:命名空间前缀,方法是在您的真实前缀之前添加“垃圾”前缀,例如"blah:g:condition"

我已经看到这个答案的变体用于属性前缀,但不是元素前缀。这些似乎都建议使用"xmlns:yourPrefix:yourAttribute"并传入完全限定名称空间作为第三个参数,实际上(至少从我自己的个人测试中)xmlns:部分几乎可以是任何东西(包括whitespace!)在冒号:之前,但在第一个冒号之前必须有某些,即":g:condition"将不起作用。除非你实际创建一个声明命名空间和前缀的节点,否则渲染的XML将是无效的(即你对你的元素进行攻击的命名空间前缀将没有声明)。

因此,根据您的原始代码,您将执行以下操作。还要注意在根节点声明中明确添加命名空间(虽然这可能通过API完成 - 但为什么要这么麻烦?)。

$xml = new SimpleXMLElement('<rss xmlns:g="http://base.google.com/ns/1.0" />'); // May as well chuck the google ns in the root element declaration here, while we're at it, rather than adding it via a separate attribute.
$xml->addAttribute('version', '2.0'); 
// $xml->addAttribute('hack:xmlns:g','http://base.google.com/ns/1.0'); //Or could do this instead...

$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('hack:g:id', $product['product_id']);
    $item->addChild('hack:g:price', $product['price_latest']);
    $item->addChild('hack:g:brand', $product['range']);
    $item->addChild('hack:g:condition', 'new');
    $item->addChild('hack:g:image_link', $product['image']);
}

答案 1 :(得分:7)

以下是使用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

答案 2 :(得分:2)

这可以通过将名称空间声明添加到根元素创建中并将名称空间添加到addChild函数来完成。

对原始代码的修改:

$products[] = array(
  "title" => "Foobar",
  "description" => "Foo",
  "url" => "https://f.oo.bar",
  "product_id" => "00001",
  "price_latest" => "$3.50",
  "range" => "Foo",
  "image" => "https://f.oo.bar/image.tiff",
);

$xml = new SimpleXMLElement('<rss xmlns:g="http://base.google.com/ns/1.0"/>');
$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'], "http://base.google.com/ns/1.0");
    $item->addChild('price', $product['price_latest'], "http://base.google.com/ns/1.0");
    $item->addChild('brand', $product['range'], "http://base.google.com/ns/1.0");
    $item->addChild('condition', 'new', "http://base.google.com/ns/1.0");
    $item->addChild('image_link', $product['image'], "http://base.google.com/ns/1.0");
}

print_r($xml->asXML());

会产生回应:

<?xml version="1.0"?>
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">
  <channel>
    <title>Removed</title>
    <description>Removed</description>
    <link>Removed</link>
    <item>
      <title>Foobar</title>
      <description>Foobar</description><link/>
      <g:id>00001</g:id>
      <g:price>$3.50</g:price>
      <g:brand>Foo</g:brand>
      <g:condition>new</g:condition>
      <g:image_link>https://f.oo.bar/image.tiff</g:image_link>
    </item>
  </channel>
</rss>