25 августа 2019 года    
Воскресенье | 19:43    
Главная
 Новости
Базы данных
Безопасность PC
Всё о компьютерах
Графика и дизайн
Интернет-технологии
Мобильные устройства
Операционные системы
Программирование
Программы
Связь
Сети
 Документация
Статьи
Самоучители
 Общение
Форум







Разделы / Интернет-технологии / XML

XOM: что это и зачем?

XOM: что это и зачем?

XOM (XML Object Model, объектная модель XML) - это новый простой API для XML, разработанный Эллиотом Расти Гарольдом, и реализованный на Java. XOM является попыткой взять удачные идеи, заложенные в других Java XML API (SAX, DOM, и JDOM) и выйти за рамки их ограничений. В результате мы имеем высокоуровневый API в открытых кодах, который легок для понимания и использования при условии, конечно, что вы уже хорошо знакомы с Java и XML.

В отличие от SAX, XOM написан классами, а не интерфейсами, что делает его более простым в использовании. Ведь при использовании SAX, приходится сначала реализовывать интерфейсы, и только после этого вы можете с ним работать. Это неудобство отчасти нивелируется посредством вспомогательных классов вроде DefaultHandler, но в целом, подход, основанный на интерфейсах, делает программирование в SAX достаточно запутанным и сложным, хотя и позволяет создавать гибкие SAX-унифицированные приложения. При использовании же классов XOM вы получаете некую гибкость за счет методов "проверки" (check methods), которые могут быть переопределены в подклассах.

XOM не является самодостаточным. Он использует внешний SAX-парсер, в качестве которого, например, может выступать последняя версия Xerces. Этот парсер обеспечивает проверку на корректность и действительность XML-документов. XOM реализуя простой интерфейс к парсеру, в сущности просто скрывает код реализации без большой потери производительности.

Аргументы в пользу XOM у меня такие же, как и для RELAX NG: для того, чтобы начать с ним работать, не надо практически ничего, кроме приемлемого знания идеологии Java. Так же, как и RELAX NG, чем больше я использую XOM, тем больше он мне нравится. Он хорошо продуман, а его авторы не пытаются заставить его делать все и также не ставят целью удовлетворить всех. Более детальную информацию о взаимосвязи XOM с другими XML API вы может почерпнуть из презентации, подготовленной Эллиотом к конференции New York XML SIG, прошедшей 17 сентября 2002 года.

С другой стороны, если вы не в восторге от простоты XOM, вы спокойно можете вернуться к вашим старым любимым API или их комбинациям, используя то, что вам нравится в каждом из них. Но с позиций простоты, открытости и готовности к немедленному использованию, ключевых при широком выборе из многообразия программных продуктов, XOM доставляет, на мой взгляд, минимум проблем.

Имейте в виду, что XOM находится все еще в стадии разработки. Эта статья всего лишь небольшой экскурс по части интерфейса, но она содержит достаточно примеров, чтобы вы получили представление об этом подходе.

Примеры программ и документов, используемые в этой статье, доступны в виде Zip-архива, который мы можете найти здесь. Также доступна документация для nu.xom.* в формате Javadoc.

Для того, чтобы запустить эти примеры, вам потребуется:

  • Java версии не ниже 1.2. Я проверял примеры на Java версии 1.4, операционная система Windows 2000.
  • Xerces версии не ниже 2.1. При подготовке примеров я использовал Xerces 2.2.
  • Последняя версия JAR-файла XOM. На момент написания статьи это был xom-1.0d8.jar.

Разбор документа при помощи XOM

Создайте рабочую директорию и распакуйте в нее архив с примерами. Также в нее скопируйте JAR-файлы Xerces и XOM. Программа Wf.java проверяет документ на корректность в соответствии с XML 1.0:


import java.io.IOException;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.ParseException;

public class Wf {

    public static void main(String[] args)
        throws IOException, ParseException {

        Builder builder = new Builder();
        Document doc = builder.build(args[0]);
        System.out.println(doc.toXML());

    }

}

Для того, чтобы скомпилировать эту программу, наберите в командной строке следующую команду:

javac -classpath xom.jar Wf.java

