6

我正在用 PHP 中的 SimpleXML 编写 Google 产品 RSS 提要。我的产品来自数据库并可以很好地创建 RSS 文件,但是在涉及名称空间时遇到了问题。

我在 Google 上搜索并搜索了 Stack Overflow,发现了数十篇关于如何解析包含命名空间的 XML 提要的帖子,但我的问题实际上是使用命名空间创作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的前缀?idpricebrandconditionimage_linkitem

4

3 回答 3

12

可以使用 SimpleXMLElement 接口来完成。恕我直言,这是最狡猾的命令,但它现在有效。

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

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

我已经看到此答案的变体用于属性前缀,但不适用于元素前缀。这些似乎都建议使用"xmlns:yourPrefix:yourAttribute"和传递完全限定的名称空间作为第三个参数,实际上(至少,根据我自己的个人测试)该xmlns:部分几乎可以是冒号之前的任何内容(包括空格!):,但是在第一个冒号之前必须有一些东西":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']);
}
于 2013-05-27T09:53:51.113 回答
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();

看到它工作

于 2012-12-20T12:10:54.197 回答
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>
于 2017-03-30T23:17:14.553 回答