Изменяем содержимое 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-файлами (их создание/изменение)? Если кто знает – прошу в комменты.
Вообще-то удалить любую ветку можно гораздо проще:
unset($xml->NODE[$id]);
где NODE - нужная к удалению ветка или параметр
В принципе да. Но тут самое интересное, что строчка
unset($xml->book[0]);
работает, а вот такая конструкция нет:
foreach ($xml->book as $k => $book) {
if ($k == 0) {
unset($xml->book[$k]);
break;
}
}
Если посмотреть в цикле на значение $k, то мы увидим название тега book.
в вышеуказанном примере unset в foreach не работает, т.к. k - это не число 0, а объект! который при сравнении с числом автоматически преобразуется к числу. а вот при использовании его как индекса массива он уже является строкой. надо его явно преобразовывать к числу: unset($xml->book[(int) $k]);
но вообще то для удаления лучше использовать самописную функцию removeNode [ссылка]
еще вариант удаления:
foreach ($xml->book as $k => $book) {
if ($k == 0) { // любое условие
$r = $xml->xpath(‘.’);
unset($r[0][0]);
break;
}
}
Да, но все эти способы не могут удалять атрибуты, а хотелось бы….
Вау-вау! Реально классная статья! Два часа рыл инет, пока набрёл на работающий способ обновления XML-файла! Респект и уважуха! =)
Вообще, информаци по xpath, simplexml даже на офф сайте так мало, а главное кот наплакал работающих примеров
Хорошая статья! Только у меня вопрос. Если стоит в корневом элементе атрибут, для примера:
, то $pos всегда 0. Как можно исправить и еще как удалять атрибуты и элементы:) help me;)
Подскажите , а будет ли описанная Вами конструкция работать с рускоязычными тегами из 1С. Я хочу просто вытащить их из XML файла с помощью PHP вот так:
$xml = simplexml_load_file(‘import.xml’);
if ($xml)
{echo “Функция загрузки файла import.xml работает!”;}
else
{echo “Функция загрузки файла import.xml НЕ работает!”;}
$id = $xml->КоммерческаяИнформация->Каталог->Товары->Товар->Ид;
echo “Ид товара равен: $id “;
Отличная статья) Спасибо
И еще одно замечание: данный пример показывает, как найти и заменить одно значение на другое, не затрагивая остальные такие же значения, т.е. заменяет первое встречное. Чтобы заменить все значения, нужно засунуть все это в цикл.
в цикл засовываю - все-равно ищет один и тот же элемент. Подскажите пожалуйста, как найти все элементы с одинаковыми значениями?
вот как то так:
$i = 0;
foreach ($xml->book as $k){
if(xml->book[$i]->title == “нужный_титл”) {
unset($xml->book[$i]);
break;}
$i++;}
проще не придумаешь…
Спасибо, помогло.
Использовал конструкцию:
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++;
}
а можно подробнее об этой строчке “$pos = $xpath->evaluate(‘count(//book[pages=848]/preceding-sibling::*”?
Спасибо тебе огромное!!! не представляешь как помог!
респект и увжуха таким программистам)))