Если вы работаете на UNIX-платформе, то для разделения JAR-файлов используйте двоеточия. При выполнении этой команды пути доступа к JAR-файлам Xerces и XOM добавляются к переменной среды CLASSPATH, после чего выполняется компиляция программы (для простоты я переименовал последнюю версию XOM JAR из "xom-1.0d8.jar" в "xom.jar"). После того как программа скомпилирована, ее можно запустить, набрав в командной строке команду:

java -cp .;xercesImpl.jar;xom.jar Wf file:///wrk/inst.xml

Я указал в качестве аргумента к Wf абсолютный путь к файлу вместо простого указания его имени для того, чтобы гарантировать доступ к нему вне зависимости от платформы, на которой вы ее запускаете. В случае успешного выполнения программы, и если inst.xml является корректным (а так и должно быть:), на консоль будет выведен входной документ с добавленной к нему XML-декларацией:

<?xml version="1.0"?>
<instant>
 <date month="December" day="1" year="2002"/>
 <time hour="10" minute="17" second="33" zone="PST"/>
</instant>

В Wf.java используются три импортированных класса XOM: nu.xom.Builder, nu.xom.Document и nu.xom.ParseException. Builder создает объект документа, читая XML-документ. Он может читать его из файла (как в примере), по URL или из входного потока. В действительности считывает документ метод build() класса Builder. Класс Document представляет документ, включая его корневой элемент и пролог. Вывод XML осуществляется методом toXML() класса Document с помощью System.out.println(). Полный документ отображается с использованием этого механизма, включая XML-декларацию, как часть вывода.

Классы IOException и ParseException отвечают за обработку исключений, а потому необходимы. В Wf.java они объявлены для простоты с помощью ключевого слова throws. В Wf2.java вместо этого используется конструкция try/catch.

Валидация документов при помощи XOM

Внеся совсем немного изменений, мы можем добавить в нашу программу проверку документов на действительность. В следующем примере (Val.java) необходимые изменения для удобства выделены полужирным шрифтом.

import java.io.IOException;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.ParseException;
import nu.xom.ValidityException;

public class Val {

    public static void main(String[] args)
        throws IOException, ParseException, ValidityException {

        Builder builder = new Builder(true);
        Document doc = builder.build(args[0]);
        System.out.println(doc.toXML());

    }

}

Добавляя true в качестве аргумента в конструктор класса Builder, мы указываем, что создаваемый объект документа должен валидироваться. Чтобы обеспечить возможность валидации также небходимо импортировать класс nu.xom.ValidityException и объявить его в main() или в конструкции try/catch (см. Val2.java).

Скомпилируйте эту программу и запустите ее из командной строки для файла instant.xml:

java -cp .;xercesImpl.jar;xom.jar Val file:///wrk/instant.xml

Документ валидируется относительно следующего DTD, указанного в декларации типа документа (instant.dtd):

<!ELEMENT instant (date, time)>
<!ELEMENT date EMPTY>
<!ATTLIST date month NMTOKEN #REQUIRED
               day NMTOKEN #REQUIRED
               year NMTOKEN #REQUIRED>
<!ELEMENT time EMPTY>
<!ATTLIST time hour NMTOKEN #REQUIRED
               minute NMTOKEN #REQUIRED
               second NMTOKEN #REQUIRED
               zone NMTOKEN #REQUIRED>

Если валидация прошла успешно, то на консоль будет выведен текст проверяемого документа:

<?xml version="1.0"?>
<!DOCTYPE instant SYSTEM "instant.dtd">
<instant>
 <date month="December" day="1" year="2002"/>
 <time hour="10" minute="17" second="33" zone="PST"/>
</instant>

Добавление элементов и атрибутов

Предположим, что мы построили в виде объекта или загрузили копию inst.xml и теперь хотим добавить элемент с атрибутом к нему. Программа AddUtc.java именно это и делает (изменения относительно первого примера выделены полужирным шрифтом):

import java.io.IOException;
import nu.xom.Attribute;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.ParseException;

public class AddUtc {

    public static void main(String[] args)
        throws IOException, ParseException {

        Builder builder = new Builder();
        Document doc = builder.build("inst.xml");
        Element root = doc.getRootElement();
        Element utc = new Element("utc");
        Attribute att = new Attribute("offset", "-08:00");
        utc.addAttribute(att);
        root.insertChild(0, "\n ");
        root.insertChild(1, utc);
        root.removeChild(4);
        root.removeChild(4);
        System.out.println(doc.toXML());

    }

}

