Перейти к тексту

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

При первом входе в developerWorks для Вас будет создан профиль. Выберите информацию отображаемую в Вашем профиле — скрыть или отобразить поля можно в любой момент.

Вся введенная информация защищена.

  • Закрыть [x]

При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

Вся введенная информация защищена.

  • Закрыть [x]

Обработка файлов конфигурации с использованием LibXML2

Дэвид Дугалл, консультант, независимый писатель
Дэвид Дугалл получил степень магистра вычислительной техники в университете Brigham Young (BYU) в 2001 г. Он работал системным администратором UNIX в течение шести лет в Колледже промышленности и технологии в BYU.

Описание:  В статье описывается использование языка XML (Extensive Markup Language) при создании приложений для ОС UNIX®. Статья ориентирована на программистов, не знакомых с XML, и описывает библиотеки XML, написанные для проекта GNOME. После краткого описания формата XML даны примеры исходного кода, который может быть использован разработчиками для создания собственных приложений, анализирующих конфигурационные файлы в формате XML с использованием библиотеки LibXML2.

Дата:  10.12.2008
Уровень сложности:  средний
Активность:  3552 просмотров
Комментарии:  


Вступление

Благодаря тому, что XML довольно удобен для приложений с высоким уровнем взаимодействия, он постепенно становится стандартным форматом как для хранения данных, так и для хранения настроек приложения в конфигурационных файлах. В этой статье представлено описание программы, конфигурационные файлы которой создаются в формате XML, для демонстрации возможностей использования XML в приложениях. Демонстрационная программа написана на Perl и использует модули Perl, основанные на библиотеке LibXML2 проекта Gnome.

После краткого описания формата XML данная статья демонстрирует файл конфигурации в этом формате. Примеры кода показывают, как анализировать данный файл с использованием библиотеки LibXML2. Несмотря на то что пользователь может изменять параметры конфигурации прямым редактированием XML-файла, разработчику может потребоваться функциональность для модификации XML-файлов прямо в приложении, и в данной статье демонстрируется модификация документа XML на примерах добавления, удаления и изменения параметров; описана выгрузка XML-документа обратно в конфигурационный файл.


Описание формата XML

До начала реального использования LibXML2 необходимо первое знакомство с XML. Формат XML является текстовым форматом для хранения структурированных данных, доступных для любого языка программирования на любой платформе. Формат использует древовидную структуру хранения в тэгах, подобных HTML-тэгам.

Рассмотрим документ, показанный в листинге 1. Это упрощённый файл конфигурации из раздела Конфигурационный файл. Упрощения были сделаны для более наглядной демонстрации базовых концепций XML.


Листинг 1. Простой файл в формате XML

<?xml version="1.0" encoding="UTF-8"?>
<files>
  <owner>root</owner>
  <action>delete</action>
  <age units="days">10</age>
</files>

Первой строкой листинга является декларация XML, которая говорит приложению, которое читает данный файл (XML-анализатору), с какой версией XML он имеет дело. Подавляющее большинство файлов, с которыми придётся иметь дело, написано с использованием версии 1.0, незначительное количество приложений использует версию 1.1. Также в декларации указана кодировка файла; большая часть XML-файлов написаны в UTF-8, но XML позволяет хранить данные в любой кодировке любого языка, даже без использования английского алфавита.

Далее идут элементы документа. Элемент начинается с открывающего тэга (такого как <files>) и заканчивается закрывающим тэгом (таким как </files>), отличающимся от открывающего тэга косой чертой (/).

Главной сущностью в объектной модели документа XML (DOM) являются объекты типа Node (узел). Моделью определены несколько типов Node, это Element (элемент) (такие как files или age в листинге 1), Attributes (атрибут) (units в листинге 1), и просто текст (как root или 10). Элементы могут иметь дочерние узлы. Например, элемент age имеет одного потомка, текстовый элемент 10. Элемент files имеет семь потомков, три из которых очевидны: это owner, action и age. Четверо остальных представляют собой пустые текстовые поля до и после элементов.

