【问题标题】:DOMDocument parsing data inside a CDATADOMDocument 解析 CDATA 中的数据
【发布时间】:2015-06-27 17:04:55
【问题描述】:

我有这个 xml(必须通过 HTML 剪切/粘贴)。

<tr>
    <td>http://www.example.co.uk/the-view-from-22/feed/</td>
    <td>Example Blogs » The View from 22 » Example Blogs</td>
    <td>http://blogs.example.co.uk/</td>
    <td><![CDATA[Listen: The Example&rsquo;s verdict on the debate]]></td>
    <td>http://blogs.example.co.uk/coffeehouse/2015/04/podcast-special-the-debate/</td>
</tr>

它正在被加载到一个 XML dom 文档中

   $dom = new DOMDocument();
   $dom->preserveWhiteSpace = false;
   $dom->formatOutput = true;
   $dom->loadXML( $xml->asXML() );
   return $dom->saveXML();

但这会引发一个关于“’”的错误。实体未定义。

警告:DOMDocument::loadXML() [domdocument.loadxml]: Entity 'rsquo' 未在 Entity 中定义,...

因为它在 CDATA 部分中,所以我希望 DOMDocument 将其视为文本并忽略它......但它没有......有没有办法解决这个问题?

数据是直接从视图中的 mysql 数据库中提取的,因此首先“修复它”的空间不大 - 我在视图的 select 子句中添加了 CDATA,这是我的尝试修复!

编辑 按照下面的建议追溯它(干杯!)

正在使用 $xml->addChild( $key, $value ) 加载数据,但 $value 的格式为您推测的那样正在编码。

所以我只是在尝试这个......

How to write CDATA using SimpleXmlElement?

它有效 - 我现在正在加载原始文档:-

 if (strpos(strtoupper($value),'<![CDATA[') === 0 && strpos(strrev($value),'>]]') === 0) {
                $child = $xml->addChild( $key );
                $node = dom_import_simplexml($child);
                $no   = $node->ownerDocument;
                $node->appendChild($no->createCDATASection(substr($value,9,strlen($value)-12)));

                //simple key/value child pair
            } else {
                $xml->addChild( $key, $value );
            }

【问题讨论】:

  • 请同时提供准确的错误信息。你为什么使用$xml-&gt;asXML()?如果这是删除 CDATA 序列,它可能会创建无效的 XML(是的,这是可能的;我怀疑您在这里有一个 SimpleXMLElement)。
  • 嗨,添加了实际的错误信息!最终结果是空白...
  • 请将$xml-&gt;asXML() 的输出添加到问题中。我怀疑它与您发布的不同(可能直接来自您的数据库)。可以吗?
  • 同样无法重现:3v4l.org/K6LkS
  • 您还需要将逐字 XML 添加到您的问题中,而不仅仅是您在浏览器显示时复制的那个。在浏览器中检查“查看源代码”并找到“XML”。

标签: php xml dom


【解决方案1】:

您可以尝试替换它,如果它只有一个 &amp;rsquo; 而不是一大堆特殊字符。

 $dom = new DOMDocument();
 $dom->preserveWhiteSpace = false;
 $dom->formatOutput = true;
 $xml = $xml->asXML();

 $xml = str_replace('&rsquo;', '&#8217;', $xml);

 $dom->loadXML($xml);
 return $dom->saveXML();

真正的问题是,&amp;rsquo; 是如何进入您的数据库的。 修复它,在插入之前......然后你可以拉出格式良好的 XML。 https://stackoverflow.com/a/3142636/1163786

或使 rsquo 成为有效实体:

<!DOCTYPE ROOT_XML_ELEMENT [ <!ENTITY rsquo "&#8217;"> ]>