Здесь мы импортируем классы Element и Attribute из пакета nu.xom. Вместо того, чтобы указать файл, который следует парсировать, с помощью аргумента, передаваемого из командной строки, мы жестко задали inst.xml внутри программы. Для определения корневого элемента в inst.xml используется метод getRootElement() класса Document.

Элемент utc создается вместе с атрибутом offset, для создания которого используется класс Attribute. Метод addAttribute() класса Element добавляет атрибут к элементу utc. Первым вызовом insertChild() мы вставляем текстовый узел на позицию 0, непосредствено после корневого элемента instant. Следующий insertChild() помещает элемент utc на позицию 1.

Также эта программа удаляет элемент time (а также предшествующие пробелы), вызывая метод removeChild() дважды с одним и тем же аргументом. (В качестве аргумента указывается позиция узла.) После того, как XOM удаляет первый указанный узел (два подряд идущих символа пробела), следующим за ним узел (элемент time) поднимается по дереву на освободившуюся позицию.

В итоге результат будет примерно следующим (utc.xml):

<?xml version="1.0"?>
<instant>
 <utc offset="-08:00" />
 <date month="December" day="1" year="2002" />
 

Сериализация вывода

Плюс ко всему, для перекодирования вывода, форматирования, выгрузки в файл можно воспользоваться классом Serializer. Следующий пример (Time.java) демонстрирует, каким образом это можно сделать.

import java.io.FileOutputStream;
import java.io.IOException;
import nu.xom.Attribute;
import nu.xom.Builder;
import nu.xom.Element;
import nu.xom.Document;
import nu.xom.Serializer;
import nu.xom.ParseException;

public class Time {

    public static void main(String[] args)
        throws IOException, ParseException {

        Builder builder = new Builder();
        Document doc = builder.build("inst.xml");
        Element root = doc.getRootElement();
        Element utc = new Element("utc");
        Attribute att = new Attribute("offset", "-08:00");
        utc.addAttribute(att);
        root.insertChild(0, "\n ");
        root.insertChild(1, utc);
        root.removeChild(4);
        root.removeChild(4);

        Element time = new Element("time");    
        Element hr = new Element("hour");
        time.appendChild(hr);
        hr.appendChild("10");
        Element min = new Element("minute");
        time.appendChild(min);
        min.appendChild("17");
        Element sec = new Element("second");
        time.appendChild(sec);
        sec.appendChild("33");
        Element zone = new Element("zone");
        time.appendChild(zone);
        zone.appendChild("PST");
        root.appendChild(time);

        FileOutputStream out = new FileOutputStream("inst-new.xml");
        Serializer ser = new Serializer(out, "ISO-8859-1");
        ser.setIndent(1);
        ser.write(doc);

    }

}

В этой программе создается пять элементов, которые добавляются после последнего дочернего элемента instant, то есть после элемента date (старый элемент time предварительно удаляется). Дополнительно импортируется класс FileOutputStream и создается выходной файл (inst-new.xml). Конструктор класса Serializer определяет выходной поток и кодировку (ISO-8859-1). Serializer также поддерживает кодировки: UTF-8, UTF-16, ISO-10646-USC-2 и ISO-8859-2 (прим. переводчика: поддерживается и ISO-8859-5). Метод setIndent() форматирует вывод с помощью добавления символов перевода строки и пробелов. Метод write() выгружает документ в файл inst-new.xml:


<?xml version="1.0" encoding="ISO-8859-1"?>
<instant> 
 <utc offset="-08:00"/> 
 <date month="December" day="1" year="2002"/> 
 <time>
  <hour>10</hour>
  <minute>17</minute>
  <second>33</second>
  <zone>PST</zone>
 </time>
</instant>

Без применения класса Serializer результат был бы без отступов и выглядел бы примерно следующим образом (time2.xml) (см. Time2.java):

<?xml version="1.0"?>
<instant>
 <utc offset="-08:00" />
 <date month="December" day="1" year="2002" />
<time><hour>10</hour><minute>17</minute><second>33</second>
<zone>PST</zone></time></instant>

