【问题标题】:PHP simplexml_load_string() "eats" some tagsPHP simplexml_load_string() “吃掉”一些标签
【发布时间】:2022-01-11 00:37:24
【问题描述】:

经过数小时的代码调试后,我发现 simplexml_load_string() 似乎默默地删除了节点值中的所有子实体。为了演示这个问题,我做了一个小 PHP 脚本:

#!/usr/bin/php
<?php
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<channel>
  <element name="headline">
    <p>Line 1<br class="HardReturn"/>Line 2</p>
    <p>This is <b>bold</b> text</p>
  </element>
</channel>
XML;

libxml_use_internal_errors(true);
$xml = simplexml_load_string($xml, "SimpleXMLElement");
$errors = libxml_get_errors();
print_r($errors);
var_dump($xml);

在 shell 中运行时,我得到以下输出:

Array
(
)
object(SimpleXMLElement)#1 (1) {
  ["element"]=>
  object(SimpleXMLElement)#2 (2) {
    ["@attributes"]=>
    array(1) {
      ["name"]=>
      string(8) "headline"
    }
    ["p"]=>
    array(2) {
      [0]=>
      string(12) "Line 1Line 2"
      [1]=>
      string(13) "This is  text"
    }
  }
}

知道我做错了什么吗?

【问题讨论】:

    标签: php simplexml


    【解决方案1】:

    SimpleXML 并没有“吃掉”节点,它只是不太擅长在调试输出(如 print_r)中显示它们。 SimpleXML API 针对 XML 作为数据树的应用程序进行了优化,而不是像您在此处使用的带有标记的文本。

    在更复杂的 API(例如 DOM)中,您的第一个 p 元素将显示为具有 三个 子节点:

    • 包含“第 1 行”的文本节点
    • 一个br元素,属性class,值HardReturn
    • 包含“第 2 行”的文本节点

    然而,SimpleXML 没有单独的“文本节点”对象,而是当您转换为字符串时,它有一个元素的组合文本内容 的访问器。在这种情况下,它结合了两个文本节点,给出“Line 1Line 2”。您仍然可以访问 br 元素,只是看不到它相对于文本的位置。

    echo "String content: ", (string)$xml->element->p[0], "\n";
    echo "Child element attribute: ", (string)$xml->element->p[0]->br['class'], "\n";
    

    String content: Line 1Line 2
    Child element attribute: HardReturn

    使用第二个p 元素,可以表示一整棵节点树:

    • 一个文本节点包含“这是”
    • 一个子元素b
      • b 元素内的文本节点 带有文本“粗体”
    • 带有文本“text”的文本节点(在p 元素中)

    由于字符串表示只捕获直接包含的文本节点,p 元素的组合文本内容就是“这是”+“文本”。

    echo "String content: ", (string)$xml->element->p[1], "\n";
    echo "Child element string content: ", (string)$xml->element->p[1]->b, "\n";
    

    String content: This is text
    Child element string content: bold

    在这两种情况下,我们都可以看到所有的节点都还在,并且取出了原来的XML:

    echo "First paragraph: ", $xml->element->p[0]->asXML(), "\n";
    echo "Second paragraph: ", $xml->element->p[1]->asXML(), "\n";
    

    First paragraph: &lt;p&gt;Line 1&lt;br class="HardReturn"/&gt;Line 2&lt;/p&gt;
    Second paragraph: &lt;p&gt;This is &lt;b&gt;bold&lt;/b&gt; text&lt;/p&gt;

    要直接使用文本节点,您需要使用不同的 API,例如 DOM;或者,因为这个例子看起来实际上是 HTML,所以寻找一个面向 HTML 的库。

    【讨论】:

      【解决方案2】:

      不知道为什么var_dump 会跳过&lt;bold&gt; 标签,但他的&lt;bold&gt; 标签仍然存在。为了证明这一点,请尝试

      echo $xml->asXML() ."\r\n";
      

      输出应该与您的输入完全相同。也试试

      $target = $xml->xpath("//b");
      echo $target[0];
      

      输出是

      bold
      

      【讨论】:

      • 我不明白为什么当我将它强制为字符串时粗体丢失了?我希望该节点具有粗体类型的子节点。
      • “当我将它强制转换为字符串时”是什么意思?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多