Ответ 1
Решение проблемы 1:
W3C определяет: значение атрибута xml:id
в качестве атрибута ID в документах XML и определяет обработку этого атрибута для идентификации идентификаторов в отсутствие проверки, без извлечения внешних ресурсов и без использования внутреннего подмножества.
Другими словами, когда вы используете
$element->setAttribute('xml:id', 'test');
вам не нужно вызывать setIdAttribute
, а также не указывать DTD или схему. DOM будет распознавать атрибут xml:id
при использовании с getElementById
без необходимости проверки документа или чего-либо еще. Это подход с наименьшими усилиями. Обратите внимание, что в зависимости от вашей ОС и версии libxml вы не получите getElementById
для работы вообще.
Решение проблемы2:
Даже если идентификаторы не были выбраны с помощью getElementById
, вы все равно можете получить их с помощью XPath:
$xpath->query('/pages/page[@id=1]');
определенно будет работать. И вы также можете напрямую получить дочерние элементы продукта для конкретной страницы:
$xpath->query('//pages/page[@id=1]/products');
Помимо этого, вы можете сделать очень мало, чтобы код DOM выглядел менее подробным, потому что это действительно сложный интерфейс. Это должно быть, потому что DOM является агностическим интерфейсом языка, снова определяемым W3C.
ИЗМЕНИТЬ после комментария ниже
Он работает, как я объяснил выше. Вот полный тестовый пример для вас. Первая часть предназначена для написания новых файлов XML с DOM. Вот где вам нужно установить атрибут xml:id
. Вы используете это вместо обычного атрибута id без имени.
// Setup
$dom = new DOMDocument;
$dom->formatOutput = TRUE;
$dom->preserveWhiteSpace = FALSE;
$dom->loadXML('<pages/>');
// How to set a valid id attribute when not using a DTD or Schema
$page1 = $dom->createElement('page');
$page1->setAttribute('xml:id', 'p1');
$page1->appendChild($dom->createElement('product', 'foo1'));
$page1->appendChild($dom->createElement('product', 'foo2'));
// How to set an ID attribute that requires a DTD or Schema when reloaded
$page2 = $dom->createElement('page');
$page2->setAttribute('id', 'p2');
$page2->setIdAttribute('id', TRUE);
$page2->appendChild($dom->createElement('product', 'bar1'));
$page2->appendChild($dom->createElement('product', 'bar2'));
// Appending pages and saving XML
$dom->documentElement->appendChild($page1);
$dom->documentElement->appendChild($page2);
$xml = $dom->saveXML();
unset($dom, $page1, $page2);
echo $xml;
Это создаст XML файл, подобный этому:
<?xml version="1.0"?>
<pages>
<page xml:id="p1">
<product>foo1</product>
<product>foo2</product>
</page>
<page id="p2">
<product>bar1</product>
<product>bar2</product>
</page>
</pages>
Когда вы снова читаете в XML, новый экземпляр DOM больше не знает, что вы объявили атрибут id
, не содержащий имен, в качестве атрибута идентификатора с setIdAttribute
. Он все равно будет в XML, но атрибут id будет просто регулярным атрибутом. Вы должны знать, что атрибуты идентификатора являются особыми в XML.
// Load the XML we created above
$dom = new DOMDocument;
$dom->loadXML($xml);
Теперь для некоторых тестов:
echo "\n\n GETELEMENTBYID RETURNS ELEMENT WITH XML:ID \n\n";
foreach( $dom->getElementById('p1')->childNodes as $product) {
echo $product->nodeValue; // Will output foo1 and foo2 with whitespace
}
Вышеописанное работает, поскольку синтаксический анализатор, совместимый с DOM, должен распознавать xml:id
атрибут идентификатора независимо от любого DTD или схемы. Это объясняется спецификациями, приведенными выше.
Причина, по которой он выводит пробелы, заключается в том, что из-за форматированного вывода есть узлы DOMText между открывающим тегом, двумя тегами продукта и закрывающими тегами, поэтому мы повторяем пять узлов. Концепция node имеет решающее значение для понимания при работе с XML.
echo "\n\n GETELEMENTBYID CANNOT FETCH NORMAL ID \n\n";
foreach( $dom->getElementById('p2')->childNodes as $product) {
echo $product->nodeValue; // Will output a NOTICE and a WARNING
}
Вышеуказанное не будет работать, потому что id
не является атрибутом идентификатора. Чтобы анализатор DOM распознал его как таковой, вам необходимо DTD или Schema, и XML должен быть проверен против него.
echo "\n\n XPATH CAN FETCH NORMAL ID \n\n";
$xPath = new DOMXPath($dom);
$page2 = $xPath->query('/pages/page[@id="p2"]')->item(0);
foreach( $page2->childNodes as $product) {
echo $product->nodeValue; // Will output bar1 and bar2
}
XPath, с другой стороны, буквально об атрибутах, что означает, что вы можете запросить DOM для элемента страницы с атрибутом id
, если getElementById
недоступен. Обратите внимание, что для запроса страницы с идентификатором p1 вам нужно будет включить пространство имен, например. @xml:id="p1"
.
echo "\n\n XPATH CAN FETCH PRODUCTS FOR PAGE WITH ID \n\n";
$xPath = new DOMXPath($dom);
foreach( $xPath->query('/pages/page[@id="p2"]/product') as $product ) {
echo $product->nodeValue; // Will output bar1 and bar2 w\out whitespace
}
И, как сказано, вы также можете использовать XPath для запроса чего-либо еще в документе. Этот не будет выводить пробелы, потому что он будет возвращать только элементы product
под страницей с id p2.
Вы также можете перемещать всю DOM с node. Это древовидная структура. Поскольку DOMNode является самым важным классом в DOM, вы хотите ознакомиться с ним.
echo "\n\n TRAVERSING UP AND DOWN \n\n";
$product = $dom->getElementsByTagName('product')->item(2);
echo $product->tagName; // 'product'
echo $dom->saveXML($product); // '<product>bar1</product>'
// Going from bar1 to foo1
$product = $product->parentNode // Page Node
->parentNode // Pages Node
->childNodes->item(1) // Page p1
->childNodes->item(1); // 1st Product
echo $product->nodeValue; // 'foo1'
// from foo1 to foo2 it is two(!) nodes because the XML is formatted
echo $product->nextSibling->nodeName; // '#text' with whitespace and linebreak
echo $product->nextSibling->nextSibling->nodeName; // 'product'
echo $product->nextSibling->nextSibling->nodeValue; // 'foo2'
На боковой панели, да, у меня есть опечатка в исходном коде выше. Это product
не products
. Но я с трудом могу утверждать, что код не работает, когда все, что вам нужно изменить, это s
. Это просто слишком похоже на то, что вы хотите быть ложкой.