Вероотступник Geronimo: Использование интегрированных пакетов: Woodstox от Codehaus

Парсер (parser - программа синтаксического анализа, парсинга) для XML часто является ключевым элементом в обеспечении устойчивости и производительности приложения. К традиционным методам парсинга XML относятся - Document Object Model (DOM) и Simple API для XML (SAX). Сейчас появился инновационный метод, названный Streaming API for XML (StAX), и уже оказался настолько полезным, что его интегрировали в спецификацию платформы Java Platform, Enterprise Edition (Java EE) 5. Apache Geronimo 2.0, и теперь полная реализация Java EE 5 включает StAX-парсер Woodstox от компании Codehaus. В данной статье мы научим вас использовать преимущества парсера StAX, а также расскажем, почему команда Geronimo выбрала Woodstox в качестве своего StAX-парсера.

Майкл Галпин, инженер по программному обеспечению, Vitria Technology

Майкл Галпин (Michael Galpin) имеет учёную степень по математике в Калифорнийском Технологическом институте. Он является Java-разработчиком с конца 90-х гг. и работает инженером по программному обеспечению в Vitria Technology, в Саннивейл, Калифорния.



21.11.2007

Значение XML

XML был создан в 1996 г. (авторы Тим Брэй и Майкл Сперберг-Макквин). Его перспективы сразу получили положительную оценку, но тогда никто не мог и предполагать, насколько важной технологией XML станет в будущем. Java-разработчики используют XML для конфигурирования в качестве хранилища данных, а еще чаще - как формат для обмена данными. Он стал основой для Web-сервисов и SOAP, а следовательно, и для конструктивных шаблонов современной сервис-ориентированной архитектуры (Service-Oriented Architecture - SOA). Но этим возможности XML не исчерпываются. В Ajax (Asynchronous JavaScript + XML) он представлен буквой X и играет ключевую роль в создании более функционально насыщенных, чем когда-либо, интерфейсов, присущих современным Web-приложениям.

Конечно, XML не всесилен и имеет свою слабую сторону. Как правило, XML-документы объемны. Для них имеется общая структура дерева каталогов, но расширяемость приводит к тому, что возможны огромные расхождения между схемами для документов. Эти аспекты представляют трудности для эффективного парсинга XML. Имеются два традиционных метода решения проблем с XML-парсингом: DOM и SAX.

Обработка XML: DOM и SAX

DOM и SAX - два классических подхода к XML-парсингу. Во многом они расходятся. DOM предоставляет для XML-документов простую объектную модель. DOM-парсер превращает XML-документ в несложный объект, который представляет все данные из XML-документа. Но за такое точное представление приходится расплачиваться: DOM-парсинг очень требователен к ресурсам памяти.

Для SAX проблема с памятью отсутствует. SAX-парсеры производят серию событий анализа. Обработчик (handler) может регистрировать обратные вызовы (callbacks) для этих событий и затем применять определенную логику к данным, связанным с этими событиями. Это работает быстро и эффективно, но нуждается в сложной программной модели.

Легче всего рассмотреть различия в использовании DOM и SAX, а следовательно, предпосылки и преимущества StAX, на конкретном примере.


Пример парсинга с использованием Flickr

Пример XML-файла для парсинга найти нетрудно. Сейчас большинство Web-сайтов предлагают какие-либо Web-сервисы на основе XML. Flickr - популярный сайт по обмену фотографиями (владелец - компания Yahoo), оснащенный мощным и гибким API. Посмотрим на какой-нибудь простой код для доступа к "интересным" фотографиям на этом сайте. (См. раздел Загрузка - там дается полный исходный код, использованный в данной статье, и обязательно либо поместите Woodstox в свой путь класса, либо используйте JDK 1.6.) Этот код показан в листинге 1.

Листинг 1. Использование API-интерфейса Flickr
String apiKey = "c4579586f41a90372f762cb65c78be5d";
String urlStr = "http://api.flickr.com/services/rest/?" + 
"method=flickr.interestingness.getList&per_page=20&api_key="+apiKey;
URL request = new URL(urlStr);
InputStream input = request.openStream();

В данном коде используется API Flickr: Representational State Transfer (REST) . (См. раздел Ресурсы, в котором имеется дополнительная информация об API Flickr и о формате REST.) Пример вывода из данного вызова показан в листинге 2.

Листинг 2. XML от Flickr
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<photos page="1" pages="25" per_page="20" total="500">
     <photo id="469774979" owner="35373726@N00" secret="c8a1be2012" server="183" 
farm="1" title="Where will it lead me......?" ispublic="1" isfriend="0" 
isfamily="0" />
     <photo id="470281793" owner="73955226@N00" secret="49612a2794" server="212" 
