PHP SimpleXML 不保留 XML 属性中的换行符

Posted

技术标签:

【中文标题】PHP SimpleXML 不保留 XML 属性中的换行符【英文标题】:PHP SimpleXML doesn't preserve line breaks in XML attributes 【发布时间】:2010-11-30 06:00:06 【问题描述】:

我必须解析外部提供的 XML,其中包含带有换行符的属性。使用 SimpleXML,换行符似乎丢失了。根据another *** question 的说法,对于 XML,换行符应该是有效的(尽管远不如理想!)。

他们为什么会迷路? [edit] 我怎样才能保存它们? [/edit]

这是一个演示文件脚本(请注意,当换行符不在属性中时,它们会被保留)。

内嵌 XML 的 PHP 文件

$xml = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<Rows>
    <data Title='Data Title' Remarks='First line of the row.
Followed by the second line.
Even a third!' />
    <data Title='Full Title' Remarks='None really'>First line of the row.
Followed by the second line.
Even a third!</data>
</Rows>
XML;

$xml = new SimpleXMLElement( $xml );
print '<pre>'; print_r($xml); print '</pre>';

print_r 的输出

SimpleXMLElement Object
(
    [data] => Array
        (
            [0] => SimpleXMLElement Object
                (
                    [@attributes] => Array
                        (
                            [Title] => Data Title
                            [Remarks] => First line of the row. Followed by the second line. Even a third!
                        )

                )

            [1] => First line of the row.
Followed by the second line.
Even a third!
        )

)

【问题讨论】:

你应该在 php 主页上问这个问题。我想这是因为它是简单的 xml 解析器。 你能解释一下你所说的 PHP 主页是什么意思吗? 最初您的问题是“为什么 SimpleXML 能做到这一点?”这就是你可以问它的开发人员而不是用户的问题。 明白了——感谢 Zilupe 的推荐。现在 bobince 已经回答了“为什么 SimpleXML 会做它所做的事情?”我想我会将其保留在 *** 上,以便希望有人可以添加我必须保留换行符的其他选项! 【参考方案1】:

这里是用该特定 XML 片段中的适当字符引用替换新行的代码。在解析之前运行此代码。

$replaceFunction = function ($matches) 
    return str_replace("\n", "&#10;", $matches[0]);
;
$xml = preg_replace_callback(
    "/<data Title='[^']+' Remarks='[^']+'/i",
    $replaceFunction, $xml);

【讨论】:

【参考方案2】:

好吧,这个问题很老,但像我一样,最终可能会有人来到这个页面。 我的方法略有不同,我认为其中最优雅的一种。

在 xml 里面,你放了一些你将用于换行的唯一词。

把xml改成

<data Title='Data Title' Remarks='First line of the row. \n
Followed by the second line. \n
Even a third!' />

然后,当您在字符串输出中的 SimpleXML 中获得所需节点的路径时,请编写如下内容:

$findme  = '\n';
$pos = strpos($output, $findme);
if($pos!=0)

$output = str_replace("\n","<br/>",$output);

它不一定是'\n,它可以是任何唯一的字符。

【讨论】:

【参考方案3】:

假设 $xmlData 在发送到解析器之前是您的 XML 字符串,这应该将属性中的所有换行符替换为正确的实体。我遇到了来自 SQL Server 的 XML 的问题。

$parts = explode("<", $xmlData); //split over <
array_shift($parts); //remove the blank array element
$newParts = array(); //create array for storing new parts
foreach($parts as $p)

    list($attr,$other) = explode(">", $p, 2); //get attribute data into $attr
    $attr = str_replace("\r\n", "&#10;", $attr); //do the replacement
    $newParts[] = $attr.">".$other; // put parts back together

$xmlData = "<".implode("<", $newParts); // put parts back together prefixing with <

使用正则表达式可能更简单,但这对我来说不是一个强项。

【讨论】:

没错,问题在于换行符在技术上在 XML 属性中无效。但是,解析器往往会修复很多问题。在所有情况下,都应该对无效实体进行编码。最好的解决方案是修复源,但如果不可用,这似乎是合法的。【参考方案4】:

