PHP的DOM和SimpleXML扩展有什么区别?

74

我不太理解为什么 PHP 需要两个 XML 解析器。

能有人解释一下这两者之间的区别吗?

5个回答

111
简而言之:
SimpleXml
- 适用于简单的XML和/或简单的用例 - 有限的API用于处理节点(例如,不能太多地编程到接口) - 所有节点都是相同类型的(元素节点与属性节点相同) - 节点可以通过魔术方式访问,例如 `$root->foo->bar['attribute']`
DOM
- 适用于任何可能的XML用例 - 是W3C DOM API的实现(在许多语言中都有实现) - 区分不同的节点类型(更多控制) - 由于显式API更冗长(可以编写到接口) - 可以解析损坏的HTML - 允许在XPath查询中使用PHP函数
这两个都是基于libxml的,并且在一定程度上受到libxml函数的影响。
个人而言,我不太喜欢SimpleXml。这是因为我不喜欢对节点的隐式访问,例如$foo->bar[1]->baz['attribute']。这将实际的XML结构与编程接口绑定在一起。每个节点类型都可以用于任何情况,这也有些不直观,因为SimpleXmlElement的行为会根据其内容而自动改变。
例如,当你有时,/foo/@bar的对象转储将与/foo的相同,但对它们进行echo将打印出不同的结果。此外,因为它们都是SimpleXml元素,你可以在它们上调用相同的方法,但只有当SimpleXmlElement支持时,它们才会被应用,例如在第一个SimpleXmlElement上尝试执行$el->addAttribute('foo', 'bar')将不起作用。当然,你不能向属性节点添加属性,但关键是,属性节点本身就不会首先公开该方法。
但这只是我的个人意见,你可以自己做决定 :)

顺便说一句,不是只有两个解析器,而是PHP中还有几个解析器。SimpleXml和DOM只是将文档解析为树结构的两个解析器。其他的解析器/读取器/写入器要么是拉取式的,要么是事件驱动的。

还可以看看我的回答


1
不错的回答。为了让它更完整,你可以添加 XMLReader http://php.net/xmlreader ;) 它更快,消耗的内存不那么多(它是基于流的),但使用起来更加困难。--刚刚读到你的回答的结尾:你提到了它。^^ - KingCrunch
1
实际上,如果你运行XPath来获取属性,返回的对象可以直接转换为字符串,如果你想要它们的值的话,比如 $attrs = $sxe->xpath('/foo/bar/@baz'); echo $attrs[0]; - Josh Davis
1
@Josh,这使得它更加不直观,因为SimpleXml元素的行为取决于其内部状态。但我在这里感到似曾相识的感觉 ;) - Gordon
我理解并在一定程度上同意你对SimpleXML的所有节点设计采用单个类的批评,但“对象转储将是相同的”这一事实是对象转储(我想你指的是print_rvar_dump)的一个限制,并不是对象本身 - 尽管我认为这仍然是该库的一个限制。 - IMSoP

45

我将尽可能提供最简短的答案,以便初学者能够轻松理解。为了简洁起见,我也略微简化了一些内容。请跳到答案结尾查看过度简化的 TL;DR 版本。


DOM和SimpleXML实际上不是两个不同的解析器。真正的解析器是libxml2,它被DOM和SimpleXML内部使用。因此,DOM/SimpleXML只是使用相同解析器的两种方式,并提供将一个对象转换为另一个对象的方法。

SimpleXML旨在非常简单,因此它具有一小组功能,并专注于读取和写入数据。也就是说,您可以轻松地读取或写入XML文件,可以更新某些值或删除某些节点(有一些限制!),就这样。没有花哨的操作,您无法访问较不常见的节点类型。例如,SimpleXML无法创建CDATA节,尽管它可以读取它们。

DOM 提供了一个完整的 DOM 实现,以及一些非标准方法,如 appendXML。如果您习惯于在 Javascript 中操作 DOM,则会在 PHP 的 DOM 中找到完全相同的方法。基本上可以做任何事情,甚至处理 HTML。这种功能丰富性的反面是它比 SimpleXML 更加复杂和冗长。


附注

人们经常想知道/询问应该使用什么扩展来处理他们的XML或HTML内容。实际上选择很容易,因为一开始就没有太多选择:

  • 如果您需要处理HTML,您实际上没有选择:您必须使用DOM
  • 如果您需要执行任何花哨的操作,例如移动节点或添加一些原始XML,则您几乎必须使用DOM
  • 如果您只需要读取和/或写入一些基本的XML(例如与XML服务交换数据或读取RSS源),则可以使用任何一个。 或者 两者都可以
  • 如果您的XML文档太大而无法放入内存,则无法使用任何一种,并且必须使用XMLReader,它也基于libxml2,使用起来更加麻烦,但仍然与其他程序兼容

简述

  • SimpleXML非常易于使用,但只适用于90%的用例。
  • DOM更复杂,但可以做任何事情。
  • XMLReader非常复杂,但使用的内存非常少。非常局限性。