Анализаторы XML могут использовать данную иерархическую структуру для навигации по документу и для модификации содержимого документа. LibXML2 является одним из таких анализаторов, демонстрируемый пример приложения использует иерархическую структуру для работы с документом. На данный момент доступно большое количество анализаторов и библиотек для различных окружений. Такие скриптовые языки как Perl или Python имеют расширения, поддерживающие LibXML2, который можно считать лучшим XML-анализатором для UNIX.

Рассмотрим пример файла конфигурации.


Конфигурационный файл

Пример приложения читает набор действий для выполнения над определёнными файлами. Действия и файлы заданы в конфигурационном файле, который расположен где угодно в файловой системе. Подобный этому файл конфигурации может быть использован, например, для конфигурации планировщика cron. Данный XML-файл конфигурации определяет пути к каталогам, содержащим файлы, и действия, которые должны быть произведены на базе определённых конфигурируемых критериев, таких как владелец файла и дата создания файла (см. листинг 2).


Листинг 2. Конфигурационный файл

<?xml version="1.0" encoding="UTF-8"?>
<filesystem>
   <path>
     <dirname>/var</dirname>
     <files>
       <owner>root</owner>
       <action>delete</action>
       <age units="days">10</age>
     </files>
     <files>
       <owner>any</owner>
       <action>delete</action>
       <age units="hours">96</age>
     </files>
   </path>
   <path>
     <dirname>/tmp</dirname>
     <files>
       <owner>any</owner>
       <action>delete</action>
       <age units="hours">24</age>
     </files>
   </path>
</filesystem>

В данном примере корневым элементом является элемент filesystem, который содержит два элемента path, содержащих имя каталога и один или несколько элементов files. Каждый элемент files задаёт действие, которое необходимо выполнить приложению, основываясь на владельце файла и возрасте файла, заданном в единицах, указанных в атрибуте units элемента age. Пробелы очень важны в XML-документе: с точки зрения структуры, каждый пустой элемент формирует отдельный текстовый узел типа Text.

В реальной среде хорошо написанное UNIX-приложение должно иметь возможность не только считывать данные и оперировать в зависимости от прочитанных значений, но и иметь возможность модифицировать данные по желанию пользователя.

Настало время обратить внимание на приложение, использующее данные в формате XML.


Пример приложения

Далее в этой статье приведены и описаны примеры кода для анализа и управления файлами XML. Данные примеры читают и модифицируют конфигурационный файл, но продемонстрированные концепции можно использовать для любых других задач, встречающихся в повседневной жизни UNIX-разработчика. Базируясь на LibXML2, можно использовать данные концепции в любом создаваемом UNIX-приложении.

Статья демонстрирует работу с XML на базе библиотеки LibXML2 для Perl. Значительная часть документации в Интернет описывает программирование в Java™ или Microsoft® Visual Studio, очевидно, неинтересные пользователям и разработчикам UNIX, которым гораздо более полезен Perl. В листинге 3 показаны модули Perl, необходимые для анализа XML-документа.


Листинг 3. Используемые модули

XML::LibXML
XML::LibXML::Common
XML::NamespaceSupport
XML::SAX

Код в последующих разделах статьи – типичный инструментарий, который разделён по назначению на три части: анализ, модификация данных, экспортирование документа.

Во время загрузки и анализа данные в приложении будут скорее всего загружаться в переменные Perl типа списков или хеш-таблиц, однако каждый программист имеет свои предпочтения в выборе способа хранения данных; мы не настаиваем на использовании наших типов переменных. Демонстрируемый код просто печатает данные, демонстрируя корректность работы с данными.

Программа производит модификацию данных путём добавления, редактирования и удаления элементов документа XML. Как правило эта модификация будет результатом каких-либо действий пользователя.

На стадии экспортирования программа записывает документ обратно в файл конфигурации.


Загрузка и анализ данных

Первым этапом в чтении XML является загрузка данных и распознавание их в объект класса Document, после чего появляется возможность навигации по дереву DOM для получения доступа к выбранным узлам. В листинге 4 продемонстрированы эти действия.