farm="1" title="Island Beauty" ispublic="1" isfriend="0" isfamily="0" />
     <photo id="469808244" owner="43568064@N00" secret="26b71544a3" server="227" 
farm="1" title="" ispublic="1" isfriend="0" isfamily="0" />
</photos>
</rsp>

Обратите внимание на то, что в листинге 2 показаны только три фото. Вызов API вывел бы все 20 (значение параметра per_page в строке с адресом URL.) Результат вполне объяснимый, поэтому посмотрим, как можно выполнить парсинг этого XML-файла. В данном примере анализируется заглавие каждой фотографии и ее идентификационный номер (ID). Этот ID можно использовать для создания адреса URL для этого фото, поэтому легко придумать Web-приложение (например, mashup), которому будет достаточно только этой информации. Вначале надо применить DOM для извлечения этих данных.


Пример использования DOM

Для использования DOM производится парсинг документа в объект документа. Это находящаяся в оперативной памяти (in-memory) структура дерева, представляющая проанализированный XML-документ. Потом надо найти в папках этого DOM-дерева название и ID для каждой фотографии. Объедините их на одной карте. В листинге 3 приведен код для такой операции.

Листинг 3. Парсинг с помощью DOM
Map<String,String> map = new HashMap<String,String>();
DocumentBuilder builder =    DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document dom = builder.parse(input);
Element root = dom.getDocumentElement();
NodeList childNodes = root.getChildNodes();
Node photosNode = null;
for (int i=0;i<childNodes.getLength();i++){
     Node node = childNodes.item(i);
     if (node.getNodeName().equalsIgnoreCase("photos")){
          photosNode = node;
          break;
     }
}
childNodes = photosNode.getChildNodes();
for (int i=0;i<childNodes.getLength();i++){
     Node node = childNodes.item(i);
     if (node.getNodeName().equalsIgnoreCase("photo")){
          String title = node.getAttributes().getNamedItem("title").getTextContent();
          String id = node.getAttributes().getNamedItem("id").getTextContent();
          map.put(id,title);
     }
}

DOM популярен, поскольку его очень легко использовать. Просто введите данные в парсер, и вы получите объект document. Теперь можно пройтись по дочерним узлам и найти узел с фотографией. Каждый узел с фото (photo node) является дочерним узлом узла с фотографиями, поэтому надо смотреть в каждом узле с фото. Затем мы получаем доступ к атрибутам title и id каждого узла с фото и сохраняем его в свою карту.

Однако при работе с DOM есть и некоторые недостатки. Приходится сохранять большое количество ненужной информации, например, о владельцах фотографий. Кроме того, всю информацию приходится считывать дважды: сначала в объект документа, а во второй раз - при просмотре объекта документа. Традиционно этой излишней работы избегали при помощи SAX.


Пример использования SAX

SAX-парсер не возвращает такого же понятного объекта document, какой выдавал DOM-парсер. Вместо этого он выдает серию событий по ходу анализа XML-документа. Для этих событий придется создать обработчик, либо реализуя интерфейс, либо расширяя класс DefaultHandler и игнорируя его методы, где это необходимо. В листинге 4 показан SAX-парсинг для XML-документа Flickr.

Листинг 4. Парсинг с помощью SAX
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
DefaultHandler handler = new DefaultHandler(){
     @Override
     public void startElement(String uri, String localName, 
     String qName, Attributes attributes) throws SAXException {
          if (qName.equalsIgnoreCase("photo")){
               String title = attributes.getValue("title");
               String id = attributes.getValue("id");
               // карта статична, поэтому можно получить к ней доступ здесь
               map.put(id, title);
          }
     }
};
parser.parse(input, handler);

Очевидно, что код, показанный в листинге 4, немного труднее для понимания, чем тот DOM-код, который вы видели в листинге 3. Для обработки SAX-событий нам потребовался ContentHandler, поэтому мы и создали DefaultHandler и игнорировали его метод startElement обратного вызова (callback). Вы удостоверились, что это - элемент фото, а затем получили доступ к его атрибутам title и id.

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


Альтернатива StAX

Сложность в SAX возникает из-за конструктивного шаблона Observer, который она реализует. Это пассивная модель (push model), то есть парсер передает модели наблюдателям, которые в дальнейшем действуют в зависимости от происшедших событий. Модель StAX похожа на SAX. Она направляет данные и события из XML-документа, что делает её быстрой и эффективной, как SAX. Серьезная разница заключается в том, что здесь используется активная модель (pull model). При этом код приложения выталкивает события из парсера.

Разница не выглядит критической, но при этом становится возможной значительно более простая программная модель. Посмотрите в листинге 5, как действует StAX.