10
谢谢,Josh。对于那些不知道“tldr”是什么意思的人,它的意思是“太长了,没看”。 - Stann
2
请删除“复杂”一词或将其标记为个人观点。DOM并不复杂。它的清晰和明确的API使得即使对于初学者来说也很容易理解。与SimpleXml不同,你必须猜测它的功能,这是我在回答中指出的原因。仅仅因为某些东西冗长并不意味着它更加复杂。相反,除此之外,写得很好。 - Gordon
尽管TL;DR部分被认为言过其实,我不想争论一个词的含义或重要性,那么我们可以说DOM是“更加复杂”吗?我的字典似乎完全支持这种表述。 - Josh Davis
关于HTML,您可以使用DOM加载HTML文档,然后使用simplexml_import_dom和SimpleXML遍历它,因此并不完全需要使用DOM。 - IMSoP
对于大型XML文档,您可以将XMLReader与SimpleXML结合使用,充分利用两者的优点。简单性和小内存占用。只需使用XMLReader查找所需标记(item、row、product等),然后将其扩展为SimpleXML对象以便轻松处理。 - Petr Pánek

4

哪些DOM节点可以由SimpleXMLElement表示?

这两个库之间最大的区别在于SimpleXML主要是一个单一的类:SimpleXMLElement。相比之下,DOM扩展有许多类,其中大部分是DOMNode的子类型。

因此,当比较这两个库时,一个核心问题是哪些DOM提供的许多类最终可以由SimpleXMLElement表示?

以下是一个比较表格,包含那些在处理XML时实际上非常有用的DOMNode类型(有用的节点类型)。你的情况可能有所不同,例如当你需要处理DTD时:

+-------------------------+----+--------------------------+-----------+
| LIBXML Constant         |  # | DOMNode Classname        | SimpleXML |
+-------------------------+----+--------------------------+-----------+
| XML_ELEMENT_NODE        |  1 | DOMElement               |    yes    |
| XML_ATTRIBUTE_NODE      |  2 | DOMAttr                  |    yes    |
| XML_TEXT_NODE           |  3 | DOMText                  |  no [1]   |
| XML_CDATA_SECTION_NODE  |  4 | DOMCharacterData         |  no [2]   |
| XML_PI_NODE             |  7 | DOMProcessingInstruction |    no     |
| XML_COMMENT_NODE        |  8 | DOMComment               |    no     |
| XML_DOCUMENT_NODE       |  9 | DOMDocument              |    no     |
| XML_DOCUMENT_FRAG_NODE  | 11 | DOMDocumentFragment      |    no     |
+-------------------------+----+--------------------------+-----------+
作为下表所示,与DOM相比,SimpleXML的接口非常有限。除了表中的接口外,SimpleXMLElement 还将访问子元素和属性列表进行了抽象,并通过元素名称(属性访问)、属性(数组访问)提供遍历,以及通过 children()attributes() 方法提供命名空间访问,同时还是一个 Traversable,可以迭代其“自己”的子元素(元素或属性)。
只要这些神奇的接口都没问题,但是无法通过扩展SimpleXMLElement来更改它,因此它尽管如此神奇,但也同样受到限制。
要查找SimpleXMLElement对象表示的节点类型,请参见:

DOM遵循DOMDocument Core Level 1规范。您可以使用该接口处理几乎所有想象得到的XML。但是它仅适用于Level 1,因此与现代DOMDocument Levels(如3)相比,在某些更酷的功能方面受到限制。当然,SimpleXML也在这里失去了优势。

SimpleXMLElement允许转换为子类型。这在PHP中非常特殊。DOM也允许这样做,尽管需要更多的工作并选择更具体的节点类型。

XPath 1.0被两者都支持,SimpleXML的结果是SimpleXMLElementsarray,而DOM是DOMNodelist

SimpleXMLElement支持转换为字符串和数组(json),而DOM中的DOMNode类则不支持。它们提供将其转换为数组的功能,但仅像任何其他对象一样(公共属性作为键/值对)。

PHP中这两个扩展的常见用法模式:

  • 通常情况下,您开始使用SimpleXMLElement。您对XML和XPath的了解水平同样低。
  • 在与其接口的神奇作斗争后,迟早会达到一定程度的挫败感。
  • 您发现可以将SimpleXMLElement导入DOM中,反之亦然。您学习更多关于DOM的知识,并使用扩展来完成一些使用SimpleXMLElement无法完成(或者无法找到如何完成)的工作。
  • 您注意到可以使用DOM扩展加载HTML文档。还有无效的XML。以及输出格式化。这些是SimpleXMLElement无法做到的。即使使用卑劣的技巧也不行。
  • 您可能会完全切换到DOM扩展,因为至少您知道该界面更加细分,允许您做更多的事情。此外,您还会看到学习DOM Level 1的好处,因为您也可以在Javascript和其他语言中使用它(对许多人来说,DOM扩展的巨大优势)。