Листинг 4. Пример загрузки и анализа файла example.xml

my $parser = XML::LibXML->new();
my $doc = $parser->parse_file("example.xml");
$filesystem = $doc->getDocumentElement();
@nodes=$filesystem->childNodes;
foreach $node (@nodes) {
  if($node->nodeType==ELEMENT_NODE) { # игнорируем текстовые узлы
    # ищем только первое совпадение
	@dirnames = $node->getElementsByTagName("dirname")->item(0);
    foreach $dirname (@dirnames) {
      print "dirname: " . $dirname->textContent . "\n";
      # помещаем его в массив
	  }
    # получаем всех потомков
    @files = $node->getChildrenByTagName("files");
    foreach $file (@files) {
      foreach $values ($file->childNodes) {
        # игнорируем текстовые узлы
        if($values->nodeType!=XML_TEXT_NODE) {
          if($values->nodeName() eq "age") {
            # проверка атрибутов, в противном случае 
            # используем значение по умолчанию 'hours'
            if($values->hasAttributes()) {
              print $values->nodeName() . ": " .  $values->textContent;
              print " " . $values->attributes->item(0)->value();
              print "\n";
            } else {
              print $values->nodeName() . ": " .  $values->textContent;
              print " hours\n";
            }
            # вычисляем расширенное значение по атрибуту units
            # и сохраняем его в хеш-таблице, связав с $dirname.
          } else {
            print $values->nodeName() . ": " .  $values->textContent;
            print "\n";
            # сохраняем его в хеш-таблице, связав с $dirname.
            # может быть несколько элементов для каждого $dirname,
            # так что, возможно, стоит использовать массив в хеш-таблице
          }
        }
      }
    }
  }
}

Сначала в листинге 4 создаётся анализатор и загружается XML из файла в переменную XML::LibXML::Document. Данный объект содержит полное дерево XML и имеет методы, предназначенные для поиска, экспортирования, проверки и создания узлов. Некоторые из этих методов будут обсуждаться в следующих разделах статьи. Для получения корневого элемента дерева необходимо вызвать метод getDocumentElement(), и начав с верхнего узла, можно выполнять навигацию по всему дереву XML.

Главный цикл foreach проходит через каждый дочерний элемент внутри родительского элемента filesystem, исключая текстовые элементы. Метод getElementsByTagName() ищет в элементе потомков на любом уровне вложенности с заданным именем (dirname в данном случае) и возвращает объект типа NodeList. Каждый элемент path содержит только одно имя каталога в элементе dirname, поэтому используется только первое возвращённое данным вызовом значение dirname. Текстовые узлы исключаются из результатов поиска, так как узлы типа Text не поддерживают метод getElementsByTagName(), и обращение к нему вызовет фатальную ошибку Perl.

В одном элементе path возможны несколько элементов files, поэтому код обрабатывает их в цикле, используя метод getChildrenByTagName(), который похож на getElementsByTagName(), но ищет только прямых потомков элемента. Таким образом, можно прочитать элементы files и проанализировать далее на один уровень ниже для получения элементов owner, action и age. После получения этих элементов необходимо вызвать textContent для получения содержимого элемента. Данный метод удобно подменяет операции считывания элемента TEXT и выбора значения этого элемента. Вот как это выглядит:

print $values->nodeName() . ": " 
print $values->firstChild()->nodeValue();

В случае элемента age возможно чтение необязательного атрибута units, это осуществляется использованием функций hasAttributes() и Attributes. При отсутствии данного атрибута программа будет использовать значение по умолчанию, hour.

Далее освещается модификация данных: добавление, удаление и редактирование параметров.


Модификация данных

На данный момент приведённый выше код представляет собой весьма полезный инструментарий разработчика. Пользователь может легко модифицировать поведение программы путём прямого редактирования конфигурационного файла, однако опытному разработчику стоит использовать XML-функции для редактирования документа прямо в программе. Например, в данной программе можно реализовать меню для модификации, добавления или удаления действия над файлом. Приведённый в листинге 5 код реализует модификацию документа.