Листинг 5. Парсинг при помощи StAX
Map<String,String> map = new HashMap<String,String>();
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
QName qId = new QName("id");
QName qTitle = new QName("title");
QName qPhoto = new QName("photo");
XMLEventReader  reader = inputFactory.createXMLEventReader(input);
while (reader.hasNext()){
     XMLEvent event = reader.nextEvent();
     if (event.isStartElement()){
          StartElement element = event.asStartElement();
          if (element.getName().equals(qPhoto)){
               String id = element.getAttributeByName(qId).getValue();
               String title = element.getAttributeByName(qTitle).getValue();
               map.put(id,title);
          }
     }
}
reader.close();

Во-первых, не пришлось расширять не один из классов, потому что не потребовалась регистрация событий. При помощи SAX можно контролировать поток событий, потому что они выталкиваются из парсера. Можно использовать знакомый итерационный синтаксис для поиска внутри документа нужных вам данных. И опять-таки сохраняются только нужные вам данные, а разбор XML-документа производится только один раз. Эффективность такая же, как при использовании SAX, но код стал гораздо понятнее.


Woodstox как поставщик StAX Geronimo

Теперь вы убедились в преимуществах StAX-парсинга. Все признают, что он улучшил XML-технологии. Неудивительно, что этот парсинг стал частью спецификации Java EE 5 (его даже включают в платформу Java Platform, Standard Edition [Java SE] 6.) В качестве компонента Java EE 5 он подлежит реализации в Geronimo 2.0.

К счастью для команды разработчиков Geronimo, существовало несколько реализаций StAX с открытым кодом, поэтому у них была возможность выбора. В качестве StAX-парсера они остановились на Woodstox и включили его в Geronimo. Woodstox считается одной из лучших реализаций StAX по производительности (см. Ресурсы - сравнение разных StAX-парсеров.) Кроме того, Woodstox лицензируется как под Lesser General Public License (LGPL), так и под Apache 2.0. Поэтому включать Woodstox и его исходный код в пакеты Geronimo можно безо всяких ограничений.

Настройка производительности вашего приложения: как добиться максимума от Woodstox

Производительность - одно из преимуществ, которые Woodstox принес в Geronimo. Как и с другими высокопроизводительными технологиями, очень важно понять, как добиться максимальной производительности от Woodstox. В коде листинга 5 используется API-интерфейс высокого уровня XMLEventReader, который включен в спецификацию StAX. Можно использовать API-интерфейс более низкого уровня, что увеличит производительность, - XMLStreamReader. В листинге 6 показано, как StAX-парсер использует этот интерфейс.

Листинг 6. StAX-парсинг при помощи XMLStreamReader
Map<String,String> map = new HashMap<String,String>();
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
QName qId = new QName("id");
QName qTitle = new QName("title");
QName qPhoto = new QName("photo");
XMLStreamReader reader = inputFactory.createXMLStreamReader(input);
while (reader.hasNext()){
    int event = reader.next();
    if (event == START_ELEMENT){ // statically included constant from XMLStreamConstants
         if (reader.getName().equals(qPhoto)){
               String id = reader.getAttributeValue(null, qId.getLocalPart());
               String title = reader.getAttributeValue(null, qTitle.getLocalPart());
               map.put(id,title);
          }
     } 
}
reader.close();

Код в листинге 6 аналогичен коду в листинге 5; хотя он явно более низкого уровня, можно добиться значительного повышения производительности.

Заключение

Вы узнали о преимуществах использования StAX-парсера для анализа XML-документов. StAX предлагает компромиссное решение для SAX и DOM. Вы можете прямо сейчас воспользоваться StAX, потому что он входит в пакет Geronimo 2.0. Вы получите не только интуитивно понятные API StAX, но и дополнительные преимущества от высокопроизводительной реализации StAX в Woodstox.


Загрузка

ОписаниеИмяРазмер
Sample article coderenegade.woodstox.source.zip4KB

Ресурсы

Научиться

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

  • Загрузите последнюю версию Apache Geronimo.(EN)
  • Загрузите бесплатную копию IBM WebSphere® Application Server Community Edition - небольшого сервера приложений J2EE на основе технологии открытого кода Apache Geronimo, которая поможет вам ускорить разработку и развертывание.(EN)
  • Реализуйте инновации в вашем следующем проекте разработки с открытым кодом с помощью ознакомительного ПО IBM, которое можно загрузить с сайта или заказать на DVD.(EN)

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


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


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

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

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

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

Выберите имя, которое будет отображаться на экране



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

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

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

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

 


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


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source, Технология Java, XML, WebSphere
ArticleID=270607
ArticleTitle=Вероотступник Geronimo: Использование интегрированных пакетов: Woodstox от Codehaus
publish-date=11212007