你可以同时使用这两个扩展,我认为你应该了解它们,越多越好。PHP中所有基于libxml的扩展都非常好且功能强大。在Stackoverflow上,标签下有一个很好的传统,涵盖了这些库的详细信息。

关于 CDATA 的注释 [2] 是错误/误导性的:当使用 __toString() 时,CDATA 节点始终以与文本节点相同的方式合并;LIBXML_NOCDATA 选项只有在“重新序列化”对象时才会有所区别——无论是使用 ->asXML() 还是输出整个结构体使用 print_r()json_encode() 等。 - IMSoP
@IMSoP:由于SimpleXMLElement确实处理了数组转换(而不仅仅是字符串转换),它表明数组转换在处理CDATA元素时存在问题。您可以在PHP中的SimpleXML和JSON编码-第II部分中找到我的详细分析,这也是我在这里回答的原因。json_encode在内部使用数组转换,因此不要被您在那里找到的json_encode()函数所困扰,因为您将其排除在外,我包含它(间接)因为涉及到数组转换。 - hakre
@演示:https://eval.in/37221 - 问题可能不是一个正确的术语,我们可以说,它有类似于决定如何遍历所有这些子元素的问题。但是[2]仍然是正确的。任何XML解析器在加载文档时都允许扩展这些CDATA元素。出于流线型化的原因(例如,如果您不想将元素转换为字符串),您可以通过将该选项设置为常量来实现此目的。这就是我所说的,所以我认为这个陈述一点也不错。还要感谢您的评论! - hakre

3

正如其他人所指出的,DOM和SimpleXML扩展并不严格意义上是"XML解析器",而是底层libxml2解析器生成的结构的不同接口。

SimpleXML接口将XML视为序列化数据结构,就像您处理解码后的JSON字符串一样。因此,它提供了快速访问文档内容的方式,重点是通过名称访问元素,并读取它们的属性和文本内容(包括自动折叠实体和CDATA节)。它支持包含多个命名空间的文档(主要使用children()attributes()方法),并可以使用XPath表达式搜索文档。它还包括对内容的基本操作支持-例如使用新字符串添加或覆盖元素或属性。

DOM 接口则将 XML 视为结构化的“文档”,其中所使用的表示形式与所代表的数据同等重要。因此,它提供了更加精细和明确地访问不同类型的“节点”的能力,比如实体和 CDATA 部分,以及一些 SimpleXML 忽略的东西,例如注释和处理指令。它还提供了一个更加丰富的操作函数集,允许你重新排列节点并选择如何表示文本内容。这样做的代价是相当复杂的 API,有大量的类和方法;由于它实现了一个标准 API(最初是为了在 JavaScript 中操作 HTML 而开发的),因此可能没有“自然 PHP”感觉,但一些程序员可能从其他上下文中熟悉它。

这两个接口都需要将完整文档解析到内存中,并有效地将指针包装到已解析的表示中;您甚至可以使用 simplexml_import_dom()dom_import_simplexml() 在两个包装器之间进行切换,例如使用 DOM API 中的函数向 SimpleXML 添加“缺失”功能。对于较大的文档,“基于拉取”的 XMLReader 或“基于事件”的 XML Parser 可能更合适。


2

SimpleXML是一个简单的XML内容解析器,仅限于解析XML格式内容。你无法解析标准的HTML内容。它易于使用且快速,因此非常适用于创建简单的应用程序。

DOM扩展则更加强大。它使您能够解析几乎任何DOM文档,包括HTML、XHTML和XML。它使您能够打开、编写甚至纠正输出代码,支持XPath和更多操作。因此,它的使用更加复杂,因为该库相当复杂,这使得它成为处理大型数据操作的完美工具。

希望回答了您的问题 :)


2
值得注意的是,您可以在同一文档上同时使用SimpleXML函数和DOM函数--请参见Jeff M在dom_import_simplexml手册页面上的示例。我已经使用这个方法来使用SimpleXML进行大部分处理,但是对于一些更棘手的操作(例如创建CDATA部分),我会使用DOM,所有操作都在同一个基础文档上进行。 - Matt Gibson
实际上不是这样的。您能否详细说明一下这些限制? - Josh Davis
好的,例如,让我们看一下http://www.php.net/manual/en/domelement.getattributens.php。SimpleXML并没有提供像这样简单易用的解决方案。我的意思是,它可以通过编写更多的代码来实现,但那是它的目的吗?我宁愿使用dom。 - usoban
我在谈论你在回答中提到的命名空间限制。除非你表达的那句话有误,你是在谈论另一种限制:“它强烈支持命名空间,而simpleXML有一定的限制。”关于你的例子,你是否知道SimpleXMLElement::attributes()?在我看来,它使用相当数量的击键来完成几乎相同的事情。 - Josh Davis
我必须承认,我的错误... 我显然不知道SimpleXMLElement :: attributes()中的$ns参数 :)现在正在编辑我的评论 :) - usoban
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接