Изменяем содержимое 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-файлами (их создание/изменение)? Если кто знает – прошу в комменты.



Теги: ,


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



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

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

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

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

  2. novice

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

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

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

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

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

  3. Борис

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

    но вообще то для удаления лучше использовать самописную функцию removeNode [ссылка]

  4. Борис

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

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

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

  6. Кирюха

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

  7. Alex

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

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

  8. Александр

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

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

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

  9. German

    Отличная статья) Спасибо

  10. Midel

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

    в цикл засовываю - все-равно ищет один и тот же элемент. Подскажите пожалуйста, как найти все элементы с одинаковыми значениями?

  11. Obrun

    вот как то так:

    $i = 0;
    foreach ($xml->book as $k){
    if(xml->book[$i]->title == “нужный_титл”) {
    unset($xml->book[$i]);
    break;}
    $i++;}

    проще не придумаешь…

  12. Спасибо, помогло.
    Использовал конструкцию:
    if (file_exists(‘xxx.xml’)) {
    $xml = implexml_load_file(‘xxx.xml’); }
    $i=0;
    foreach ($xml->user as $user) {
    if {…
    $xml->user[$i]->mailsend = ‘1’;}

    $i++;
    }

  13. TRTH-HRTZ

    а можно подробнее об этой строчке “$pos = $xpath->evaluate(‘count(//book[pages=848]/preceding-sibling::*”?

  14. Армен

    Спасибо тебе огромное!!! не представляешь как помог!
    респект и увжуха таким программистам)))


© Copyright. . I-Novice. All Rights Reserved. Terms | Site Map