这对我有用:

首先,获取xml作为字符串:

    $xml = file_get_contents($urlXml);

然后进行替换:

    $xml = str_replace(".\xe2\x80\xa9<as:eol/>",".\n\n<as:eol/>",$xml);

“。”并且 "" 在那里,因为在这种情况下我需要添加中断。新行“\n”可以替换成你喜欢的任何内容。

替换后,只需将 xml-string 作为 SimpleXMLElement 对象加载:

    $xmlo = new SimpleXMLElement( $xml );

等一下

【讨论】:

【参考方案5】:

使用 SimpleXML,换行符似乎丢失了。

是的,这是意料之中的……事实上,任何符合标准的 XML 解析器都要求属性值中的换行符表示简单的空格。请参阅 XML 规范中的 attribute value normalisation。

如果属性值中应该有一个真正的换行符,那么 XML 应该包含一个 &amp;#10; 字符引用而不是原始换行符。

【讨论】:

澄清一点:换行符是 VALID,但 XML 解析器(为了符合规范)必须减少将它们缩小为一个空格字符(参见 bobince 链接的第 3 项)。 感谢链接 bobince 和澄清 TML。所以我想我的问题现在变成了,我怎样才能保留这些换行符?我从 SharePoint Web 服务接收此数据,因此无法更改 XML 以包含 。有没有办法在这方面覆盖解析器的合规性? 很遗憾,XML 在这一点上非常不灵活;如果 Web 服务正在生成 \n,而这意味着 &amp;#10;,这是一个错误。 (令人惊讶的是,这是一个基本功能,任何 XML 序列化程序都应该能够做到这一点......当然,除非该服务正在使用正则表达式或字符串模板,而不是使用适当的 XML 库!) 除非您可以访问子类或猴子补丁您的 XML 解析器,否则您无法更改...而且我认为 SimpleXML 使用 libxml,您没有希望从 PHP 摆弄。预处理通用 XML 输入以将 &amp;#10;s 放入其中也有点不太容易,因为您必须编写大部分 XML 解析器才能区分属性中的换行符值和一个直接在标签内(&amp;#10; 将是非法的)。如果目前确切的格式非常锁定,那么像 Anthony 的黑客可以作为临时修复。 (对那里的code 感到抱歉,似乎在&amp;...; 周围的SO 标记中存在缺陷......)【参考方案6】:

新行的实体是&amp;#10;。我一直在玩你的代码,直到找到可以解决问题的方法。这不是很优雅,我警告你:

//First remove any indentations:
$xml = str_replace("     ","", $xml);
$xml = str_replace("\t","", $xml);

//Next replace unify all new-lines into unix LF:
$xml = str_replace("\r","\n", $xml);
$xml = str_replace("\n\n","\n", $xml);

//Next replace all new lines with the unicode:
$xml = str_replace("\n","&#10;", $xml);

Finally, replace any new line entities between >< with a new line:
$xml = str_replace(">&#10;<",">\n<", $xml);

根据您的示例,假设是节点或属性内出现的任何新行都会在下一行包含更多文本,而不是 &lt; 来打开新元素。

如果您的下一行包含一些包含在行级元素中的文本,这当然会失败。

【讨论】:

非常聪明!!!唯一的问题是我正在处理从 SharePoint Web 服务喷涌而出的大量 SOAP 封装的 XML,所以做如此蛮力的事情让我有点紧张。不过,根据 bobince 的帖子,看来我可能不得不朝这个方向发展。我想知道是否有更优雅的方式来实现它。

以上是关于PHP SimpleXML 不保留 XML 属性中的换行符的主要内容,如果未能解决你的问题,请参考以下文章

PHP SimpleXML解析具有多个属性的元素

PHP 5 SimpleXML 函数

simpleXML技术解析xml文件(php)

PHP SimpleXML + 获取属性

详解SimpleXML添加_修改_删除_遍历XML节点属性

xml 元素中的属性不在 simplexml 数组中