PHP 日本大会 2024

DOMDocument::saveXML

(PHP 5, PHP 7, PHP 8)

DOMDocument::saveXML 将内部 XML 树转储回字符串

描述

public DOMDocument::saveXML(?DOMNode $node = null, int $options = 0): string|false

从 DOM 表示创建 XML 文档。此函数通常在从头开始构建新的 dom 文档后调用,如下例所示。

参数

node

使用此参数仅输出特定节点而不带 XML 声明,而不是整个文档。

options

附加选项。支持 LIBXML_NOEMPTYTAGLIBXML_NOXMLDECL 选项。在 PHP 8.3.0 之前,仅支持 LIBXML_NOEMPTYTAG 选项。

返回值

返回 XML,如果发生错误则返回 false

错误/异常

DOM_WRONG_DOCUMENT_ERR

如果 node 来自另一个文档,则引发此错误。

变更日志

版本 描述
8.3.0 现在支持 LIBXML_NOXMLDECL

示例

示例 #1 将 DOM 树保存到字符串中

<?php

$doc
= new DOMDocument('1.0');
// 我们想要一个漂亮的输出
$doc->formatOutput = true;

$root = $doc->createElement('book');
$root = $doc->appendChild($root);

$title = $doc->createElement('title');
$title = $root->appendChild($title);

$text = $doc->createTextNode('This is the title');
$text = $title->appendChild($text);

echo
"Saving all the document:\n";
echo
$doc->saveXML() . "\n";

echo
"Saving only the title part:\n";
echo
$doc->saveXML($title);

?>

以上示例将输出

Saving all the document:
<?xml version="1.0"?>
<book>
  <title>This is the title</title>
</book>

Saving only the title part:
<title>This is the title</title>

参见

添加注释

用户贡献的注释 16 条注释

liumenglei0211 at 163 dot com
5 年前
当节点内容为空时如何显示两个标签。

<?php
$dom
= new \DOMDocument('1.0');
$document = $dom->createElement('document');
$document = $dom->appendChild($document);
$head = $dom->createElement('title','this is title');
$content = $dom->createElement('content','');
$document->appendChild($head);
$document->appendChild($content);
echo
$dom->saveXML();
?>

在 XML 中,它们被认为是完全相同的东西,任何解析器都应该识别这两种形式。但是,如果您仍然需要它,您可以这样编写

<?php
echo $dom->saveXML($dom->documentElement, LIBXML_NOEMPTYTAG);
?>

示例 1:

<?xml version="1.0"?>
<document>
<title>this is title</title>
<content/>
</document>

示例 2:
<document>
<title>this is test</title>
<content></content>
</document>
devin at SPAMISBAD dot tritarget dot com
18 年前
我花了一些时间才找到这个。到目前为止,我在手册中没有看到太多关于这个故障的解释。(我认为是 PHP5)

formatOutput = true; 当 DOM 的来源通过 load() 来自文件时似乎会失败。例如

<?php
$dom
= new DOMDocument();
$dom->load ("test.xml");
$dom->formatOutput = true;

$new_tag = $dom->createElement ('testNode');
$new_tag->appendChild (
$dom->createElement ('test', 'this is a test'));
$dom->documentElement->appendChild ($new_tag);

printf ("<pre>%s</pre>", htmlentities ($dom->saveXML()));
?>

不会缩进输出,并且会将修改后的节点全部显示在一长行中。在保存到文件时,这使得编辑 config.xml 有点困难。

通过在 load() 之前添加 preserveWhiteSpace = false;,formatOutput 可以按预期工作。例如

<?php
$dom
= new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->load ("test.xml");
$dom->formatOutput = true;

$new_tag = $dom->createElement ('testNode');
$new_tag->appendChild (
$dom->createElement ('test', 'this is a test'));
$dom->documentElement->appendChild ($new_tag);

printf ("<pre>%s</pre>", htmlentities ($dom->saveXML()));
?>

警告:如果加载的xml文件(test.xml)的根节点为空,且未简写或没有子节点,则此方法无效。

示例

无效
<?xml version="1.0"?>
<root>
</root>

有效
<?xml version="1.0"?>
<root/>

有效
<?xml version="1.0"?>
<root>
<!-- comment -->
</root>

有效
<?xml version="1.0"?>
<root>
<child/>
</root>
vikramvmalhotra at hotmail dot com
15年前
如果在XML中存储多字节字符,则使用saveXML()保存XML会产生问题。它将输出以编码格式转换的字符。

<?php
$str
= domdoc->saveXML(); // 输出 "&x#1245;" 一些编码数据
?>

请尝试以下方法

<?php
$str
= domdoc->saveXML(domdoc->documentElement); // 输出 "保存しました" 正确的多字节数据
?>
padys at tlen dot pl
20年前
保存整个文档时
DOMDocument->saveXML()生成的字符串编码由属性DOMDocument->encoding定义。

