PHP сообщество

любители кода :)

Работа c SimpleXml с помощью Xpath

Bloged in Без рубрики от admin Вторник Январь 8, 2008 at about 1:49 пп

Хочу вот поделиться с сообществом скромными наработками по SimpleXml и Xpath, надеюсь, что кому-нибудь это может пригодиться.

Устройство

SimpleXml – встроенное в php5 расширение для работы с XML (доступно по умолчанию). Суть работы, которого заключается в преобразовании XML документа в некий объект (типа SimpleXMLElement).
После преобразования любой узел XML документа может быть представлен объектом SimpleXMLElement, который по сути является массивом.
Например, пусть имеется XML документ, представленный ниже:

<?xml version=”1.0″ encoding=”utf-8″ standalone=”yes”?>
<html>
 <body>
   <div id=”head”>text</div>
   <div id=”content”>
      <p></p>
      <p></p>
      <p>
	  <label>text</label>
      </p>
      <p></p>
   </div>
   <div id=”footer”>
     text
     <p></p>
     <p></p>
   </div>
 </body>
</html>

после преобразования SimpleXml:
$xml = simplexml_load_file($xmlfile);
или
$xml = simplexml_load_string($xmlstring);
смотря, где у нас расположен XML документ (в файле или строке)

мы получим объект $xml типа SimpleXMLElement, который содержит другие такие же SimpleXMLElement объекты (соответствующие преобразованные узлы исходного XML документа).

Т.е. узел (в данном случае корень) <html></html> это сам $xml или $xml[0] (помните про массивы?), а узел <body></body> это $xml->body ($xml[0]->body[0] или $xml->body[0]), а узел <div id=”footer”></div> это $xml->body->div[2] или $xml[0]->body[0]->div[2].

Далее необходимо сделать некоторые пояснения (для наглядности, на примере узла <div>):
1. Объект SimpleXMLElement всегда указывает на текстовое содержимое одноименного узла, т.е. текст, который содержится между <div></div>, при этом (для случая чтения) записи div и div[0] эквивалентны, т.е. они обе всегда указывают на 1-й элемент массива.
(т.е. echo $xml->body->div выведет 'text', и echo $xml->body->div[0] тоже выведет 'text'. А вот echo $xml->body->div[1] уже не выведет ничего, т.к. в <div id=”content”></div> нет текстового содержимого).
2. Исходя из вышесказанного для изменения текстового содержимого узла документа XML достаточно выполнить:
а) $xml->body->div='test1' (этот вариант будет работать только если div единственный потомок своего родителя, в противном случае выскочит Warning.
б) $xml->body->div[0]='test1' (этот вариант будет работать всегда, но при этом потомки не учитываются!, т.е. $xml->body->div[1]='test1' уничтожит всех потомков <div id=”content”> оставив <div=”content”>text1</div>).
3. Помните про то, что каждый узел в представлении SimpleXML это массив? Так вот (как можно было заметить из приведенных выше примеров), если одноименный потомок у родителя один, то это массив из одного элемента, а если их несколько, то соотвестственно массив из нескольких элементов.
(т.е. как видим из исходного XML документа, $xml->body[0] - массив из 1 элемента, а $xml->body->div - массив из нескольких элементов.)
4. Если у узла в исходном XML документе имеются свойства или при добавлении оных средствами SimpleXML, то к объекту SimpleXMLElement можно обращаться как двухмерному массиву, где 2-ой массив - массив свойств.
(т.е. echo $xml->body->div[0]['id'] выведет head, а $xml->body->div[0]['id']='new_id' установит новое свойство id).
И соответсвенно, если у узла нет свойств, то их можно добавить так: $xml->body[0]['id']='body_id'.
5. Помимо всего объект SimpleXMLElement имеет свои методы, о которых можно узнать из официальной документации.

Таким образом мы можем обратиться к любому узлу. Но стоит заметить, что чем больше вложенность узлов, тем неудобней к ним обращаться: например, чтобы обратиться к узлу <label></label> нам потребуется написать следующее:
$xml->body->div[1]->p[2]->label.
А если вложенность будет еще больше? То вышеприведенная строчка может удлиниться. К счастью есть такая штука как Xpath и SimpleXML может с ней работать.

Xpath

Для работы с Xpath достаточно обратиться c нужным запросом к методу xpath(), который выполнит поиск и вернет нам массив найденных объектов SimpleXMLElement, которые будут соответствовать узлам преобразованного XML документа. Например:
$result = $xml->Xpath(”//*”); - найдет вообще все объекты;
$result = $xml->Xpath(”//div”); - найдет все объекты div;
$result = $xml->Xpath(”//div[@id='head']”); - найдет 1 объект div c id = head;
$result = $xml->Xpath(”//*[text()='text']”); - найдет все объекты, текстовое содержание которых = 'text'.

И так далее, подробнее о функциях и предикатах Xpath можно узнать из официальной документации по Xpath, стоит заметить, что не все они подходят для использования в методе xpath().

Обобщение

С учетом вышесказанного можно создать так называемые “обертки” (например) вокруг метода xpath(), с помощью которых можно делать такие вещи:
<?php
// подключаем “обертку”
require_once('smplxmlfunc.php');
// преобразуем XML документ
$xml = simplexml_load_file($xmlfile);

/**
* работаем с “обертками”
*/
// устанавливаем всем div уникальные ID

setId($xml, '//div');
// добавляем в div картинки
addNode($xml, '//div', 'img');
// задаем картинкам атрибут alt
addAtrribute($xml, '//div/img', 'alt');
// удаляем div с id=3 со всеми потомками
removeNode($xml, “//div[@id='3']”, 'all');

// сохраняем
$xml->asXML($xmlfile);
// при необходимости форматируем (убираем лишние пробелы, расставляем табуляцию для читабельности)
trimXml($xmlfile);
?>

Комментариев нет

Комментариев нет.

Извините, комментирование на данный момент закрыто.