Изменяем содержимое XML

Я уже рассказывал в некоторых статьях о том, как извлекать информацию из XML-файла разными способами, но ни разу не написал о том, как ее туда вставлять :) Сегодня расскажу о своих экспериментах по этому поводу с помощью SimpleXML.

Допустим, у нас есть XML-файл следующей структуры и содержания (почти как в другой моей статье):

<?xml version="1.0" encoding="UTF-8" ?>
<books>
  <book>
    <title>Антилузер</title>
    <author>Илиас Меркури</author>
    <pages>288</pages>
    <isbn>978-5-271-19777-2</isbn>
    <year>2008</year>
  </book>
  <book>
    <title>Присягнувшие тьме</title>
    <author>Жан-Кристоф Гранже</author>
    <pages>848</pages>
    <isbn>978-5-389-00013-1</isbn>
    <year>2008</year>
  </book>
  <book>
    <title>Евангелие от Джимми</title>
    <author>Дидье Ван Ковеларт</author>
    <pages>400</pages>
    <isbn>978-5-98358-189-0</isbn>
    <year>2008</year>
  </book>
</books>

И мы хотим поменять количество страниц у второго элемента – сделать к примеру не 848, а 850, поступаем следующим образом:

<?php
	$xml = simplexml_load_file('test.xml');
	$xml->book[1]->pages = 850;
	$xml->asXML('test.xml');
?>

Тут мы явно обратились ко второму элементу из множества books. Но давайте попробуем найти элемент со значением pages равным 848 с помощью XPath. Для этого придется немного извратиться и задействовать XPath из DOM:

<?php
	$xml = simplexml_load_file('test.xml');

	// превращаем объект SimpleXML в DOMDocument
	$dom_sxe = dom_import_simplexml($xml);
	$dom = new DOMDocument('1.0', 'UTF-8');
	$dom_sxe = $dom->importNode($dom_sxe, true);
	$dom_sxe = $dom->appendChild($dom_sxe);

	// ищем в объекте books книгу с 848-ю страницами
	$xpath = new DOMXPath($dom);
	$pos   = $xpath->evaluate('count(//book[pages=848]/preceding-sibling::*)'); // нашли позицию

	// обновляем кол-во страниц
	$xml->book[intval($pos)]->pages = 850;

	// сохраняем файл
	$xml->asXML('test.xml');
?>

Недостаток данного примера конечно в том, что пришлось для поиска задействовать DOM, но тут наверное ничего не поделаешь, т.к. в SimpleXML нет похожей функции – evaluate для XPath.

И еще одно замечание: данный пример показывает, как найти и заменить одно значение на другое, не затрагивая остальные такие же значения, т.е. заменяет первое встречное. Чтобы заменить все значения, нужно засунуть все это в цикл.

Считайте, что с помощью этого примера мы научились конвертировать SimpleXML в DOM и освоили XPath-запрос, позволяющий вычислить позицию искомого элемента ;)

Теперь научимся добавлять описание еще одной книги в наш документ:

<?php
	$xml = simplexml_load_file('test.xml');

	$book = $xml->addChild('book');
	$book->addChild('title', 'New title');
	$book->addChild('author', 'New author');
	$book->addChild('pages', '100');
	$book->addChild('isbn', 'New ISBN');
	$book->addChild('year', '2009');

	// сохраняем файл
	$xml->asXML('test.xml');
?>

Этот код добавит в конец документа еще одну книгу.

Кроме этого нам может потребоваться добавить атрибут к книге с помощью addAttribute, но я думаю в этом нет ничего сложного и пример приводить не буду.

А что если нам нужно удалить какой-то элемент или атрибут их XML-файла? Тут я бы посоветовал с помощью XPath выбрать все элементы кроме того, которого нужно удалить, и сохранить эти элементы в файл. В итоге получим эффект удаления ;)

Ну а в принципе, может я тут изобретаю велосипед и для PHP уже есть удобные решения для работы с XML-файлами (их создание/изменение)? Если кто знает – прошу в комменты.



Теги: ,

Читайте также:



 #  #  #  #  #  #  #  #  #  #

8 Ответов на “Изменяем содержимое XML”

  1. Dexter
    Июнь 23rd, 2009

    Вообще-то удалить любую ветку можно гораздо проще:

    unset($xml->NODE[$id]);

    где NODE – нужная к удалению ветка или параметр

  2. novice
    Июнь 24th, 2009

    В принципе да. Но тут самое интересное, что строчка

    unset($xml->book[0]);

    работает, а вот такая конструкция нет:

    foreach ($xml->book as $k => $book) {
    if ($k == 0) {
    unset($xml->book[$k]);
    break;
    }
    }

    Если посмотреть в цикле на значение $k, то мы увидим название тега book.

  3. Борис
    Июль 28th, 2009

    в вышеуказанном примере unset в foreach не работает, т.к. k – это не число 0, а объект! который при сравнении с числом автоматически преобразуется к числу. а вот при использовании его как индекса массива он уже является строкой. надо его явно преобразовывать к числу: unset($xml->book[(int) $k]);

    но вообще то для удаления лучше использовать самописную функцию removeNode http://i-novice.net/gout/DkIQRQgcSxMSRxkVX0MbDwZCHlkCWkBZVRZSDElEXQNIRQ1YQl8BHAhcGRVfQxZZUwcGAg==/

  4. Борис
    Июль 28th, 2009

    еще вариант удаления:

    foreach ($xml->book as $k => $book) {
    if ($k == 0) { // любое условие
    $r = $xml->xpath(’.');
    unset($r[0][0]);
    break;
    }
    }

  5. Holy Diver
    Сентябрь 28th, 2009

    Да, но все эти способы не могут удалять атрибуты, а хотелось бы….

  6. Кирюха
    Февраль 2nd, 2010

    Вау-вау! Реально классная статья! Два часа рыл инет, пока набрёл на работающий способ обновления XML-файла! Респект и уважуха! =)
    Вообще, информаци по xpath, simplexml даже на офф сайте так мало, а главное кот наплакал работающих примеров

  7. Alex
    Апрель 5th, 2010

    Хорошая статья! Только у меня вопрос. Если стоит в корневом элементе атрибут, для примера:

    , то $pos всегда 0. Как можно исправить и еще как удалять атрибуты и элементы:) help me;)

  8. Александр
    Апрель 20th, 2010

    Подскажите , а будет ли описанная Вами конструкция работать с рускоязычными тегами из 1С. Я хочу просто вытащить их из XML файла с помощью PHP вот так:

    $xml = simplexml_load_file(’import.xml’);
    if ($xml)
    {echo “Функция загрузки файла import.xml работает!”;}
    else
    {echo “Функция загрузки файла import.xml НЕ работает!”;}

    $id = $xml->КоммерческаяИнформация->Каталог->Товары->Товар->Ид;
    echo “Ид товара равен: $id “;

Оставить комментарий


© 2008 - 2010 i-novice.net | Все права защищены.