仅保存一个节点时
DOMDocument->saveXML(DOMNode) 始终生成 UTF-8 编码的字符串。
mswiercz at mwerk dot com
20年前
使用DOM生成文档时,减少内存占用的快速技巧。

不要使用
$xmlStr = DOMDocument->saveXML();
echo $xmlStr;

将大型DOM转储到输出缓冲区,请使用PHP输出流,例如:

DOMDocument->save('php://output');

生成大型DOM时,可以节省大量内存。
qjerry.com at gmail.com
15年前
去除XML声明(<?xml version="1.0" ... ?>)最简单(可能也是最快)的方法是从输出文档中单独输出DOMDocument的子节点

<?php

$document
= new DOMDocument();
$document->load('/some/file.xml');

// 这也会输出文档类型和顶层注释
foreach($document->childNodes as $node)
$result .= $document->saveXML($node)."\n";

echo
$result;

?>

在处理浏览器兼容性问题时,这可能很有用,例如,IE6中有效的XHTML的众所周知的问题。
JITR
17年前
对`devin at SPAMISBAD dot tritarget dot com`帖子的评论

感谢您指出`formatOutput`与`load*()`的陷阱。这确实让我避免了一些可能的意外。

我认为这种看似奇怪的行为是可以解释的。警告:以下内容主要基于推论和实验。很少基于研究源码和规范(我不确定有些规范是否会提供答案,至少不容易)。

正如您指出的那样,必须在从源字符串加载DOM之前设置`preserveWhiteSpace`(我使用的是`loadXML()`,但我相信您使用的`load()`情况应该相同)。这看起来合乎逻辑,因为此属性似乎控制解析和DOM创建过程,在此过程中,包含空格的文本节点要么包含,要么被丢弃。可以通过转储DOM结构并比较基于`preserveWhiteSpace`值的结果来证明这一点。当`preserveWhiteSpace`设置为`FALSE`时,返回的DOM中将不包含任何包含空格的文本节点。当此属性为`TRUE`时,这些节点将存在。

注意:在上一段中谈到空格时,我们几乎肯定是在谈论所谓的“元素内容中的空格”或“元素内容空格”,如果我没有弄错的话。另请参见我在`DOMText->isWhitespaceInElementContent()`方法注释中的评论。

至于`saveXLM()`输出的神秘影响,我认为解释在于上述空格文本节点的存在与否。这也通过实验得到证明:在向不包含任何空格节点的DOM(使用`preserveWhiteSpace`设置为`FALSE`的`loadXML()`创建的DOM)中添加此类节点后,输出格式会受到影响,在添加的节点之后,文档其余部分的格式会丢失。我认为空格文本节点的存在会强制执行这种渲染,即这些节点的内容用于分隔相邻节点,从而禁用默认格式。只有当不存在此类文本节点时,输出格式才会生效(当然,前提是`formatOutput`设置为`TRUE`)。

好吧,我不太理解的是,您是如何在`formatOutput`设置为`TRUE`的情况下得到单行输出的。当不存在空格文本节点(即使用`preserveWhiteSpace`设置为`FALSE`加载XML)*和*`formatOutput`设置为*`FALSE`*时(`formatOutput`的值相反,格式应该起作用,您不应该只得到一行),这种情况也发生在我身上过。但我没有看过你的源代码。也许你的DOM中包含不包含换行的空格节点?

至于关于根元素的警告,我在缩写形式或完整形式的空根元素中都没有看到任何问题。当你说它“有效”或“无效”时,你在想什么?
xianrenb at gmail dot com
12年前
如果要保存xhtml(在字符串中),可以尝试以下方法
<?php
$doc
= new DOMDocument('1.0');

// ...

$xhtml = (string) $doc->saveXML($doc->doctype);
$xhtml .= "\n";
$xhtml .= (string) $doc->saveXML($doc->documentElement);
?>
Alexander Fedulin
10年前
正如@fbernodi之前所说,当定义了多个命名空间时,DOMNode的saveXML存在问题。这是一个简单的解决方案

<code>
// $node 是其他文档的某个节点
$temp_document = new DOMDocument('1.0', 'utf-8');
$temp_document->appendChild($temp_document->importNode($node, true));
echo $temp_document->saveXML();
</code>
fbernoldi at gmail dot com
11年前
你好,

由于saveXML方法在保存标签时没有指定命名空间,导致我的某些解析代码出现意外行为。如果尝试将从具有引用命名空间的DOMNode保存的字符串加载到DOMDocument中,但在原始文档中该元素未在其中定义,则会得到一个格式不正确的xml。在示例中,您可以看到问题和可能的解决方案,另一个解决方案可能是向ChildElement节点添加命名空间引用(<testNS:ChildElement testNS="http://example.org">),但在我的场景中不需要。

<?php

// 测试 XML
$test_xml =
"<?xml version=\"1.0\"?>
<testNS:RootNode xmlns:testNS=\"http://example.org\">
<testNS:ChildElement>
<testNS:AnotherChildElement>我是另一个子节点。</testNS:AnotherChildElement>
</testNS:ChildElement>
</testNS:RootNode>"
;

