Изменяем содержимое 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-файлами (их создание/изменение)? Если кто знает – прошу в комменты.
Июнь 23rd, 2009
Вообще-то удалить любую ветку можно гораздо проще:
unset($xml->NODE[$id]);
где NODE – нужная к удалению ветка или параметр
Июнь 24th, 2009
В принципе да. Но тут самое интересное, что строчка
unset($xml->book[0]);
работает, а вот такая конструкция нет:
foreach ($xml->book as $k => $book) {
if ($k == 0) {
unset($xml->book[$k]);
break;
}
}
Если посмотреть в цикле на значение $k, то мы увидим название тега book.
Июль 28th, 2009
в вышеуказанном примере unset в foreach не работает, т.к. k – это не число 0, а объект! который при сравнении с числом автоматически преобразуется к числу. а вот при использовании его как индекса массива он уже является строкой. надо его явно преобразовывать к числу: unset($xml->book[(int) $k]);
но вообще то для удаления лучше использовать самописную функцию removeNode
Июль 28th, 2009
еще вариант удаления:
foreach ($xml->book as $k => $book) {
if ($k == 0) { // любое условие
$r = $xml->xpath(’.');
unset($r[0][0]);
break;
}
}
Сентябрь 28th, 2009
Да, но все эти способы не могут удалять атрибуты, а хотелось бы….
Февраль 2nd, 2010
Вау-вау! Реально классная статья! Два часа рыл инет, пока набрёл на работающий способ обновления XML-файла! Респект и уважуха! =)
Вообще, информаци по xpath, simplexml даже на офф сайте так мало, а главное кот наплакал работающих примеров
Апрель 5th, 2010
Хорошая статья! Только у меня вопрос. Если стоит в корневом элементе атрибут, для примера:
, то $pos всегда 0. Как можно исправить и еще как удалять атрибуты и элементы:) help me;)
Апрель 20th, 2010
Подскажите , а будет ли описанная Вами конструкция работать с рускоязычными тегами из 1С. Я хочу просто вытащить их из XML файла с помощью PHP вот так:
$xml = simplexml_load_file(’import.xml’);
if ($xml)
{echo “Функция загрузки файла import.xml работает!”;}
else
{echo “Функция загрузки файла import.xml НЕ работает!”;}
$id = $xml->КоммерческаяИнформация->Каталог->Товары->Товар->Ид;
echo “Ид товара равен: $id “;