Вы также можете новый XML-документ выгрузить вместо файла в стандартный вывод (см. констурктор Serializer в Time3.java).

Еще одна программа

И еще один, на этот раз последний пример (Final.java). В нем мы добавляем в XML-документ некоторые общие структуры:

import java.io.FileOutputStream;
import java.io.IOException;
import nu.xom.Attribute;
import nu.xom.Builder;
import nu.xom.Comment;
import nu.xom.DocType;
import nu.xom.Element;
import nu.xom.Document;
import nu.xom.Serializer;
import nu.xom.Text;
import nu.xom.ProcessingInstruction;
import nu.xom.ParseException;

public class Final {

    public static void main(String[] args)
        throws IOException, ParseException {

        Builder builder = new Builder();
        Document doc = builder.build("inst.xml");
        Element root = doc.getRootElement();

        DocType dtd = new DocType("instant", "final.dtd");
        ProcessingInstruction pi =
            new ProcessingInstruction("xml-stylesheet",
                "href=\"final.xsl\" type=\"text/xsl\"");
        doc.insertChild(0, dtd);
        doc.insertChild(1, pi);

        Element utc = new Element("utc", "http://www.wyeast.net/utc");
        Comment gmt = new Comment(" Greenwich Mean Time ");
        Attribute att = new Attribute("offset", "-08:00");
        utc.addAttribute(att);
        root.insertChild(0, "\n ");
        root.insertChild(1, gmt);
        root.insertChild(2, "\n ");
        root.insertChild(3, utc);
        root.removeChild(6);
        root.removeChild(6);

        Element time = new Element("time");    
        Element hr = new Element("hour");
        time.appendChild(hr);
        Text h = new Text("11");
        h.setData("10");
        hr.appendChild(h);
        Element min = new Element("minute");
        time.appendChild(min);
        min.appendChild("17");
        Element sec = new Element("second");
        time.appendChild(sec);
        sec.appendChild("33");
        Element zone = new Element("zone", "urn:wyeast-net:utc");
        zone.setNamespaceURI("http://www.wyeast.net/utc");
        time.appendChild(zone);
        zone.appendChild("PST");
        root.appendChild(time);

        FileOutputStream out = new FileOutputStream("final.xml");
        Serializer ser = new Serializer(out, "UTF-8");
        ser.setIndent(3);
        ser.write(doc);

    } 
}

При выполнении этой программы создаются объявление типа документа и инструкция по обработке, которые затем добавляются в пролог final.xml. Для элемента utc определяется пространство имен. А непосредственно над ним вставляется комменнтарий. Текстовый дочерний узел или, другими словами, содержание элемента hour задается с помощью класса Text; затем оно переопределяется вызовом метода setData() класса Text. В конструкторе Element для zone устанавливается новое пространство имен, но затем заменяется с помощью метода setNamespaceURI().

Вот, что должно получится в результате:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE instant SYSTEM "final.dtd">
<?xml-stylesheet href="final.xsl" type="text/xsl"?>
<instant>
   <!-- Greenwich Mean Time -->
   <utc xmlns="http://www.wyeast.net/utc" offset="-08:00"/>
   <date month="December" day="1" year="2002"/> 
   <time>
      <hour>10</hour>
      <minute>17</minute>
      <second>33</second>
      <zone xmlns="http://www.wyeast.net/utc">PST</zone>
   </time>
</instant>

В завершение

ХОМ достаточно гибок, чтобы пользователь имел возможность написать свои собственные методы в подклассах. В него входят также и другие пакеты, о которых я не рассказал в этой статье: класс nu.com.canonical сериализует вывод в канонический XML; nu.xom.xslt позволяет использовать XSLT-преобразования с помощью поддерживающих TrAX процессоров, таких как Saxon; а nu.xom.xinclude является реализацией XML Inclusions.

Я нашел XOM простым и удобным в использовании. Он дает мне большую функциональность и в тоже время избавляет от ненужного беспокойства по пустякам. Если у вас есть какие-нибудь соображения относительно разработки XOM, вы можете их высказать и обсудить внутри листа рассылки XOM-interest.

XOM: что это и зачем?
Лента новостей


2006 (c) Copyright Hardline.ru