【问题标题】:Output variable data as CDATA XML with XMLWriter使用 XMLWriter 将变量数据输出为 CDATA XML
【发布时间】:2014-12-04 05:52:14
【问题描述】:

我正在尝试在 PHP 中创建一个 Web 服务,以便应用程序与之通信,该服务将从数据库中获取数据并将其转换为应用程序的 XML 格式。但是,其中一列包含 HTML,需要作为 CDATA 输出(我认为)。我很难做到这一点。请指教

<?php
mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);
mysql_select_db(DB_NAME);

$sql = "SELECT post_date_gmt, post_content, post_title FROM [schema].wp_posts WHERE post_status = \"publish\" && post_type = \"post\" ORDER BY post_date_gmt DESC;";
$res = mysql_query($sql);

$xml = new XMLWriter();

$xml->openURI("php://output");
$xml->startDocument();
$xml->setIndent(true);

$xml->startElement('BlogPosts');

while ($row = mysql_fetch_assoc($res)) {

    $xml->startElement("Post");

    $xml->startElement("PostDate");
    $xml->writeRaw($row['post_date_gmt']);
    $xml->endElement();

    $xml->startElement("PostTitle");
    $xml->$writeRaw($row['post_title']);
    $xml->endElement();

    $xml->startCData("PostContent");
    $xml->writeCData($row['post_content']);
    $xml->endCData();

    $xml->endElement();

}

$xml->endElement();

header('Content-type: text/xml');
$xml->flush();

?>

非常感谢您提供的任何帮助!

【问题讨论】:

  • $xml-&gt;$writeRaw - 第二个“$”很可能出错?
  • 老实说,我从上周四开始一直在尝试使用 JSON 编码,并且在尝试解析它时再次创建它时遇到了麻烦。我对 XML 感觉更舒服,所以我只是回到它,直到我可以掌握 NSJSONSerializer 并编写代码来制作无警告的 JSON。

标签: php sql xml cdata


【解决方案1】:

不要使用XMLWriter::writeRaw(),除非你真的想直接写XML片段。 “Raw”表示这里不会逃出图书馆。

将文本写入 XML 文档的正确方法是XMLWriter::text()

$xml->startElement('PostTitle');
$xml->text('foo & bar');
$xml->endElement();

输出:

<?xml version="1.0"?>
<PostTitle>foo &amp; bar</PostTitle>

如果您在此示例中使用 XMLWriter::writeRaw(),则结果将包含未转义的 &amp; 并且是无效的 XML。

CDATA 部分是字符节点,与文本节点不同,但允许特殊字符不转义并保留空格。您始终必须单独创建元素节点。一个元素节点可以包含多个其他节点,甚至可以包含多个 CDATA 部分。

XmlReader 有两种创建 CDATA 节的方法:

单一方法:

$xml->startElement("PostContent");
$xml->writeCData('<b>post</b> content');
$xml->endElement();

输出:

<?xml version="1.0"?>
<PostContent><![CDATA[<b>post</b> content]]></PostContent>

或开始/结束方法:

$xml->startElement("PostContent");
$xml->startCData();
$xml->text('<b>post</b> content');
$xml->text(' more content');
$xml->endCData();
$xml->endElement();

输出:

<?xml version="1.0"?>
<PostContent><![CDATA[<b>post</b> content more content]]></PostContent>

【讨论】:

  • 非常感谢您的回复!我已经添加了您的更改,不幸的是,当它到达 $xml->text($row=['post_title']); 时出现错误该段的新代码是: $xml->startElement("PostTitle"); $xml->$text($row['post_title']); $xml->endElement();它使用相同的代码完美地打印日期,所以我不确定这里有什么问题。你能帮我解决最后一点吗?
  • 好的,我复制并粘贴了工作段,现在它可以工作了,但只是有时。由于某种原因,它仅在部分时间开始、填充和结束 post_title 元素。查询中的那一列总是被填充,所以我仍然不知道它发生了什么。
  • $xml-&gt;$text($row['post_title']); 有很多 $。应该是$xml-&gt;text($row['post_title']);
  • ThW,他们需要一个标志为你的真棒按钮!非常感谢你的帮助!我对此还有一个问题,然后我想我会在我的程序的 PHP 方面进行设置,但鉴于它与原始帖子无关,我将使它成为一个新问题。如果你愿意帮助我,我会很高兴的。你应该可以在我的个人资料上看到它。
【解决方案2】:

您可以像这样将它添加到需要用 CDATA 包装的元素中:

 $xml->writeRaw('<![CDATA['.$row['post_date_gmt'].']]>');