// 我们定义测试 DOMDocument
$domDoc = new DOMDocument("1.0");
$domDoc->loadXML($test_xml);

// 我们使用 xpath 搜索 ChildElement:
$domXPath = new DOMXPath($domDoc);
$domXPath->registerNamespace("testNS", "http://example.org");
$DOMNodeList_ChildElement = $domXPath->query("//testNS:RootNode/testNS:ChildElement");
$ChildElement = $DOMNodeList_ChildElement->item(0);

echo
"加载到另一个文档中无用的xml:\n\n";
echo
$domDoc->saveXML($ChildElement);

/* 此处输出:
*
* 加载到另一个文档中无用的xml:
*
* <testNS:ChildElement>
* <testNS:AnotherChildElement>我是另一个子节点。</testNS:AnotherChildElement>
* </testNS:ChildElement>
*
*/

/**
* 用于帮助我们将元素克隆到另一个文档的函数。
* @param DOMElement $node 要克隆的节点。
* @param DOMDocument $doc 我们将在其中引用元素的文档。
* @return DOMElement 没有命名空间的新克隆元素。
*/
function cloneNode($node, $doc) {
// 使用原始 localName(无命名空间)创建新元素
$nd = $doc->createElement($node->localName);

// 克隆属性
foreach($node->attributes as $value)
$nd->setAttribute($value->localName, $value->value);

// 没有更多子节点,则完成。
if (!$node->childNodes)
return
$nd;
// 我们有子节点,添加它们
foreach($node->childNodes as $child) {
if (
$child->nodeName=="#text") // 只需要克隆文本节点,即文本注释、空格、制表符等。
$nd->appendChild($doc->createTextNode($child->nodeValue));
else
$nd->appendChild(cloneNode($child, $doc)); // 递归克隆所有子节点。
}
return
$nd;
}

// 新文档,用于引用没有命名空间的新节点
$domDoc2 = new DOMDocument("1.0");

// 我们克隆此节点,去除命名空间
$new_node = cloneNode($ChildElement, $domDoc2);

echo
"\n\n我们可以毫无问题地将此加载到 DOMDocument 中:\n\n";
echo
$domDoc2->saveXML($new_node);

/*
* 我们可以毫无问题地将此加载到 DOMDocument 中:
*
* <ChildElement>
* <AnotherChildElement>我是另一个子节点。</AnotherChildElement>
* </ChildElement>
*/

?>
Kriogen
16年前
使用以下内容创建 test.xml:
<?xml version="1.0" encoding="utf-8"?>
<Photos>
</Photos>

并在你的 php 文件中粘贴以下内容:

<?php
$simp
= simplexml_load_file('test.xml');
$node = $simp->addChild('home');
$node->addChild('mychild', '插入文本');
$s = simplexml_import_dom($simp);
$s->saveXML('test.xml');
?>

这段代码在根节点中创建一个子节点。
所有者 http://www.mensfashion.ru
samstah at gmail dot com
14年前
我们发现使用 DOMDocument::saveHTML() 会将其转换为与 HTML 4.01 兼容的标记;而不是 XHTML。简单的答案是改用 saveXML(),尽管这会在顶部添加 XML 声明。

致 qjerry.com at gmail.com,感谢你在下面的提示 - 但我认为最简单的方法似乎是使用
<?php $domDocument->saveXML($domDocument->documentElement); ?>

当然,如果你正在处理 XHTML,这也会去除文档中的任何 <!DOCTYPE> 声明。
shinsh at shinmugen dot net
16年前
小心,此函数在 5.2.6 版本中已更改。添加一个并非必需的必需参数从来不是最明智的做法,尤其对于一个经常使用的函数而言。

如果你遇到错误,要修复你的程序,请按如下方式填写第一个参数

$dom->saveXML($dom->documentElement);

为什么开发人员不将其简单地实现为可选参数,并将默认参数修复为 documentElement?
Sander
17年前
请注意,对于大型 DOM 树(数万个元素嵌套至少几层),当调用 saveXML() 时,将 formatOutput 设置为 true 会将内存使用量提高到相当高的水平。(使用 PHP 5.2.1 测试)漂亮的输出不值得付出这个代价。
匿名
18 年前
我使用了“joe”发布的函数,但以下函数对获取 innerXML 有效
<?php
$itemLeido
= $XMLRespuesta->getElementsByTagName("articulos");
foreach(
$itemLeido as $node) {
echo(
$node->ownerDocument->saveXML($node));
}
?>
nevyn at NOSPAM dot email dot PLEASE dot it
17年前
一个用于获取 XML 节点完整 XML 内容的小函数。

function innerXml($node)
{
$out = $node->ownerDocument->saveXML($node);
$re = "{^<(\\w*)(?:\\s*\\w+=(?:\"[^\"]*\"|\'[^\']*\'))*\\s*>(.*)</\\1>$}";
preg_match($re, $out, $mat);
return $mat[2];
}
To Top