Листинг 5. Модификация документа XML.

$newnode = $doc->createElement("path");
$newdirnode = $doc->createElement("dirname");
$newdirnode->appendText("/root");
$newfilesnode = $doc->createElement("files");
$newownernode = $doc->createElement("owner");
$newownernode->appendText("any");
$newactionnode = $doc->createElement("action");
$newactionnode->appendText("archive");
$newagenode = $doc->createElement("age");
$newagenode->appendText("30");
$newagenode->setAttribute("units","days");
$newfilesnode->addChild($newownernode);
$newfilesnode->addChild($newactionnode);
$newfilesnode->addChild($newagenode);
$newnode->addChild($newdirnode);
$newnode->addChild($newfilesnode);
$filesystem->addChild($newnode);

Код в листинге 5 создаёт и заполняет все подэлементы элемента path, затем добавляет созданный элемент в корневой элемент filesystem. Каждый элемент создается использованием метода createElement() класса XML::LibXML::Document (элементы создаются объектом класса Document). Данный метод возвращает пустой элемент, который не имеет связи с элементами документа. Затем можно заполнить элемент, используя метод appendText() класса XML::LibXML::Element. Данный метод является удобной альтернативой созданию нового элемента типа TEXT, его наполнения и добавления к элементу. Добавление атрибутов осуществляется вызовом метода setAttribute(), который автоматически создаёт новый узел типа ATTRIBUTE в случае отсутствия у модифицируемого элемента атрибута с таким именем.

После создания и наполнения элементов необходимо вызвать метод addChild() родительского элемента, задав параметром вызова новый дочерний элемент. В приведённом выше коде элемент $newownernode становится потомком элемента $newfilesnode. Все элементы расположены в документе в соответствии с порядком добавления. Если требуется задать другое расположение, то надо использовать методы insertAfter() или insertBefore().

Каждый элемент добавляется к родительскому до того момента, когда в конце концов элемент добавляется к реально существующему элементу в документе. Приведённый выше пример добавляет к корневому элементы filesystem. В случае, когда данный документ создаётся с нуля, необходимо вызывать addChild() к самому объекту Document для добавления корневого элемента, и только затем добавлять к полученному корню дочерние элементы.

Как объяснялось ранее, XML в листинге 2 написан в понятном для пользователя формате. Переносы строк и отступы (табуляция) делают формат более читабельным, каждый из этих символов (пробелы и символы табуляции) считывается анализатором XML как элемент типа TEXT. Пример в листинге 5 не добавляет никаких элементов типа TEXT, таким образом, вывод приложения не будет иметь никаких переносов строк или отступов от начала строки. Если необходим экспорт в текст более читабельного вида, с отступами, переносами и выравниванием, надо добавить элементы типа TEXT, используя либо класс XML::LibXML::Text, либо метод объекта Document createTextNode(). Данный метод возвращает элемент, который можно добавлять в дерево таким же образом, как и обычный элемент с данными.

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

$newnode = $doc->createElement("owner");
$newnode->appendText("toor");
$oldnode->replaceNode($newnode);

Удалить элемент можно несколькими способами. Один – это простое удаление из структуры дерева, например, таким образом:

$file->unbindNode();

После нахождения элемента, который необходимо удалить, можно его удалить описанным выше способом, однако он будет удален из структуры дерева, но не из документа. Вызов этой функции не уничтожает структуру данных, пока не завершится работа программы. Если необходимо переместить элемент в другую часть дерева, то можно вызвать метод addNode() с этим элементом в качестве параметра для добавления в другое место документа. Для удаления элемента с уничтожением хранимых в нем данных необходимо вызывать функции removeChild() или removeChildNodes().


Сохранение XML

В некоторых языках программирования сохранение XML-документа в файл может быть довольно нудной процедурой, однако использование LibXML2 делает сохранение очень простой операцией:

$doc->toFile("example.xml");

