Мне трудно загружать XML-документ в DOM, сохраняя пустые теги и строки нулевого размера. Вот пример:
$doc = new DOMDocument("1.0", "utf-8"); $root = $doc->createElement("root"); $doc->appendChild($root); $element = $doc->createElement("element"); $root->appendChild($element); echo $doc->saveXML();
создает следующий XML:
<?xml version="1.0" encoding="utf-8"?> <root><element/></root>
Пустой элемент, как и ожидалось. Теперь добавим пустой элемент текста в элемент.
$doc = new DOMDocument("1.0", "utf-8"); $root = $doc->createElement("root"); $doc->appendChild($root); $element = $doc->createElement("element"); $element->appendChild($doc->createTextNode("")); $root->appendChild($element); echo $doc->saveXML();
создает следующий XML:
<?xml version="1.0" encoding="utf-8"?> <root><element></element></root>
Непустой элемент с нулевой строкой. Хорошо! Но когда я пытаюсь сделать:
$doc = new DOMDocument(); $doc->loadXML($xml); echo $doc->saveXML($doc);
на этих XML-документах я всегда получаю
<?xml version="1.0" encoding="utf-8"?> <root><element/></root>
т.е. строка нулевого размера удаляется и загружается только пустой элемент. Я считаю, что это происходит на loadXML (). Есть ли способ убедить DOMDocument loadXML () не преобразовывать строку нулевого размера в пустой элемент? Было бы предпочтительнее, если бы DOM имел TextNode с нулевым размером в качестве дочернего элемента.
Решение должно быть в PHP DOM из-за того, как это будет происходить с загруженными данными.
Проблема в том, чтобы различать эти два, заключается в том, что, когда DOMDocument загружает XML-сериализованный документ, он выполняет только спецификации.
По книге в <element></element>
в этом элементе нет пустого текстового узла, что уже прокомментировали другие.
Однако DOMDocument отлично подходит, если вы вставляете пустой текстовый узел там своим. Затем вы можете легко отличить самозакрывающийся тег (без детей) и пустой элемент (имеющий один дочерний элемент, пустой текстовый узел).
Итак, как вводить эти пустые текстовые узлы? Например, используя библиотеку XMLReaderIterator на основе XMLReader , в частности DOMReadingIteration , которая может создавать документ, предлагая каждый текущий узел XMLReader для взаимодействия:
$doc = new DOMDocument(); $iterator = new DOMReadingIteration($doc, $reader); foreach ($iterator as $index => $value) { // Preserve empty elements as non-self-closing by making them non-empty with a single text-node // children that has zero-length text if ($iterator->isEndElementOfEmptyElement()) { $iterator->getLastNode()->appendChild(new DOMText('')); } } echo $doc->saveXML();
Это дает вам свой вклад:
<?xml version="1.0" encoding="utf-8"?> <root><element></element></root>
Этот выход:
<?xml version="1.0"?> <root><element></element></root>
Безвоздмездно. Прекрасный сборщик DOMDocument . Пример из examples/read-into-dom.php
и прекрасное доказательство того, что это не проблема при загрузке документа через XMLReader, и вы имеете дело с этим единственным специальным случаем, который у вас есть.
Здесь нет никакой разницы для загрузки синтаксического анализа XML. DOM точно такой же.
Если вы загружаете / сохраняете XML-формат, который имеет проблему с пустыми тегами, вы можете использовать опцию, чтобы избежать пустых тегов при сохранении:
$dom = new DOMDocument(); $dom->appendChild($dom->createElement('foo')); echo $dom->saveXml(); echo "\n"; echo $dom->saveXml(NULL, LIBXML_NOEMPTYTAG);
Вывод:
<?xml version="1.0"?> <foo/> <?xml version="1.0"?> <foo></foo>
Вы можете обмануть XSLT-процессоры, чтобы не использовать самозакрывающиеся элементы, делая вид xsl:value-of
inserting variable, но эта переменная является пустой строкой ''
.
Входные данные :
<?xml version="1.0" encoding="utf-8"?> <root> <foo> <bar some="value"></bar> <self-closing attr="foobar" val="3.5"/> </foo> <goo> <gle> <nope/> </gle> </goo> </root>
Стили:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="*[not(node())]"> <xsl:copy> <xsl:for-each select="@*"> <xsl:attribute name="{name()}"> <xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> <xsl:value-of select="''"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
Вывод:
<?xml version="1.0" encoding="utf-8"?> <root> <foo> <bar some="value"></bar> <self-closing attr="foobar" val="3.5"></self-closing> </foo> <goo> <gle> <nope></nope> </gle> </goo> </root>
Чтобы решить это на PHP без использования XSLT-процессора, я могу думать только о добавлении пустых текстовых узлов ко всем элементам без детей (например, при создании XML).