如果您的内容是 UTF-8,只需将其替换为:`


(我认为)原来的问题是这个:

警告:实体“rsquo”未在实体中定义,行:...

<?php

$xml = <<<XML
<tr>
    <td>Listen: The Example&rsquo;s verdict on the debate></td>
</tr>
XML;

$doc = new DOMDocument();
$doc->presverWhitespace = false;
$doc->formatOutput = true;
$doc->loadXML($xml);
echo $doc->saveXML();

因为实体 'rsquo' 不是有效的 XML,所以会弹出错误。现在 perrin 通过添加“CDATA 修复”解决了这个问题。这就是我对问题的理解。

你不需要 CDATA - 如果你

  • 在根处定义实体或
  • 将其添加到 DTD 以使其有效或
  • 手动替换(见上文)
  • 或者只是在它进入数据库之前修复它

【讨论】:

  • 您好,实体是有效的——它的一些 HTML 我正在通过数据库和 XML 文档传递(或者如果它正在工作的话!)
  • 我怀疑该实体是否有效。您的 XML 文件包含对实体“’”的实体引用但该实体未在任何地方声明(例如 DTD)。所以你得到:错误未定义实体。
  • @JensA.Koch:在 CDATA 中没有这样的实体,只有字符数据,所以没有什么可以让 DOMDocument 正常工作。我怀疑问题出在$xml-&gt;asXML(); 上。使用 XML 逐字将是答案。但是 OP 还没有分享为什么首先会有 SimpleXMLElement。
  • 我对此的理解略有不同,因为并非所有实体在 CDATA 部分中都是已知且有效的。w3.org/TR/REC-xml/#NT-Char
  • 让我们来看看这个:“&”:检查,。好的! “#”:检查,OK! - “8”:检查,OK! - “2”:检查,OK! - “1”:检查,OK!,“7”:检查,OK! - “;”:检查,OK! -- 结果:所有这七个字符都是文档编码中的有效字符(假定为 US-ASCII)。最好在这里比较en.wikipedia.org/wiki/CDATA#Issues_with_encoding --- 很可能用户有双重编码。
【解决方案2】:

正如我用my example code 演示的那样,我无法重现您的问题。因此,我得出的结论是,您必须进行双重编码,而双重编码的数据是 XML 解析器阻塞并正确地向您发出警告的地方。只是由于双重编码,这不是立即可见的。

对数据进行一次解码,以便对其进行正确的 XML 编码。然后 DOMDocument 就可以轻松加载了。


旧答案(对于通过搜索引擎来到这里的用户仍然有用):

我怀疑您的问题与 $xml-&gt;asXML() 有关,因为 CDATA 部分不会产生该错误。

有更好的方法先转换成DOMDocument:

$dom = dom_import_simplexml($xml)->documentElement;

这也应该保留 CDATA 部分的编码(不是 100% 肯定)。对于您的格式,您可能需要重新加载文档,但也许您不需要。试试

$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$result = $dom->saveXML();

如果结果还不是您正在寻找的预期漂亮打印格式, 您可以从 dom 重新加载文档:

$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($dom->saveXML());
$result = $dom->saveXML();

我希望这是 DOMDocument,以前的 CDATA 编码字符没有问题,类似于实体。

转换函数dom_import_simplexml() is in the manual 和 SimpleXML 和 DOM 共享接口,如果你想在 DOM 和 SimpleXML 之间切换或反之,使用它应该是首选方式。

【讨论】:

  • 根据您之前的评论 - asXML() 正在编码 CDATA 标签(!)
  • 所以它显示 <![CDATA[... !我更改为您建议的代码,但现在得到 Fatal error: Call to undefined method stdClass::saveXML()
  • @pperrin:您存储到数据库中的任何内容都不是 XML。在将其存储到数据库之前,您对其进行了错误的编码。您必须先修复数据。很可能是双重编码。您问题中的代码显示得太少,无法确定这一点,更重要的是,实际上给出了其他建议,而不仅仅是修复您的数据。
  • 数据库包含任意文本片段(恰好是 HTML)- 我想通过 XML 文档将此片段传输到另一个进程。因此,该字段被读取、包装在 CDATA 中并放入 XML 文档中,XML 在 CDATA 内部没有任何业务(除了 CDATA 的末尾)。根据您的建议,我看到到 XML 的往返行程然后通过“saveXML”将 CDATA 搞乱了 - 我会逐步查看我得到的 - 谢谢。
猜你喜欢
相关资源
最近更新 更多
热门标签