Данная операция является самой простой из всех, совершаемых над документом. После завершения редактирования документа, хранящегося в памяти приложения, вызовом данного метода приложение записывает документ обратно в конфигурационный файл. Для сохранения XML в строковую переменную либо в дескриптор открытого файла необходимо использовать функции Perl toString() и, соответственно, toFH(), что даёт большую гибкость при проектировании собственных приложений.


Заключение

Проект Gnome оказал ценную услугу предоставлением библиотек LibXML2 и сопутствующих модулей Perl. Данная статья освещает три важных этапа использования и обработки конфигурационных XML-файлов. Стоит отметить, что этап анализа может показаться самым сложным, так как требует рекурсии для распознавания всего дерева XML. Операции модификации документа XML в памяти несложны, а экспортирование модифицированной конфигурации в LibXML2 – весьма простая задача.

Несмотря на некоторые идеологические разногласия формата XML и парадигмы UNIX, XML предоставляет разработчикам мощные возможности для управления данными в приложениях. Древовидная структура предоставляет более гибкое представление данных, чем простой формат хранения базы данных. При разработке новых приложений или модификации старых использование стандартизованных файлов конфигурации в XML-формате может быть легко произведено с использованием библиотек LibXML2.


Ресурсы

Научиться

  • Config file processing with LibXML2: оригинал статьи (EN).

  • LibXML: Web-cайт с информацией об анализаторе текста XML С и XML-инструментарии от проекта Gnome.(EN)

  • XML tutorial (EN): статья об использовании XML в приложениях.

  • Understanding DOM (EN) (июль 2003, developerWorks): статья с описанием объектной модели документа (DOM), которая позволяет разработчику работать с XML.

  • Раздел XML на сайте developerWorks.

  • XML parsers: обширный список XML-анализаторов.(EN)

  • Команда IBM developerWorks проводит по всему миру сотни бесплатных технических консультаций.(EN)

Получить продукты и технологии

  • Cpan.org: модули для PERL.(EN)

  • XML toolkit: XML-инструментарий для Perl от developerWorks.(EN)

  • IBM trial software: ознакомительные версии программного обеспечения для разработчиков, которые можно загрузить прямо со страницы сообщества developerWorks.(EN)

Обсудить

Об авторе

Дэвид Дугалл получил степень магистра вычислительной техники в университете Brigham Young (BYU) в 2001 г. Он работал системным администратором UNIX в течение шести лет в Колледже промышленности и технологии в BYU.

Помощь по сообщениям о нарушениях

Сообщение о нарушениях

Спасибо. Эта запись была помечена для модератора.


Помощь по сообщениям о нарушениях

Сообщение о нарушениях

Сообщение о нарушении не было отправлено. Попробуйте, пожалуйста, позже.


developerWorks: вход


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


При первом входе в developerWorks для Вас будет создан профиль. Выберите информацию отображаемую в Вашем профиле — скрыть или отобразить поля можно в любой момент.

Выберите ваше отображаемое имя

При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

(Должно содержать от 3 до 31 символа.)


Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Оценить эту статью

Комментарии

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=AIX и UNIX
ArticleID=357597
ArticleTitle=Обработка файлов конфигурации с использованием LibXML2
publish-date=12102008
author1-email=david.dougall@gmail.com
author1-email-cc=

Теги

Help
Используйте форму поиска, чтобы найти любой контент с данным тегом в My developerWorks. Используйте ползунок, чтобы отразить больше или меньше тегов.

КнопкаПопулярные теги отображает самые распространенные теги для данной области контента (например: Java, Linux, WebSphere).

Кнопка Мои теги отображает Ваши теги для данной области контента (например: Java, Linux, WebSphere).

Используйте форму поиска, чтобы найти любой контент с данным тегом в My developerWorks. Кнопка Популярные теги отображает самые распространенные теги для данной области контента (например: Java, Linux, WebSphere). Кнопка Мои теги отображает Ваши теги для данной области контента (например: Java, Linux, WebSphere).