| Каталог | Индекс раздела |
©IBM deweloperWork
© А.С.Деревянко (перевод)
В этом продвинутом учебнике рассматриваются более сложные темы манипулирования XML-документами при помощи технологии Java. Автор, Doug Tidwell, показывает вам, как выполнять такие задачи, как генерация структур данных XML, проверка правильности XML-документов, работа с пространствами имен и интерфейс XML-парсеров с не-XML источниками данных. Как вы и ожидаете, все примеры базируются на открытых стандартах.
В первом учебнике я показал вам основы разбора XML в языке Java. Я рассмотрел главные API (DOM, SAX и JDOM) и провел вас через несколько примеров, которые демонстрировали основные задачи, общие для большинства XML-приложений. Во втором учебнике серии я рассмотрел свойства парсера, пространства имен и проверку правильности XML. Этот последний учебник рассмотрит более трудные вещи, которые не рассматривались раньше, такие как:
Как и предыдущих учебниках, я рассматриваю такие API:
Хотя многие из программных примеров, которые я здесь обсуждаю, используют JAXP (Java API for XML parsing), я не буду специально обсуждать JAXP в данном учебнике.
Большинство примеров здесь будут работать с сонетом Шекспира, который появился в предыдущих учебниках. Структура сонета такая:
Я использую этот документ-пример через весь данный учебник. Полный набор файлов-примеров перечислен ниже:
Как альтернатива, вы можете выгрузить x-java3_codefiles.zip и посмотреть эти файлы в текстовом редакторе.
В дополнение к сонету вы также изучите, как разбирать файлы со значениями, разделенными запятыми, и текстовые строки, включая несколько подходов к конвертированию этой информации в XML или структуры данных XML.
Вам следует установить несколько вещей на вашей машине прежде, чем вы сможете выполнять примеры. (Предполагается, что вы знаете, как компилировать и запускать Java-программы, и что вы знаете, как установить переменную Иногда вы можете захотеть разобрать строку XML. Обычно парсер работает с
XML-документом, сохраненном в файле. Если другой компонент присылает вам строку, содержащую XML-документ, вы не захотите записывать эту строку в файл, а затем снова читать ее и разбирать. Вместо этого вы захотите вызвать парсер для самой этой строки.
Трюк состоит в конвертировании String языка Java в Здесь вы создаете Единственное изменение состоит в том, что метод принимает на входе В ранних DOM-приложениях вы получали дерево DOM от парсера после того, как он разобрал XML-файл. Иногда вы можете захотеть создать дерево DOM без исходного файла XML. Например, вам может понадобиться конвертировать результаты запроса SQL в дерево DOM, а затем использовать инструменты XML по дереву DOM.
Приложение Как и следовало ожидать, вам нужно начать с запроса к фабрике объектов создать Начните с создания нового Теперь, когда вы создали Document, вам нужно добавлять в него. Кодовый пример ниже начинается с получения В этом листинге вы устанавливаете атрибут type корневого элемента ( вы должны создать элемент Чтобы выполнить приложение, просто введите Вы не должны включать имя XML-файла, поскольку код строит дерево DOM с нуля (это, фактически, и есть целью примера). Как видно из вывода, у вас нет никаких узлов пропусков в вашем дереве DOM, потому что вы и не собирались позаботиться о том, чтобы включить их в дерево.
Позже в разделе Генерация событий SAX из разделенных запятыми значений я покажу, как генерировать события SAX из различных источников. Пока что вы можете посмотреть полный исходный листинг Построение Заметьте, насколько этот код проще. Например, при добавлении текста к узлу в DOM, вы должны создать текстовый узел (используя узел документа как фабрику элементов), затем сделать текстовый узел потомком элемента, затем сделать этот
элемент потомком другого элемента и т.д. В JDOM вы можете создать
После того, как вы установили содержимое вашего элемента Как и в JDOM-приложениях из предыдущих учебников, вы используете Полный исходный код см. в JdomBuilder.java.
Последний пример генерации в SAX иллюстрирует, как сгенерировать события SAX из не-XML источника данных. Эти приемы исключительно полезны. Любой код, который обрабатывает данные, может генерировать события SAX из этих данных, давая возможность SAX-парсеру трактовать данные как источник данных XML. В следующем разделе, Преобразование из одного API в другой, содержится пример, названный В данном примере используемый источник данных является файлом со значениями, разделенными запятыми, называемый также CSV-файлом. (Хорошим примером будет также код, который обращается к базе данных через драйвер JDBC, но соединение с базой данных более сложно.) Для разбора файла вы будете использовать объект Java Вот несколько строк из файла-примера, Этот листинг был сгенерирован из запроса SQL, в каждой строке содержится семь полей данных. Используйте следующие имена элементов XML для окружения этих данных:
Для начала установите Этим определяются свойства По мере разбора составляющих в CSV-файле мы проходим через следующие шаги:
Я покажу вам этот код в деталях далее.
Прежде всего, генерируйте событие (Заметьте, что для добавления перевода строки в вывод генерируется событие Тут вы должны установить вложенные циклы Заметьте, что вы используете переменную Прежде, чем я закончу, - несколько слов о работе класса Класс Java В конце вложенного цикла, наращивайте счетчик ( Используя этот прием, вы теперь конвертировали поток данных, разделенных запятыми в серию событий SAX, которые представляют данные так, как если бы они были в XML-документе. Вы можете использовать этот подход для любого вида структурированных или неструктурированных данных.
Полный исходный код вы можете увидеть в CsvToSax.java. Файл с данными примера - test.csv.
Обычно XML-приложение использует SAX, однако, вы можете захотеть использовать оба интерфейса вместе. Предположим, например, вы имеете систему отчетов, которая генерирует счета для 10,000 пользователей. Эти счета создаются как один XML-файл, содержащий элементов 10,000 Для данного примера вы можете использовать гибридный подход. Для разбора XML-файла примените SAX-парсер, используйте все события SAX для данного Для преобразования событий SAX в дерево DOM, рассмотрим наиболее общие события SAX:
Далее я покажу вам обработчики событий в Я упоминал выше, что события SAX не сохраняют состояния. Данное событие По мере получения событий от SAX-парсера вы можете преобразовывать каждое событие в соответствующий тип Вы в вашем коде игнорируете другие типы узлов, такие как Для обработки события Здесь вызовы методов - таких, как Вы можете обрабатывать эти события созданием нового текстового узла и добавлением его к Обработчик события Для обработки события Если в стеке более одной составляющей, вы можете выбрать текущую составляющую из стека, а затем добавить ее к составляющей, находящейся теперь в вершине стека.
Когда вы получаете последнее событие Все, что осталось сделать теперь, это создать объект-парсер, который будет разбирать файл и строить дерево DOM из событий SAX. Исходный код метода Вы создаете Когда вы запустите пример, вы увидите:
Полный листинг исходного кода см. в SaxToDom.java.
Теперь вы увидели, как создать дерево DOM из событий SAX. Далее я покажу вам, как генерировать события SAX из дерева DOM. Я покажу вам это в упрощенном виде; я не уверен, что кому-то понадобится делать это. (Если у вас есть соображения по использованию этого приема, пожалуйста, дайте мне знать об этом.)
Сначала взглянем на отображение между узлами дерево DOM и событиями SAX:
Другая основная задача состоит в том, что ваш код должен реализовывать интерфейс Далее я покажу вам этот код.
Я определю, как мы собираемся отображать узлы DOM на различные типы событий SAX, так что, теперь пора взглянуть на код. Шаги, через которые вы должны пройти, следующие:
Подробности см. в исходном коде DomToSax.java.
Код для шагов 1 и 2 такой:
Далее я пройду через три типа узлов, обрабатываемых классом Сначала рассмотрим обработчик Генерируем событие Похожим способом вы обрабатываете узел документа, теперь вы генерируете Последний (и самый простой) случай обрабатывает Заметьте, что вы не должны обрабатывать игнорируемые пропуски каким-то иным способом, потому что DOM не делает различия между пропусками и другими текстовыми узлами. Если вы хотели обрабатывать их раздельно, у вас будет больше работы.
Выполнение этого кода при помощи командной строки Объектная Модель Документа предоставляет несколько методов для добавления, перемещения и удаления узлов в дереве DOM. Чтобы проиллюстрировать, как это работает, я покажу вам приложение, которое сортирует 14 элементов Первые несколько строк являются стандартным кодом разбора в DOM, какой вы использовали раньше. Далее Заметьте, что вы не передаете дерево DOM в метод Теперь - о более сложных вещах. Концептуально пузырьковая сортировка не сложно; проблема состоит в получении текста данного узла. Вы можете думать, что метод Чтобы сделать текст лучше читаемым, используйте метод На самом деле этот код - уловка, потому что вы ищете только первый узел-потомок. Это работает для документа-примера, но для более сложных документов вы должны проделать большую работу. (Например, для случая, когда внутри элемента Вашей последней задачей является сортировка узлов. Используйте метод Хотя этот код выглядит сбивающим с толку, он на самом деле не так уж и плох. Циклы Полный исходный код см. в DomSorter.java.
Манипулирование атрибутами узлов в дереве DOM очень похоже на другие функции, которые я рассматривал здесь. Несколько методов DOM работают с атрибутами:
Кодовый пример DomAttributes.java разбирает XML-файл, затем он применяет метод Теперь, когда вы научились манипулировать деревом DOM, я покажу вам, как сделать то же самое с JDOM. Как вы скоро увидите, JDOM предлагает несколько удобных методов, которые упрощают задачу, в частности, по сравнению с версией DOM.
Код начинается со сканирования командной строки, а затем вызова метода Метод Когда вы работали с DOM, вы применяли Эквивалентом Поскольку вы знаете структуру вашего XML-документа, вы можете получить элемент Для данного объекта Document запросите корневой элемент ( Заметьте, что JDOM возвращает В JDOM процедура пузырьковой сортировки такая:
Пару вещей стоит здесь упомянуть. Прежде всего, заметьте, что JDOM предоставляет вам метод Когда вам нужно поменять местами две соседние строки, вы используете совместно методы Одно последнее замечание: поскольку интерфейс Теперь, когда вы отсортировали строки сонета, вашей последней задачей является вывести их. Вы применяете Прикажите при помощи метода Полный исходный код см. в JdomSorter.java.
Хотя это и лежит вне сферы рассмотрения нашего учебника, знайте, что простейшим способом отсортировать строки сонета является таблица стилей XSLT. XSLT предоставляет великолепный элемент Я не хочу подробно объяснять эту таблицу стилей, но этот шаблон делает для вас всю работу по сортировке. Когда вы находите элемент Вы можете увидеть полную таблицу стилей в sonnetSorter.xsl.
До сих пор вы применяли в примерах DOM Второй подход состоит в использовании класса Для первого подхода просто создайте пакет с именем Единственное изменение состоит в переименовании метода в Дальше вы увидите, как создать Второй способ записать дерево DOM как XML-документ состоит в применении интерфейса Этот код использует несколько специфичных для Xerces реализаций интерфейсов DOM Уровня 3. На июль 2004 г., этот код компилируется и выполняется только со специальной пристройкой к парсеру Xerces. По мере развития стандарта DOM Уровня 3 реализация Xerces будет становиться более зрелой, и этот код почти определенно изменится. В приведенном выше коде классы Хотя в примере сортировки сонета они вам не нужны, но есть несколько других методов DOM, полезных для манипуляции деревьями DOM. Наиболее часто применяются такие методы:
Во всех примерах этого учебника вы использовали парсер Xerces. Однако, большинство примеров может использовать любой другой JAXP-совместимый парсер без каких-либо изменений. (Единственное исключение - пример Вы можете задать свойство Второй состоит в добавлении следующих строк в ваш исходный Java-код перед созданием Когда я впервые показал вам SAX-парсер, я отмечал, что одним из преимуществ использования SAX является то, что вы получаете события SAX по мере того, как парсер читает XML-файл. При применении DOM-парсера вы можете видеть данный элемент только после того, как будет обработан весь документ; в SAX вы видите этот элемент тогда же, когда и парсер. Вы можете развить это свойство, останавливая SAX-парсер, когда вы нашли то, что искали. Я покажу вам, как создать фатальную ошибку, которая убивает парсер, а это значит, что SAX-парсер не будет даже читать всего XML-файла.
В вашем приложении, убивающем парсер, вы будете искать четвертый элемент Поскольку события SAX не сохраняют состояния, ваш код должен отслеживать все события, которые вы увидите. Если вы хотите найти четвертый элемент Прежде всего, используем три переменные для поддержания информации о состоянии:
Используйте Метод Далее я покажу вам другие обработчики событий.
Теперь кратко рассмотрим другие обработчики событий, которые использует "убийца парсера". Прежде всего, обработчики Теперь обработчик для При выполнении Исключение не выведено, поскольку вы Полный листинг см. в SaxKiller.java.
Как вы видели ранее, JAXP дает вам возможность задавать разные DOM-парсеры во время выполнения; он также дает вам возможность задавать во время выполнения и реализацию Вы можете задать свойство Второй состоит в добавлении следующих строк в ваш исходный Java-код перед созданием Этот код приказывает JAXP использовать парсер Xerces от Apache Software Foundation.
Просмотрите предыдущие учебники этой серии:
Посетите страницу страницу технических отчетов DOM в W3C, если вас интересуют ссылки на все, относящееся к DOM. Отдельные спецификации см. в:
Прочитайте о SAX Version 2.0.
Изучите все о JDOM на домашней странице проекта JDOM.
Если вы хотите освежить свои познания о самом XML, прочитайте популярный учебник "Introduction to XML Вы найдете дополнительные ресурсы, относяшиеся к обсуждаемым здесь технологиям на developerWorks в зонах технологии Java и XML.
Наконец, посмотрите, как вы можете стать Сертифицированным в IBM разработчиком в XML и родственных технологиях (http://www-1.ibm.com/certify/certs/adcdxmlrt.shtml).
Doug Tidwell
В проведенном во нескольких центрах двойном слепом клиническом испытании Doug Tidwell показал себя обеспечивающим значительное облегчение симптомов сезонной аллергии, вызываемой программированием в технологиях XML и Java.
Также доступен слабоактивный Doug Tidwell (Doug Tidwell SA), который прописывается для приема небольшими дозами через каждые
24 часа. Побочными эффектами от Doug Tidwell обычно были легкими и включали в себя головокружение, тошноту - от умеренной до сильной, нечувствительность в конечностях, в редких случаях - паралич и смерть.
Попросите у вашего врача Doug Tidwell, если он подходит для вас.
Подробности см. в описании препарата Doug.
О примерах
<sonnet>
<author>
<lastName>
<firstName>
<nationality>
<yearOfBirth>
<yearOfDeath>
</author>
<lines>
[14 элементов <line>]
</lines>
</sonnet>
Установка вашей машины
CLASSPATH.)
xerces-2_5_0 или как-то в этом роде, в зависимости от релиза парсера. JAR-файлы, которые вам нужны (xercesImpl.jar и xml-apis.jar) должны быть в корневом каталоге Xerces.
jdom-b9 или как-то в этом роде. JAR-файл, который вам нужен (jdom.jar) должен быть в каталоге build.
.), xercesImpl.jar, xml-apis.jar и
jdom.jar в вашу переменную CLASSPATH.
Раздел 2. Построение структур XML с нуля
Разбор строки
org.xml.sax.InputSource. Парсер - все равно DOM или SAX (используется DocumentBuilder или SAXParser JAXP) - может взять InputSource и разобрать его, точно так же, как и любую другую разметку. Чтобы конвертировать String, код такой:
ParseString ps = new ParseString();
String markup = new String("<html><body><h1>" +
"This XML document was a " +
"<b>string!</b>" +
"</h1></body></html>");
InputSource iSrc = new InputSource(new StringReader(markup));
ps.parseAndPrint(iSrc);
InputSource из StringReader, который вы создаете из String. Внутри метода parseAndPrint код очень похож на примеры разбора в предыдущих учебниках:
public void parseAndPrint(InputSource xmlSource)
{
Document doc = null;
try
{
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(xmlSource);
if (doc != null)
DomTreePrinter.printNode(doc);
}
InputSource вместо URL. Чтобы напечатать строку, вы используете класс com.ibm.dw.xmlprogjava.DomTreePrinter. Полный исходный код находится в файлах ParseString.java и DomTreePrinter.java.
Построение с нуля дерева DOM
DomBuilder делает это. Хотя все узлы, которые он строит, зашиты в приложение, вы можете легко добавить ваш собственный код для генерации тех узлов, которые вы захотите.
DocumentBuilder:
try
{
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dbf.newDocumentBuilder();
Document doc = docBuilder.getDOMImplementation().
createDocument("", "sonnet", null);
. . .
DocumentBuilderFactory и нового DocumentBuilder, как и прежде. Затем вызовите DocumentBuilder.getDOMImplementation(), чтобы получить экземпляр чего-то, что реализует интерфейс DOMImplementation. Используйте метод этого объекта createDocument для получения нового объекта Document. (Замечание: DOMImplementation является частью DOM, а не JAXP.) В данном примере три аргумента метода createDocument задают, что ваш новый Document не имеет пространства имен, имя корневого элемента - sonnet, и Document не имеет DOCTYPE.
Использование
DocumentElementDocumentElement из объекта Document. Отличия между ними едва различимы, но существенны: объект Document является полной структурой, которая представляет разобранную версию XML-документа; DocumentElement является корневым элементом, который содержит всю разметку XML. (Комментарии могут появляться за пределами корневого элемента XML-документа; эти комментарии будут в объекте Document, но не в DocumentElement.) В XML-файле примера элемент <sonnet> содержит остальную часть документа.
. . .
Document doc = docBuilder.getDOMImplementation().
createDocument("", "sonnet", null);
Element root = doc.getDocumentElement();
root.setAttribute("type", "Shakespearean");
Element author = doc.createElement("author");
Element lastName = doc.createElement("lastName");
lastName.appendChild(doc.createTextNode("Shakespeare"));
author.appendChild(lastName);
. . .
Element yearOfDeath = doc.createElement("yearOfDeath");
yearOfDeath.appendChild(doc.createTextNode("1616"));
author.appendChild(yearOfDeath);
root.appendChild(author);
. . .
<sonnet>), затем вы создаете элемент <author>. Через весь код объект Document используется как фабрика для создания новых Node. Ваш код должен также создать иерархию документа. Чтобы построить элемент <author>, вы создаете сам элемент <author>, а затем создаете другие элементы, содержащиеся в <author> (<lastName>, <firstName> и т.д.). По мере создания дочерних элементов для <author>, вы добавляете их к элементу <author>. Когда элемент <author> завершен, вы добавляете его к родительскому элементу, <sonnet>. Наконец, заметьте некоторую неуклюжесть в добавлении текста к элементу. Чтобы создать такую разметку:
<yearOfDeath>1616</yearOfDeath>
<yearOfDeath>, затем создать текстовый узел, содержащий текст 1616, затем добавить текстовый узел к элементу <yearOfDeath>, и затем вы можете добавить элемент <yearOfDeath> к элементу <author>. Для того, чтобы добавить текстовый узел к элементу, нет метода вроде Element.setText(), как можно было бы ожидать. Особенности вроде этой были тем, что привело к созданию JDOM; я покажу вам, как строить XML-документ при помощи JDOM через минуту.
Выполнение DomBuilder
java DomBuilder в командной строке. Вы увидите такой вывод:
C:\adv-xml-prog>java DomBuilder
<?xml version="1.0" ?>
<sonnet type="Shakespearean"><author><lastName>Shakespeare
</last-name><firstName>William</firstName><nationality>Bri
tish</nationality><yearOfBirth>1564</yearOfBirth><yearOfDe
ath>1616</year-of-death></author><title>Sonnet 130</title>
<lines><line>My mistress' eyes are nothing like the sun,</
line><line>Coral is far more red than her lips red.</line>
<line>If snow be white, why then her breasts are dun,</lin
e><line>If hairs be wires, black wires grow on her head.<l
DomBuilder в DomBuilder.java.
Построение с нуля JDOM
DocumentDocument в JDOM является, как и следовало ожидать, значительно более легким, чем DOM-версия. Как вы можете вспомнить из целей JDOM, создание Document работает так же, как создание объекта Java. Вот как этот код начинается:
public void buildDocument()
{
Element root = new Element("sonnet");
root.setAttribute("type", "Shakespearean");
Vector author = new Vector();
author.add(new Element("lastName").addContent("Shakespeare"));
author.add(new Element("firstName").addContent("William"));
author.add(new Element("nationality").addContent("British"));
author.add(new Element("yearOfBirth").addContent("1564"));
author.add(new Element("yearOfDeath").addContent("1616"));
root.addContent(new Element("author").setContent(author));
root.addContent(new Element("title").addContent("Sonnet 130"));
Vector элементов, а затем использовать метод setContent для добавления в Vector какого-то другого элемента, как потомка.
root, вы можете создавать объект Document JDOM в нем:
Vector lines = new Vector();
lines.add(new Element("line").
addContent("My mistress' eyes are nothing like the sun,"));
. . .
lines.add(new Element("line").
addContent("As any she belied with false compare."));
root.addContent(new Element("lines").setContent(lines));
Document doc = new Document(root,
new DocType("sonnet", "sonnet.dtd"));
try
{
XMLOutputter xo = new XMLOutputter(" ", true);
xo.output(doc, System.out);
}
XMLOutputter для вывода документа на консоль. Заметьте, что JDOM дает вам возможность создать декларацию DOCTYPE при создании объекта Document.
Генерация событий SAX из разделенных запятыми значений
SaxToDom, который преобразует события SAX в объекты DOM; комбинирование этих двух приемов дает вам исключительно гибкий способ обрабатывать много различных видов данных.
StreamTokenizer, а затем будете генерировать соответствующий XML из найденных в данных лексем.
test.csv:
"000010","CHRISTINE","I","HAAS","A00","3978",19650101
"000020","MICHAEL","L","THOMPSON","B01","3476",19731010
"000030","SALLY","A","KWAN","C01","4738",19750405
"000050","JOHN","B","GEYER","E01","6789",19490817
static String tagNames[] = {"employeeNumber",
"firstName",
"middleInitial",
"lastName",
"deptNo",
"extension",
"dateOfBirth"};
CSV в SAX, продолжение
StreamTokenizer:
BufferedReader br = new BufferedReader(new FileReader(uri));
StreamTokenizer st = new StreamTokenizer(br);
st.eolIsSignificant(true);
st.whitespaceChars(',', ',');
st.quoteChar('"');
StreamTokenizer. Теперь определите три массива символов, которые вы будете использовать для красивой печати XML на выходе:
char [] lineBreak = new String("\n").toCharArray();
char [] singleIndent = new String(" ").toCharArray();
char [] doubleIndent = new String(" ").toCharArray();
startDocument.
startElement для элемента <employees>.
startElement для элемента <employee>.
<employeeNumber>, второй будет <firstName> и т.д. Для каждой лексемы генерируйте startElement для элемента, генерируйте characters для текста, затем генерируйте endElement.
endElement для элемента <employee>.
endElement для элемента <employees>.
endDocument.
Генерация событий SAX
startDocument. Вы знаете, что вы должны вложить весь XML-документ в элемент <employees>, так что вы можете двигаться дальше и сгенерировать startElement. После обработки всего документа вы сгенерируете событие endElement для элемента <employees>. Вот код:
dh.startDocument();
dh.startElement(null, null, "employees", null);
dh.ignorableWhitespace(lineBreak, 0, lineBreak.length);
ignorableWhitespace.)
while для обработки строк файла. Внешний цикл while выполняется до тех пор, пока StreamTokenizer не вернет тип, соответствующий маркеру конца файла (StreamTokenizer.TT_EOF). В каждой итерации цикла вызывайте startElement для элемента <employee>, обрабатывайте все составляющие в текущей строке исходного файла, затем вызывайте endElement для <employee>. Внутренний цикл обрабатывает каждую строку до тех пор, пока StreamTokenizer найдет либо маркер конца строки (TT_EOL), либо маркер конца файла (TT_EOF). Вот первая часть кода:
st.nextToken();
while (st.ttype != StreamTokenizer.TT_EOF)
{
dh.ignorableWhitespace(singleIndent, 0, singleIndent.length);
dh.startElement(null, null, "employee", null);
dh.ignorableWhitespace(lineBreak, 0, lineBreak.length);
int i = 0;
while (st.ttype != StreamTokenizer.TT_EOL &&
st.ttype != StreamTokenizer.TT_EOF)
{
i для подсчета того, сколько элементов нашел сканер (tokenizer). Используйте его для выборки имени элемента из массива, обсуждавшегося ранее.
Класс StreamTokenizer
StreamTokenizer. В первый раз, когда вы используете этот класс, вам нужно вызывать метод nextToken. Этот метод приказывает сканеру найти следующую лексему в файле (в данном случае - первую). Теперь вы можете получить лексему от объекта-сканера использованием поля nval для числовых значений и поля sval для строковых значений. Вы используете также поле ttype для определения типа лексемы. Код для преобразования числовой лексемы в серию событий SAX такой:
if (st.ttype == StreamTokenizer.TT_NUMBER)
{
char [] chars = BigInteger.valueOf((long)st.nval).
toString().toCharArray();
dh.ignorableWhitespace(doubleIndent, 0, doubleIndent.length);
dh.startElement(null, null, tagNames[i], null);
dh.characters(chars, 0, chars.length);
dh.endElement(null, null, tagNames[i]);
dh.ignorableWhitespace(lineBreak, 0, lineBreak.length);
}
BigInteger используется здесь для обработки длинных больших значений, которые могут появляться в файлах с разделителем-запятой. (Например, даты в реляционных базах данных часто кодируются как 8-значные числа.) Преобразовывайте числовые значение в массив символов, а затем вызывайте соответствующие события SAX.
i) и вызывайте метод StreamTokenizer.nextToken, чтобы продвинуть сканер. Когда внутренний цикл заканчивается, вызывайте endElement для элемента <employee>. Когда внешний цикл заканчивается, вызывайте endElement для элемента <employees>, за которым следует событие:
st.nextToken();
i++;
}
dh.ignorableWhitespace(singleIndent, 0, singleIndent.length);
dh.endElement(null, null, "employee");
st.nextToken();
dh.ignorableWhitespace(lineBreak, 0, lineBreak.length);
}
dh.endElement(null, null, "employees");
dh.ignorableWhitespace(lineBreak, 0, lineBreak.length);
dh.endDocument();
}
Раздел 3. Преобразование из одного API в другой
Преобразование событий SAX в деревья DOM
<invoice>. Для обработки каждого <invoice> вы хотите использовать дерево DOM. К сожалению, у вас нет машины с достаточным объемом памяти для создания дерева DOM, содержащего потенциально миллион объектов, представляющих эти 10,000 счетов.
<invoice>, чтобы построить дерево DOM. Когда вы получаете событие startElement для элемента <invoice>, вы создаете новое дерево DOM. Как только ваш код получает события SAX, вы добавляете новые Node в дерево DOM. Когда вы получаете событие endElement для <invoice>, вы передаете дерево DOM в вашу процедуру обработки счета. После обработки текущего <invoice> вы you можете удалить дерево DOM и начать сначала с новым рядом событий SAX.
Отображение событий SAX на объекты DOM
startDocument
Естественно подумать, что вы будете использовать это событие, чтобы поручить вашему объекту DocumentBuilder создать Document. К сожалению, startDocument не сообщает вам имя корневого элемент, так что вы должны делать это в обработчике startElement.
startElement
Вы должны обрабатывать два случая startElement:
startElement (иными словами, это корневой элемент), используйте ваш объект DocumentBuilder для создания нового Document. Информация в событии startElement сообщает вам, наряду с прочим, имя корневого элемента, так что вы используете эту информацию для установки имени корневого элемент.
startElement, используйте объект Document для создания нового Element. Любые атрибуты, содержащиеся в событии startElement, добавляются в новый Element. Когда вы закончите, поместите новый Element в стек.
characters
Для события characters создайте новый узел Text и добавьте его в качестве потомка к узлу, находящемуся в вершине вашего стека.
ignorableWhitespace
Если вы хотите включать пропуски в дерево DOM, создайте новый узел Text, содержащий пропуск. Как и с обычными событиями characters, вы можете добавить его к узлу, находящемуся в вершине стека.
endElement
endElement означает, что парсер нашел конец элемента. Это значит, что вам нужно выбрать из стека законченный элемент, а затем добавить его в качестве потомка к элементу, который теперь находится в вершине стека. (Чтобы избежать EmptyStackException, убедитесь, что в стеке есть родительский элемент.)
endDocument
Игнорируйте это событие. После того, как вы обработали последнее событие endElement, стек будет содержать единственную составляющую, корневой элемент документа.
SaxToDom и продемонстрирую, как вы можете создавать объекты DOM по мере поступления событий SAX.
Использование стека
characters только сообщает вам, что парсер нашел какие-то символы в документе; оно ничего не сообщает вам об элементе, в котором содержатся эти символы. Если вам нудна эта информация (а очень часто она вам нужна), вы должны отслеживать ее сами.
Node DOM. Если вы создали Node, вам нужно знать его родителя. Наиболее эффективным способом сделать это является стек, в частности, java.util.Stack. Обрабатывайте различные типы Node DOM следующим образом:
endElement), выберите элемент из стек и добавьте его к тому элементу, который находится в вершине стека теперь.
Text как реакцию на события characters и ignorableWhitespace. В обоих случаях метод peek() классаof the java.util.Stack позволяет вам добавить текстовый узел без выборки элемента из стека.
ProcessingInstruction и Comment. Если вы будете создавать узлы этих типов, вы просто будете добавлять их к элементу в вершине стека. (Если вы реализуете это, имейте в виду, что комментарии и инструкции обработки могут появляться вне корневого элемента.)
Обработчик события
startElementstartElement вы обычно создаете новый Element DOM и помещаете его в стек. Однако, эта ситуация имеет дополнительное осложнение: для первого события startElement вам нужно создать новый объект DOM Document. Все другие объекты DOM, которые вы будете создавать, будут потомками объекта Document. Вот код:
if (firstElementNotFoundYet)
{
root = docBuilder.getDOMImplementation().
createDocument(namespaceURI, rawName, null);
Element docElement = root.getDocumentElement();
if (attrs != null)
{
int len = attrs.getLength();
for (int i = 0; i < len; i++)
docElement.setAttribute(attrs.getQName(i),
attrs.getValue(i));
}
elementStack = new Stack();
elementStack.push(docElement);
firstElementNotFoundYet = false;
}
else
{
Element currentElement = root.createElement(rawName);
if (attrs != null)
{
int len = attrs.getLength();
for (int i = 0; i < len; i++)
currentElement.setAttribute(attrs.getQName(i),
attrs.getValue(i));
}
elementStack.push(currentElement);
}
createDocument(), getDocumentElement() и createElement() - такие же, как вы применяли в DomBuilder. Главное отличие в обработке здесь состоит в использовании elementStack для отслеживания последнего созданного вами Element.
Обработчики событий
characters и ignorableWhitespace Element, который находится в вершине стека. Вы можете использовать метод Stack.peek() для обращения к верхнему элементу стека без удаления его из стека. Коды этих двух обработчиков событий такие:
public void ignorableWhitespace(char ch[], int start, int length)
{
characters(ch, start, length);
}
public void characters(char ch[], int start, int length)
{
((Element) elementStack.peek()).
appendChild(root.createTextNode(new String(ch, start, length)));
}
ignorableWhitespace просто вызывает обработчик события characters. В обработчике события characters вы можете использовать метод peek() для обращения к элементу в вершине стека. Заметьте, что вы должны преобразовать элемент стека в Element; метод peek() возвращает Object языка Java. Вы можете затем создать новый текстовый узел и добавить его к Element в вершине стека.
Обработчик события
endElementendElement вам нужно удалить составляющую из вершины стека и добавить ее в ту составляющую, которая прежде была под ней. Единственным исключением является событие endElement в конце документа; для этого события стек имеет только одну составляющую. Вот как выглядит код:
public void endElement(String namespaceURI, String localName,
String rawName)
{
if (elementStack.size() > 1)
{
Element currentElement = (Element) elementStack.pop();
((Element) elementStack.peek()).appendChild(currentElement);
}
}
endElement, в вершине стека находится единственный корневой элемент. Вы можете затем выбрать корневой элемент из стека и обработать его.
Завершение всего этого
parseAndPrint такой:
Document root = null; // global variable
. . .
public void parseAndPrint(String uri)
{
try
{
dbf = DocumentBuilderFactory.newInstance();
docBuilder = dbf.newDocumentBuilder();
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
sp.parse(uri, this);
if (root != null)
printDomTree(root);
}
. . .
DocumentBuilder, который применяется для создания объектов DOM. Вы также создаете объект SAXParser для разбора XML-файла и генерации событий SAX.
C:\adv-xml-prog>java SaxToDom sonnet.xml
<?xml version="1.0"?>
<!DOCTYPE sonnet SYSTEM "sonnet.dtd">
<sonnet type="Shakespearean">
<author>
<lastName>Shakespeare</lastName>
<firstName>William</firstName>
<nationality>British</nationality>
<yearOfBirth>1564</yearOfBirth>
<yearOfDeath>1616</yearOfDeath>
</author>
<title>Sonnet 130</title>
<lines>
<line>My mistress' eyes are nothing like the sun,</line>
. . .
Генерация событийSAX из дерева DOM
DOCUMENT_NODE
Вызвать startDocument(), затем обработать все в узле документа, затем вызвать endDocument(). Чтобы обработать все в документе, используйте рекурсивную технику, которая использовалась в примерах DOM.
ELEMENT_NODE
Теперь рассмотрите элемент, чтобы выбрать все атрибуты, которые у него есть; вам нужно сделать это, когда вы вызываете startElement(). После того, как вы вызвали startElement(), вы будете обрабатывать всех потомков элемент, а затем вызывать endElement().
TEXT_NODE
Для этого типа узла вы просто создаете массив char, который содержит значение узла.
DefaultHandler. Этот интерфейс определяет обработчики событий SAX. Когда ваш код обходит дерево DOM, вы создаете события SAX и посылаете их себе же.
Создание событий SAX
try
{
dbf = DocumentBuilderFactory.newInstance();
db = dbf.newDocumentBuilder();
doc = db.parse(uri);
if (doc != null)
generateSAXEvents(doc, this);
}
DomToSax.
Создание событий SAX, продолжение
DOCUMENT_NODE:
case Node.DOCUMENT_NODE:
{
dh.startDocument();
generateSAXEvents(((Document)node).getDocumentElement(), dh);
dh.endDocument();
break;
}
startDocument, вызываем процедуру рекурсивно для обработки элемента документа, затем генерируем событие endDocument.
ELEMENT_NODE обрабатывается похоже на DOCUMENT_NODE, с тем исключением, что вам нужно обработать атрибуты элемента DOM прежде, чем вы сможете генерировать событие startElement. startElement требует, чтобы вы передали объект, который реализует интерфейс Attributes вместе с именем элемента. Вот код:
case Node.ELEMENT_NODE:
{
AttributesImpl saxAttrs = new AttributesImpl();
if (node.hasAttributes())
{
NamedNodeMap attrs = node.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
saxAttrs.addAttribute(null, null,
attrs.item(i).getNodeName(),
null, attrs.item(i).getNodeValue());
}
dh.startElement(null, null, node.getNodeName(), saxAttrs);
if (node.hasChildNodes())
{
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++)
generateSAXEvents(children.item(i), dh);
}
dh.endElement(null, null, node.getNodeName());
break;
}
startElement, вызываете рекурсивно процедуру, затем генерируете событие endElement.
TEXT_NODE. Метод текстового узла getNodeValue возвращает String; преобразуйте его в массив символов (char), затем сгенерируйте событие characters:
case Node.TEXT_NODE:
{
char[] chars = node.getNodeValue().toCharArray();
dh.characters(chars, 0, chars.length);
break;
}
DomToSax sonnet.xml даст тот результат, которого вы ждете на основании предыдущих примеров. Полный исходный код находится в DomToSax.java.
Раздел 4. Манипулирование структурами дерева
Манипулирование деревом DOM
<line>. Для сортировки этих элементов вам нужно перемещать узлы с одного места на другое в дереве DOM.
Поскольку сонет имеет только 14 строк, вы можете использовать пузырьковый алгоритм сортировки для их упорядочивания. Когда этот код будет работать правильно для документа-примера, я покажу вам сокращенный способ. (В качестве упражнения вы можете попробовать сделать этот код более надежным.)
Для начала вам нужно получить все элементы <line> в дереве DOM. К счастью, интерфейсы Document и Element содержат метод getElementsByTagName. По данному имени тега этот метод возвращает NodeList со всеми элементами, имеющими такое имя тега. Код такой:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setIgnoringElementContentWhitespace(true);
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(uri);
if (doc != null)
{
NodeList theLines = doc.getDocumentElement().
getElementsByTagName("line");
sortLines(theLines);
DomTreePrinter.printNode(doc);
}
getElementsByTagName применяется для получения NodeList элементов <line>; передаваемых затем в метод sortLines. После того, как строки сонета отсортированы, вы применяете класс DomTreePrinter для печати измененного дерева DOM.
sortLines. Пока ваш объект Document не попадет под сборку мусора, вi можете начать с любого из узлов в NodeList найти предка этого узла, братьев этого узла и потомков этого узла и т.д. Это означает, что вы можете начать с узла, найти его соседний (братский) элемент (следующий элемент <line>) и сравнить текст в этих элементах. Если вам нужно поменять их местами, вы можете использовать функции DOM, чтобы приказать родителю этих узлов поместить один узел перед другим.
...или это дерево DOM манипулирует вами?
getNodeValue сделает то, что вы хотите, но не в этом случае. В соответствии со стандартом DOM, значение узла Element - null. Чтобы получить текст данного <line> , вам нужно получить все его узлы-потомки типа Text. Когда вы вызываете getNodeValue с узлом Text, то, что вы получаете, и будет текстом, который вы ищете.
getTextFromLine для выделения текста, который вам нужен:
public String getTextFromLine(Node lineElement)
{
StringBuffer returnString = new StringBuffer();
if (lineElement.getNodeName().equals("line"))
{
NodeList kids = lineElement.getChildNodes();
if (kids != null)
if (kids.item(0).getNodeType() == Node.TEXT_NODE)
returnString.append(kids.item(0).getNodeValue());
}
else
returnString.setLength(0);
return new String(returnString);
developerWorks(r) ibm.com/developerWorks
XML programming in Java technology, Part 3
Page 18 of 30 (c) Copyright IBM Corporation 1994, 2005. All rights reserved.
}
<line> допускается элемент <b>.) Этот код использует метод getNodeName, чтобы убедиться, что это правильный вид элемента, затем он получает потомка этого узла, затем он убеждается, что первый потомок узла - текстовый узел. Предположим, все это так, тогда метод возвращает текст элемента.
Сортировка узлов
String.compareTo, чтобы определить, какая из двух строк является первой в порядке сортировки. Если вам нужно поменять местами два узла, используйте метод insertBefore DOM. Этот метод вставляет один узел перед другим; лучше всего, если узел уже существует в дереве DOM, он просто перемещается в новое место. Вот этот код:
public void sortLines(NodeList theLines)
{
if (theLines != null)
{
int len = theLines.getLength();
for (int i = 0; i < len; i++)
for (int j = 0; j < (len - 1 - i); j++)
if (getTextFromLine(theLines.item(j)).
compareTo(getTextFromLine(theLines.item(j+1)))
> 0)
theLines.item(j).getParentNode().
insertBefore(theLines.item(j+1),
theLines.item(j));
}
}
for the отрабатывают пузырьковую сортировку. Оператор if сравнивает текст в данной строке с текстом в следующей строке; если вам нужно поменять их местами, вы можете применить insertBefore. Заметьте, что getParentNode применяется для получения предка узла. Если вы имеете предка, вы приказываете предку поместить следующую строку перед текущим узлом.
Работа с атрибутами
Node.getAttributes()
Если этот Node является Element, метод возвращает NamedNodeMap атрибутов элемента. Если Node является чем-то другим, метод возвращает null.
Element.getAttribute(String name)
Возвращает строковое значение атрибута с заданным именем.
Element.getAttributeNode(String name)
Element.getAttributeNodeNS(String namespaceURI, String name)
Этот метод возвращает объект, который имеет заданное имя (и пространство имен, если оно задано) и реализует интерфейс Attr.
Element.hasAttribute(String name)
Element.hasAttributeNS(String name)
Этот метод возвращает true, если элемент имеет атрибут с заданным именем (и пространством имен, если оно задано).
Element.removeAttribute(String name)
Element.removeAttributeNS(String namespaceURI, String name)
Этот метод удаляет с заданным именем (и пространством имен, если оно задано).
Element.removeAttributeNode(Attr oldAttr)
Это одно из обстоятельств, сбивающих с толку: аргумент для этого метода является объектом, который реализует интерфейс Attr. Вы хотите, чтобы Element удалил атрибут, который соответствует объекту. Метод возвращает объект (Attr), который был удален. Чтобы еще усложнить дело, если атрибут имеет значение по умолчанию (иными словами, если атрибут имеет значение независимо от того, задано оно в XML-документе или нет), удаленный атрибут заменяется на новый Attr, который имеет значение по умолчанию.
Element.setAttribute(String name, String value)
Element.setAttributeNS(String namespaceURI, String name,
String value)
Этот метод добавляет новый атрибут с заданным именем и значением (и пространством имен, если оно задано).
Element.setAttributeNode(Attr newAttribute)
Element.setAttributeNodeNS(Attr newAttribute)
Этот метод добавляет объект Attr, переданный в него в качестве аргумента. Если новый атрибут заменяет уже существующий с тем же именем (и пространством имен, если оно задано), этот метод возвращает замененный объект, иначе метод возвращает null.
Element.setAttribute для добавления атрибута к каждому узлу Element в дереве DOM. Его последняя задача - использование класса DomTreePrinter для печати модифицированного дерева DOM.
Манипулирование деревом JDOM
parseAndSortLines() для разбора и обработки сонета:
public static void main(String[] argv)
{
if (argv.length == 0 ||
(argv.length == 1 &&argv[0].equals("-help")))
// print message and exit
JdomSorter js = new JdomSorter();
js.parseAndSortLines(argv[0]);
}
public void parseAndSortLines(String uri)
{
try
{
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(new File(uri));
sortLines(doc);
sortLines() - то место, где выполняется большая часть работы. Передайте весь ваш документ в этот метод; его первой задачей является получение всех элементов <line>.
getElementsByTagName() для нахождения всех элементов, которые вам нужны. Если вы имеете эти элементы, вы можете использовать родительский элемент (доступный через getParentNode()) для перемещения <line>, как вам нужно.
Еще о манипуляциях в JDOM
getElementsByTagName() в JDOM являются getChild() и getChildren(). Главная концептуальная разница между JDOM и DOM состоит в том, что JDOM работает только с непосредственными "детьми" элемента, а не с его отдаленными потомками. Другими словами, вы не можете начать с корня документа и запросить все элементы <line> в документе; вы должны найти элемент <lines> и запросить все его дочерние элементы <line>.
<lines> очень быстро. Вот как это сделать:
public void sortLines(Document sonnet)
{
Element linesElement = sonnet.getRootElement().
getChild("lines");
List lines = linesElement.getChildren("line");
<sonnet>), затем запросите его дочерний элемент с именем <lines>. Далее запрашивайте для всех элементов <line> дочерний элемент. Если вы не знаете точной структуры документа, вы должны применять метод getChildren() для получения дочерних элементов, выбирать подходящий дочерний элемент, а затем применять getChildren(), пока не найдете тот элемент, который вам нужен.
List, часть Java Collections API. Одна из многих замечательных вещей, относящихся к интерфейсу List, состоит в том, что любые изменения, которые вы сделаете в List, отражаются в обеспечивающей его базовой структуре.
for (int i = 0; i < 14; i++)
for (int j = 0; j < (14 - 1 - i); j++)
if ( ((Element)lines.get(j)).getText().
compareTo(((Element)lines.get(j+1)).getText())
> 0)
lines.add(j, lines.remove(j+1));
getText() для получения текста данного элемента. Это значит, что вам не нужно писать вспомогательную процедуру для получения текста элемента, дочернего для данного элемента, как вы делали в DOM. Также заметьте, что вы должны преобразовать тип составляющей из List в Element.
add() и remove(). Удаляете строку на позиции j+1, затем вставляете ее на позицию j.
List дает вам возможность модифицировать обеспечивающие его базовые данные, ваш метод sortLines() не возвращает ничего. Изменения, которые вы делаете в элементе <lines>, отражаются в самом объекте Document.
Вывод результатов
XMLOutputter с некоторыми его свойствами, о которых я раньше не упоминал:
sortLines(doc);
XMLOutputter xo = new XMLOutputter();
xo.setTrimAllWhite(true);
xo.setIndent(" ");
xo.setNewlines(true);
xo.output(doc, System.out);
setTrimAllWhite(), чтобы XMLOutputter удалял все лишние пропуски, затем используйте setIndent() и setNewlines(), чтобы установить красивую печать вашего XML-документа. Результаты выглядят так:
C:\adv-xml-prog>java JdomSorter sonnet.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sonnet SYSTEM "sonnet.dtd">
<sonnet type="Shakespearean">
<author>
. . .
<lines>
<line>And in some perfumes is there more delight</line>
<line>And yet, by Heaven, I think my love as rare</line>
<line>As any she belied with false compare.</line>
<line>But no such roses see I in her cheeks.</line>
<line>Coral is far more red than her lips red.</line>
. . .
Последнее замечание о манипуляции деревом
<xsl:sort>, который делает все, что вы с таким трудом делали в этом примере. Вот часть таблицы стилей:
<xsl:template match="lines">
<lines>
<xsl:for-each select="line">
<xsl:sort/>
<xsl:copy>
<xsl:apply-templates select="*|@*|text()"/>
</xsl:copy>
</xsl:for-each>
</lines>
</xsl:template>
<lines>, вы выводите новый элемент <lines>, сортируете все элементы <line> внутри него, а затем копируете их в результирующий документ.
Раздел 5. Продвинутые свойства DOM
Сериализация дерева DOM
printDomTree. Теперь я покажу вам два других способа вывести (или сериализовать) дерево DOM. Первый подход состоит в простом перемещении printDomTree в отдельный класс, так что вы не должны включать этот метод в исходный Java-код каждого DOM-приложения, которое вы создаете.
DOMSerializer. Этот класс является частью DOM Уровня 3, но в настоящее время он не добавлен к классам фабрик JAXP. (DOMSerializer, между прочим, может быть добавлен или не добавлен в JAXP в будущем.)
com.ibm.dw.xmlprogjava и класс в пакете с именем DomTreePrinter. Этот класс содержит один public static метод с именем printNode. Вот как выглядит его код:
package com.ibm.dw.xmlprogjava;
. . .
public class DomTreePrinter
{
/** Prints the specified node, recursively. */
public static void printNode(Node node)
{
int type = node.getNodeType();
switch (type)
{
// print the document element
case Node.DOCUMENT_NODE:
{
System.out.println("<?xml version=\"1.0\" ?>");
printNode(((Document)node).getDocumentElement());
break;
}
printNode вместо printDomTree. Полный исходный код см. в DomThree.java и DomTreePrinter.java.
DOMSerializer.
Применение LSSerializer
LSSerializer. Он является частью пакета org.w3c.dom.ls, который является частью спецификации DOM Уровня 3 Загрузка и Сохранение. Поддержка этого пока незаконченного стандарта в Xerces, возможно, изменится, но код, который работает в июле 2004 г. такой:
import org.apache.xerces.dom.DOMOutputImpl;
import org.w3c.dom.Document;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSParser;
import org.w3c.dom.ls.LSSerializer;
. . .
public class DomFour
{
public void parseAndPrint(String uri)
{
Document doc = null;
try
{
System.setProperty(DOMImplementationRegistry.PROPERTY,
"org.apache.xerces.dom.DOMXSImplementationSourceImpl");
DOMImplementationRegistry direg =
DOMImplementationRegistry.newInstance();
DOMImplementationLS dils =
(DOMImplementationLS) direg.getDOMImplementation("LS");
LSParser lsp = dils.createLSParser
(DOMImplementationLS.MODE_SYNCHRONOUS, null);
doc = lsp.parseURI(uri);
LSSerializer domWriter = dils.createLSSerializer();
LSOutput lso = new DOMOutputImpl();
lso.setByteStream(System.out);
domWriter.write(doc, lso);
}
catch (Exception e)
{
System.err.println("Sorry, an error occurred: " + e);
}
}
DOMXSImplementationSourceImpl и DOMOutputImpl специфичны для парсера Xerces. Полный исходный код см. в DomFour.java.
Другие функцииDOM
appendChild(Node newChild)
Добавляет узел newChild как последний дочерний узел родительского узла.
removeChild(Node oldChild)
Удаляет узел oldChild из родительского узла.
replaceChild(Node newChild, Node oldChild)
Заменяет oldChild на newChild. (Примечание: и newChild, и oldChild должны быть созданы одним DocumentBuilder. )
Использование разных DOM-парсеров
DOMSerializer, который использует классы DOM Уровня 3, еще не стандартизированные в JAXP.) Одной из целей JAXP является обеспечение для вас возможности менять парсеры без каких-либо изменений в вашем исходном коде. JAXP достигает этого загрузкой определенного парсер во время выполнения. Во время выполнения JAXP определяет имя класса, который реализует интерфейс DocumentBuilderFactory. JAXP ищет имя класса в четырех местах (именно в таком порядке):
javax.xml.parsers.DocumentBuilderFactory
javax.xml.parsers.DocumentBuilderFactory в файле jre/lib/jaxp.properties
CLASSPATH, первое имя, найденное в файле META-INF/services/javax.xml.parsers.DocumentBuilderFactory
DocumentBuilderFactory по умолчанию для платформы Java (в JDK
1.4, парсер по умолчанию -org.apache.crimson.jaxp.DocumentBuilderFactoryImpl)
javax.xml.parsers.DocumentBuilderFactory двумя способами. Первый состоит в использовании параметра -D командной строки:
java -Djavax.xml.parsers.DocumentBuilderFactory=[DBF class] . . .
DocumentBuilderFactory:
System.setProperty("javax.xml.parsers.DocumentBuilderFactory",
"org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Раздел 6. Продвинутые свойства SAX
Останов SAX-парсера
<line> в сонете. Когда вы его найдете, вы создадите SAXParseException, а затем вызовете метод fatalError. Я далее покажу вам этот код.
Поиск
<line>, вы знаете, что он начинается с четвертого события startElement, для которого rawName - line. Все события characters и ignorableWhitespace, которые ваш парсер парсер получает, пока он находится в четвертом элементе <line>, являются частью текста элемента, и когда он находит четвертое событие endElement, для которого rawName - line, вы знаете, что у вас есть уже все, что вы искали. В этот момент создавайте ваше исключение и вызывайте fatalError.
int lineCount = 0;
boolean inFourthLine = false;
StringBuffer fourthLine = new StringBuffer();
lineCount для подсчета того, сколько элементов <line> вы увидели до сих пор, установите флажок inFourthLine, когда вы увидите событие startElement для четвертого элемента <line>, используйте fourthLine для сохранения текста самого элемента <line>.
startElement выглядит так:
public void startElement(String namespaceURI, String localName,
String rawName, Attributes attrs)
{
if (rawName.equals("line") && ++lineCount == 4)
inFourthLine = true;
}
Обработка других событий
characters и ignorableWhitespace - которые просто проверяют флаг inFourthLine и сохраняют текст, если парсер находится в четвертом элементе <line>:
public void characters(char ch[], int start, int length)
{
if (inFourthLine)
fourthLine.append(new String(ch, start, length));
}
public void ignorableWhitespace(char ch[], int start, int length)
{
if (inFourthLine)
characters(ch, start, length);
}
endElement. Когда ваш парсер достигает конца четвертого элемента<line>, отпечатайте текст строки, а затем создайте исключение, которое убьет парсер:
public void endElement(String namespaceURI, String localName,
String rawName)
throws SAXException
{
if (rawName.equals("line") && inFourthLine)
{
System.out.println("\nThe text of the fourth line is: \n");
System.out.println("\t" + fourthLine);
SAXParseException spe =
new SAXParseException("Found the fourth <line>, " +
"so we killed the parser!",
new LocatorImpl());
fatalError(spe);
}
}
SaxKiller вы должны увидеть что-то такое:
C:\xml-prog-java>java SaxKiller sonnet.xml
The text of the fourth line is:
If hairs be wires, black wires grow on her head.
catch его в методе main:
SaxKiller s1 = new SaxKiller();
try
{
s1.parseURI(argv[0]);
}
// We're expecting an exception, so we ignore
// anything that happens...
catch (Exception e) { }
Использование разных SAX-парсеров
SAXParserFactory. Во время выполнения JAXP определяет имя класса, который реализует интерфейс SAXParserFactory. Порядок, в котором JAXP ищет имя класса, следующий:
javax.xml.parsers.SAXParserFactory
javax.xml.parsers.SAXParserFactory в файле jre/lib/jaxp.properties
CLASSPATH, первое имя, найденное в файле META-INF/services/javax.xml.parsers.SAXParserFactory
SAXParserFactory по умолчанию для платформы Java (в JDK 1.4 парсер по умолчанию -
org.apache.crimson.jaxp.SAXParserFactory)
javax.xml.parsers.SAXParserFactory двумя способами. Первый состоит в применении параметра командной строки -D:
java -Djavax.xml.parsers.SAXParserFactory=[SPF class name] . . .
SAXParserFactory:
System.setProperty("javax.xml.parsers.SAXParserFactory",
"org.apache.xerces.jaxp.SAXParserFactoryImpl");
SAXParserFactory spf = SAXParserFactory.newInstance();
Раздел 7. Резюме и ссылки
Резюме
В этой последней части серии учебников по XML-программированию в технологии Java я рассмотрел более эзотерические подробности API DOM, SAX и JDOM. Теперь вы должны знать почти обо всем, что может делать парсер. Когда вы будете строить собственные XML-приложениея, я надеюсь, что эти методы и приемы облегчат вам жизнь.
Ресурсы
Об авторе
| Каталог | Индекс раздела |