【讨论】:

  • 这会输出无效的 XML - &amp; 例如仍然需要在 CDATA 部分中转义。
  • 为什么需要完全转义 & 字符?如果我在不转义的情况下测试此代码,它仍然有效。
  • 好吧,如果$row['post_date_gmt'](它可能不能,但可以,这就是它可以表示任何变量数据的关键)在某处包含“]]&gt;”,这显然被破坏了。除此之外,它并不是很聪明:使用 XMLWriter 并假设问题尚未解决,使用 XMLWriter 将变得多余。对于提出问题的OP来说,这也是某种退化。正确的答案是:$xml-&gt;writeCData($row['post_date_gmt']); - 因为它已经包装了它。无需重新发明轮子。
  • 我错了,对不起。您无法为 CDATA 转义。就像 hakre 指出的 ]]&gt; 可以创建损坏的 XML。在这种情况下,DOM 会拆分 CDATA 部分。
【解决方案3】:

answer by ThW 总体上是周到的,也是要走的路。它很好地解释了PHP中XMLWriter的接口是如何使用的。

正如我们昨天在聊天中讨论的问题一样,他为这个与众不同的答案所做的大部分工作也要归功于他。

XML 中的 CDATA 有一些限制,但这也适用于将 XMLWriter 用于 CDATA 的两种方法:

字符串 ']]>' 不能放在 CDATA 节中,因此不允许嵌套 CDATA 节(格式良好的约束)。

来自:CDATA Section - 比较 2.7 CDATA Sections

通常XMLWriter 接受未编码供使用的字符串数据。例如。如果您传递一些文本,它将被正确编码(除非定制的XMLWriter::writeRaw)。

但是如果你开始一个 CDATA 部分然后写文本你直接写 CDATA,传递的字符串不能结束也不能包含另一个 CDATA 部分。这意味着,它不能包含字符序列“]]&gt;”,因为这会提前结束 CDATA 部分。

因此,将有效数据传递给 XMLWriter 的责任仍由这些方法的用户负责。

这样做通常很简单(单八位字节、基于 US-ASCII 的字符集二进制编码和 UTF-8 Unicode),这里是一些示例代码:

/**
 * prepare text for CDATA section to prevent invalid or nested CDATA
 *
 * @param $string
 *
 * @return string
 * @link http://www.w3.org/TR/REC-xml/#sec-cdata-sect
 */
function xmlwriter_prepare_cdata_text($string) {
    return str_replace(']]>', ']]]]><![CDATA[>', (string) $string);
}

还有一个用法示例:

$xml = new XMLWriter();
$xml->openURI("php://output");
$xml->startDocument();

$xml->startElement("PostContent");
$xml->writeCDATA(xmlwriter_prepare_cdata_text('<![CDATA[Foo & Bar]]>'));
$xml->endElement();

$xml->endElement();

示例输出:

<?xml version="1.0"?>
<PostContent><![CDATA[<![CDATA[Foo & Bar]]]]><![CDATA[>]]></PostContent>

DOMDocument 顺便说一句。已经做了一些非常相似的事情:

$dom = new DOMDocument();
$dom->appendChild(
    $dom->createElement('PostContent')
);
$dom->documentElement->appendChild(
    $dom->createCdataSection('<![CDATA[Foo & Bar]]>')
);
$dom->save("php://output");

输出:

<?xml version="1.0"?>
<PostContent><![CDATA[<![CDATA[Foo & Bar]]]]><![CDATA[>]]></PostContent>

要从技术上理解为什么 PHP 中的 XMLWriter 会有这种行为,您需要知道 XMLWriter 是基于 libxml2 library。完成大部分工作的 PHP 扩展将调用传递给 libxml:

PHP 的 xmlwriter_write_cdata 委托给 libxml xmlTextWriterWriteCDATA,它执行 xmlTextWriterStartCDATAxmlTextWriterWriteStringxmlTextWriterEndCDATAsuspected sequence

xmlTextWriterWriteString 用于许多例程(例如编写 PI),但仅在某些文本编写情况下,内容参数字符串 已编码:

  • 姓名,
  • 文字和
  • 属性。

对于所有其他人,它按原样传递。这包括 CDATA,因此传递给 XMLWriter::writeCData 的数据必须符合 XML CData 的要求(因为它是由该方法写入的):

  • [20]CData ::= (Char* - (Char* ']]&gt;' Char*))

从技术上讲:任何不包含“]]&gt;”的字符串。

这很容易被忽略,我自己昨天怀疑这可能是一个错误。而且我不是唯一一个,PHP.net 上的相关错误报告是:多年前的https://bugs.php.net/bug.php?id=44619

另见What does <![CDATA[]]> in XML mean?

【讨论】:

    猜你喜欢
    • 2011-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-28
    相关资源
    最近更新 更多