[an error occurred while processing this directive] IT • archiv :: Print

IT • archiv


[an error occurred while processing this directive]

[an error occurred while processing this directive]

Программирование XML в Java. Часть 2

[an error occurred while processing this directive](none) [an error occurred while processing this directive](none)[an error occurred while processing this directive] ::
[an error occurred while processing this directive](none)
[an error occurred while processing this directive]([an error occurred while processing this directive] Марк Джонсон [an error occurred while processing this directive])

[an error occurred while processing this directive](none)

Преимущества использования SAX, LAX, и DTDs.

XML Extra
PDF versionPDF версия
Обзор
Первая статья в этой серии вводит понятие SAX, Simple API для XML. Эта статья глубже знакомит с использованием SAX в ваших приложениях. Сначала вы прочитаете про определение типа документа(DTD), которое определяет структуру XML документа. Вы узнаете про LAX, Lazy API для XML (разработанный для этой статьи), который делает использование SAX даже проще. Затем вы увидете, как LAX применяется в системе с небольшим документооборотом, которая использует похожие XML данные для генерации многоцелевых документов. (3600 слов)

Если вы читали статью, написанную прошлым месяцем, вы уже понимаете, как можно использовать SAX (Simple API для XML) для представления XML документов. (Если вы её пока не читали, можете читать эту). В той статье я объяснял, как авторы приложений применяют SAX интерфейс DocumentHandler, который выполняет специфические действия, когда в ходе анализа(parsing) XML документа выполняется частичное условие(такое как начальный тэг). Но что хорошего в такой функции? Читайте дальше. Вы также узнаете, что XML парсер (XML parser) проверяет, что документ хорошосформирован (имеется в виду, что все открытые тэги закрываются и нет неправильного наложения). Но даже хорошосформированные документы могут содержать ничего не значащие данные или иметь бессмысленную структуру. Как можно определить и сформулировать условия этого?

Эта статья отвечает на оба вопроса иллюстрирующим примером. Я начну с последнего вопроса: проанализировав документ один раз, как вы удостоверитесь, что анализируемый вашей программой XML действительно имеет смысл? Затем я продемонстрирую расширение к XML, которое я называю LAX(Lazy API для XML), которое делает написание SAX обработчиков событий даже проще. Наконец, я свяжу их вместе и продемонстрирую полезную технологию с маленьким примером, которая производит форматированные рецепты и списки покупок из одного XML документа.

Мусор получил, мусор выдал.

Особенность XML, как вы возможно слышали, в том, что он позволяет разработчику системы определять дополнительные тэги. С непроверяющим парсером (который обсуждали в Части 1 этой серии), вы, конечно, имеете такую возможность. Вы можете придумать любой тэг, какой захотите и манипулировать открывающими и закрывающими тэгами произвольно длинно, не перекрывая их с нарушением правил — непроверяющий SAX парсер проанализирует документ без проблем. Например, непроверяющий SAX парсер будет корректно анализировать и срабатывать на события документа в Листинге 1.

Правильно оформленный лишённый смысла документ

001 <?xml version="1.0">
002 <Art CENTURY="20">
003 <Dada>

004 <Author CENTURY="18" NOMDEPLUME="Voltaire">
005 Franзois-Marie Arouet
006 </Author>
007 <Tree SPECIES="Maple">
008 <Yes/>
009 <Book AUTHOR="Musashi, Miyamoto">
010 <Title LANG="English">The Book of Five Rings</Title>
011 <Title LANG="Nihongo">Go Rin No Sho</Title>
012 <Filter POLY="Chebyshev" POLES="2"/>
013 <Title LANG="Espanol">El Libro de Cinco Anillos</Title>
014 <Title LANG="Francais">Le Livre de Cinq Bagues</Title>
015 </Book>
016 <Bahrain FORMAT="MP3">
017 <Cathedral CITTA="Firenze">
018 <Nome>Santa Maria del Fiore</Nome>
019 <Architetto>Brunelleschi, Filippo (1377-1466)</Architetto>
020 <Ora FORMAT="DMY24">22032000134591</Ora>
021 </Cathedral>
022 </Bahrain>
023 <Phobias>
024 <Herbs NAME="Ma Huang"/>
025 <Appliance COLOR="Harvest Gold">Yuck</Appliance>
026 </Phobias>
027 </Tree>
028 </Dada>
029 </Art>

Непроверяющий SAX парсер сгенерирует адекватный поток событий для документа в Листинге 1, потому что входящий документ правильно составлен. Правда, действительно глупый документ, но он правильно составлен. Каждый открывающий тэг имеет соответствующий закрывающий тэг, и тэги не нахлёстываются (имеется в виду отсутствие комбинаций тэгов, подобных <A><B></A></B>). Таким образом, у непроверяющего парсера не будет проблем с Листингом 1.

К сожалению, если вы пишете программу, которая, например, обозревает музейные коллекции, форматирует архитектурную информацию или распечатывает каталоги многоязыковых карт для библиотек, ваша программа может считывать этот действительно глупый XML и генерировать действительно глупый выход, потому что он выходит за пределы известных тэгов (подобных <Data>, <Cathedral> или <Book>). Как говорится, "Мусор получил, мусор выдал".

Чтобы минимизировать возможность того, что ваша программа произведёт мусор, вы должны продумать способ определения и отбраковки мусора на входе. Затем входные данные, имеющие смысл, вы можете преобразовать в корректные выходные данные.

Предполагается, что документ имеет три уровня корректности: лексический, синтаксический и семантический. Лексическую корректность я подразумеваю, когда говорю "хорошо сформированный": основная структура документа разумна и корректна, но содержимое тэгов не проверяется. Любой тэг может содержаться внутри любого другого тэга любое число раз, любой тэг может иметь произвольные атрибуты и атрибуты могут принимать любые значения. Таким образом, Листинг 1 хорошо сформирован, но не имеет смысла, потому что нет контроля над тем, когда и где тэги и атрибуты появляются в структуре.

Синтаксическая корректность означает, что документ не только хорошо сформирован, но также содержит определённые тэги в определённых комбинациях. XML документ может включать раздел, называемый определением типа документа (DTD), который определяет правила синтаксической корректности.

позволяет разработчику системы создавать индивидуальный язык разметки, диалект XML. DTD показывает, какие тэги могут (или должны) включаться в другие указанные тэги, какие атрибуты может иметь тэг, требуемый порядок тэгов и т.д. Проверяющий парсер использует DTD, чтобы проверить анализируемый документ на синтаксическую корректность. Парсер сообщает об ошибке и выводит предупреждающее сообщение для любой найденной ошибки и затем отбраковывает любой документ, который не соответствует DTD. Прикладной программист может затем написать код, полагая что структура документа правильная, потому что парсер уже проверил её.

Так например, в Листинге 1 проектировщик может написать DTD, который определяет тэг <Book> как содержащий только один или более тэгов <Title>. Парсер выдаст сообщение об ошибке в строке 12, где присутствует тэг <Filter>, потому что DTD не позволяет это.

DTD является также блестящим способом точно определять входные данные для вашей программы. Входной XML документ или соответствует конкретному DTD, или нет. Ваша программа может корректно преобразовать входные данные, которые согласованы с данным DTD. DTD также позволяет вам тестировать ваше приложение на корректность или полноту; если входной документ соответствует DTD, но ваша программа не обрабатывает его должным образом, то вы имеете ошибку или пропущенную особенность.

XML парсеры немного могут предложить для проверки семантической корректности. Семантическая корректность означает, что фактический экземпляр данных подходит для целей приложения. Проверяющий парсер может выдать ошибку, когда он находит атрибут FORMAT в тэге <Bahrain> (как в строке 16 Листинга 1). Но задача проверить, находится ли Собор Святой Марии дель Фьёре в Бахрэйне или Италии сложна для любого парсера. Семантическая корректность остаётся в сфере вашего приложения: это от вас зависит, что добавить в значение определённого вами XML документа. Проверяющий XML парсер и DTD помогают автоматизировать определение массы лексических и синтаксических ошибок на входе вашей программы, позволяя вам сфокусироваться на значении данных.

С другой стороны, HTML использует для создания Web страниц специализирован SGML DTD, которое гораздо более сложная и мощная, чем XML DTD. XML DTD по существу являются подмножеством этих SGML DTD с нескольками небольшими отличиями в нотации. HTML DTD отчётливо предписывает, какой тип входных данных может принять программа, обрабатывающая HTML. XHTML, XML-совместимая версия HTML, определяет XML DTD для HTML. Только что появился релиз World Wide Web Consortium (W3C) о нём.

В следующий главе я создам DTD для маленького диалекта XML для описания рецептов.

Parlez-vous DTD?

Два человека как правило не могут общаться друг с другом без того, чтобы они говорили на обоюдопонятном языке. Аналогично, две программы не могут взаимодействовать с помощью XML без того, чтобы согласовать используемый язык XML. DTD определяет множество правил для допустимых в XML документе тэгов и атрибутов, порядок и число вложений тэгов. Программы, использующие DTD должны быть согласовать, что означают тэги(опять семантика), но DTD определяет слова (или тэги) и правила грамматики для данного XML диалекта.

Листинг 2 показывает простой DTD для крошечного XML языка, который я назвал XML рецептов.

Листинг 2. DTD для XML рецептов.

001 <!ELEMENT Recipe (Name, Description?, Ingredients?, Instructions?)>
002
003 <!ELEMENT Name (#PCDATA)>
004
005 <!ELEMENT Description (#PCDATA)>
006
007 <!ELEMENT Ingredients (Ingredient)*>
008
009 <!ELEMENT Ingredient (Qty, Item)>
010 <!ATTLIST Ingredient
011 vegetarian CDATA "true">
012
013 <!ELEMENT Qty (#PCDATA)>
014 <!ATTLIST Qty
015 unit CDATA #IMPLIED>
016
017 <!ELEMENT Item (#PCDATA)>
018 <!ATTLIST Item
019 optional CDATA "0">
020
021 <!ELEMENT Instructions (Step)+>
022 <!ELEMENT Step (#PCDATA)>

DTD в Листинге 2 определяет законченный крошечный язык для передачи рецептов. Программы, использующие этот DTD, могут рассчитывать на структуру соответствующих файлов, чтобы согласовывать правила в DTD.

Я разберу этот файл строка за строкой:

001 <!ELEMENT Recipe (Name, Description?, Ingredients?, Instructions?)>

Эта строка определяет использование тэга <!ELEMENT. Целая строка от открытия <!ELEMENT до закрытия > называется определением типа элемента (element type declaration). Это определение говорит, что Recipe составлен из Name, следующими за ним необязательными Description, Ingredients, и Instructions. Оператор запятая (,) показывает допустимые тэги, который может содержать определяемый тэг и порядок, в котором эти тэги должны появляться. Оператор вопросительный знак (?) показывает, что элемент слева от него необязателен. Так как Nameимеет только оператор запятая после себя, Recipe должен иметь в точности один Name. Круглые скобки предназначены для группирования и не появляются во входном документе.

Поэтому выражение:

<Recipe><Name>Zabaglione</Name></Recipe>

правильный рецепт, так как он соответствует DTD (то есть, содержит <Name> и необязательно <Description> после.) Хотя:

<Recipe>
   <Description>Italian dessert</Description>
   <Name>Zabaglione</Name>
</Recipe>

неправильный рецепт (Recipe), потому что Description идёт до Name.

003 <!ELEMENT Name (#PCDATA)>

Эта строка утверждает, что тэг(или элемент) Nameне содержит тэгов других типов и может содержать текст между открывающим и закрывающим тэгами. Проверяющий парсер отметит любой тэг внутри тэга Name как ошибку.

007 <!ELEMENT Ingredients (Ingredient)*>

Эта строка утверждает, что тэг Ingredients может содержать произвольное число тэгов Ingredient. Оператор астериск или звёздочка (*) показывает, что тэгов содержится сколько угодно.

010 <!ATTLIST Ingredient
011 vegetarian CDATA "true">

Это объявление списка атрибутов, которое использует <!ATTLIST, определяет атрибуты для тэга. У тэга позволительны только атрибуты, определённые внутри объявления списка атрибутов. Эта строка утверждает, что предварительно определённый тэг Ingredient имеет единственный атрибут, vegetarian, определяющий символьные данные (CDATA), значение по умолчанию "true". Объявление списка атрибутов подобно этому примеру; один может определять многие атрибуты, каждый с типом и значением по умолчанию, следующие после названия тэга.

014 <!ATTLIST Qty
015 unit CDATA #IMPLIED>

Это объявление списка атрибутов определяет значение по умолчанию для атрибута unit как #IMPLIED. Это значит, что атрибут может появляться или не появляться: если оно не появляется, то приложение передаёт значение. Так создаются необязательные атрибуты.

021 <!ELEMENT Instructions (Step)+>

Эта строка утверждает, что тэг Instructions если он присутствует, должен содержать, по крайней мере, один Step. Оператор плюс (+) показывает появление элемента одно или более.

DTD имеет больше операторов и условий, но этот пример охватывает простые. (Вы можете узнать более подробно о DTD в XML в XML рекомендации; см. Ресурсы.)

DTD — это метаинформация, это информация об информации. Может быть, вы уже знакомы с этой концепцией. Таблица в реляционной базе данных имеет схему, описывающую такие вещи, как имена столбцов, типы данных, размеры и значения по умолчанию полей. Но описание таблицы не содержит значений данных, оно содержит описание этих значений. Подобно этому, DTD — простой вид схемы, которая определяет, каким конкретно может быть тип документа. (Это теперь разрабатываемые пути по созданию схемы XML, которая гораздо больше похожа на схему базы данных; см. Ресурсы.)

DTD также немного походят на BNF, формы Бэкуса-Наура (см. Ресурсы для дискуссии), которая описывает правила преобразования для грамматик; хотя, BNF может включать структуры, которые XML DTD включать не могут.

XML документ декларирует своё DTD с помощью декларации <!DOCTYPE как показано в Листинге 3. Тип документа определяется внешним используемым для проверки документа. Тэг высшего порядка документа должен быть таким же, как документ, определённый <!DOCTYPE (в нашем случае это Recipe.)

Листинг 3. Использование внешней декларации DOCTYPE

001 <?xml version="1.0"?>
002
003 <!DOCTYPE Recipe SYSTEM "example.dtd">
004
005 <Recipe>
006 <Name>Lime Jell-O Marshmallow 
	Cottage Cheese Surprise</Name>
007 <Description>My grandma's favorite 
	(may she rest in peace.)</Description>
008 <Ingredients>
...<!-- and so on... -->

Строка 3 в Листинге 3 утверждает, что документ должен соответствовать содержащемуся в данном файле DTD или файл синтаксически неправилен.

DTD может быть также определено внутри,как показано в Листинге 4. Отметим, что в Листинге 4 DTD заканчивается ]> в строке 19.

Листинг 4. Внутреннее объявление типа документа

001 <?xml version="1.0"?>
002
003 <!DOCTYPE Recipe [
004 <!ELEMENT Recipe (Name, Description?, Ingredients?, Instructions?)>
005 <!ELEMENT Name (#PCDATA)>
006 <!ELEMENT Description (#PCDATA)>
007 <!ELEMENT Ingredients (Ingredient)*>
008 <!ELEMENT Ingredient (Qty, Item)>
009 <!ATTLIST Ingredient
010 vegetarian CDATA "true">
011 <!ELEMENT Qty (#PCDATA)>
012 <!ATTLIST Qty
013 unit CDATA #IMPLIED>
014 <!ELEMENT Item (#PCDATA)>
015 <!ATTLIST Item
016 optional CDATA "0">
017 <!ELEMENT Instructions (Step)+>
018 <!ELEMENT Step (#PCDATA)>
019 ]>
020 <Recipe>
021 <Name>Lime Jell-O Marshmallow 
	Cottage Cheese Surprise</Name>
022 <Description>My grandma's favorite 
	(may she rest in peace.)</Description>
...<!-- and so on... -->

Полные версии этих простых файлов, которые я буду использовать в дальнейшем, включены в архив исходников для этой статьи. Скачайте файлы исходников из Ресурсов и экспериментируйте с классом SimpleValidatingSaxReporter, который создает проверяющий SAX парсер и затем анализирует и проверяет документ по DTD. Основная программа с этим классом появляется в Листинге 5.

Листинг 5. Использование проверяющего SAX парсера

083 public static void main(String args[]) {
084 SimpleValidatingSaxReporter ssr = 
	new SimpleValidatingSaxReporter();
085 try {
086 ssr.parseDocument(true, ssr, args[0]);
087 } catch (Exception ex) {
088 System.err.println(ex);
089 }
090 }
...
099 protected void parseDocument(boolean isValidating, 
	HandlerBase handler, String sFilename) {
100 try {
101 // Get a "parser factory", an object that creates parsers
102 SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
103
104 // Set up the factory to create the appropriate type of parser
105 saxParserFactory.setValidating(isValidating);
106 saxParserFactory.setNamespaceAware(false); // Не этот месяц...
107
108 SAXParser parser = saxParserFactory.newSAXParser();
109
110 parser.parse(new File(sFilename), handler);
111 } catch (Exception ex) {
112 System.err.println("Exception: " + ex);
113 System.exit(2);
114 }
115 }

Строка 102 в Листинге 5 создает SAXParserFactory(фабрику парсеров), объект, который производит парсеры. Строки 105 и 106 говорят фабрике парсеров какой тип парсера создать и строка 108 создаёт парсер. Затем строка 110 говорит парсеру анализировать файл, используя обработчик из main() для управления событиями. Обработчик — сам SimpleValidatingSaxReporter, так как этот класс включает HandlerBase. Результат потока событий SAX настолько длинный, насколько правильные входные данные в соответствии с DTD. Эксперимент с кодом добавления и удаления элементов из простых XML файлов в архиве источников.

Помните, что в example2.xml есть ошибки и парсер сообщает:

e: file:C:/mj-java/XMLSAX2/example2.xml: line 30: 
org.xml.sax.SAXParseException: Element "Ingredient" 
does not allow "Item" here.

Парсер распознаёт, что порядок тэгов Qty и Item нарушен. Если вы удалите из example2.xml декларацию <!DOCTYPE, то получите следующее сообщение об ошибке, выданное обработчиком ошибок:

w: file:C:/mj-java/XMLSAX2/example2.xml: line 6: 
org.xml.sax.SAXParseException: 
Valid documents must have a <!DOCTYPE declaration.
e: file:C:/mj-java/XMLSAX2/example2.xml: line 6: 
org.xml.sax.SAXParseException: 
Element type "Recipe" is not declared.

Он выдаёт это сообщение об ошибке, потому что не указано DTD, так что нельзя найти определение тэга Recipe. Манипулируйте над этим классом, чтобы почувствовать, какие типы ошибок может выловить парсер.

Отметим, что используемый вами экземпляр ErrorHandler попросту докладывает об ошибках и не прекращает работу, когда получает их, парсер продолжает пытаться анализировать файл. Кто бы ни писал обработчик ошибок (например, вы), он ответственный за принятие решений, когда возникает ошибка.

Сейчас вы наконец знаете достаточно, как использовать SAX и проверяющий парсер, чтобы создавать XML приложение. Я решил сделать процесс легче для себя, так я создал LAX, который раскрою ниже.

LAX: Lazy(Ленивый) API для XML

Написание обработчика документа для SAX парсера довольно легко: просто переписать подкласс HandlerBase, связанные методы и делать что вы пожелаете в ответ на события, приходящие от парсера. Будучи ленивым и поэтому виртуозным программистом (см Часть 1 для объяснения), я решил сделать небольшую дополнительную работу только один раз, чтобы упростить программирование в SAX для дальнейших проектов. Просто Написание SAX обработчиков тоже большая работа.

Видите, когда вы переписываете startElement(), endElement или characters(), вы всегда проверяете название тэга, чтобы решить что делать. Таким образом, эти методы обычно становятся большими если-то-иначе блоками. Это требует набора большого объёма текста, что влечёт ошибки, к тому же мне просто надоедает это делать. Так я создал LAX, Lazy(Ленивый) API для XML.

LAX позволяет любому классу использовать только именованные условия для управления событий SAX, во многом схожим способом JavaBeans определяют множество свойств и событий, рассматривая подпись метода. Класс может стать обработчиком события просто определяя методы со связанным названием и подписью. Нет надобности перекрывать класс HandlerBase, так как LAX делает это за вас. Чтобы просто создать объект LAX, представьте его для парсера в качестве обработчика документа, затем свяжите ваш обработчик объектов с LAX. LAX переводит поток событий XML в вызовы методов ваших объектов.

LAX использует представление Java для нахождения методов в ваших классах, чтобы управлять разобранными XML тэгами. Когда LAX'у встретится открывающий тэг, называемый, скажем, <Tag>, он ищет во всех его обработчиках (экземпляров наисанных вами классов) объекты, которые имеют такой же метод void startTag() или void startTag(AttributeList list), и вызывает метод любого найденного объекта. Когда он встречает закрывающий тэг </Tag>, он ищет во всеч обработчиках метод, называемый void endTag(), и вызывает любой такой найденный метод. Когда LAX'у встречаются символы (в его собственном методе characters()), он запоминает текующий тэг, ищет и вызывает все методы с подписью void textOfTag(String string).

Как результат, вам не нужно писать гигантские если-то-иначе выражения, используя DocumentHandler или расширенный HandlerBase. Просто пишите методы со связанными подписями, связывайте экземпляры ваших классов с LAX и анализируйте входной документ при помощи объекта LAX как обработчика документа. Что может быть проще?

Исходник для LAX в архиве источников для этой статьи, доступный в Ресурсах ниже. Сейчас я буду разрабатывать программу, использующую LAX.

Управление рецептами с LAX.

Часто я получаю почту от читателей, кто хочет знать, как использовать Java для преобразования XML в HTML (или другие форматы) для отображения. Следующий пример показывает одну из возможностей использования XML, чтобы создавать HTML файлы для различных целей с различным форматированием одним проходом разбора. Против популярных CGI, ASP(Active Server Pages) и JSP(JavaServer Pages) я противопоставляю написание любого кода, который имеет жёстко установленное описание, как HTML. Языки стилей такие как CSS(Cascading Style Sheets), DSSSL(Document Style Semantics and Specification Language, язык стилей SGML) и XSL(Extensible Stylesheet Language) больше подходят для трансформации данных в какое-то представление(Почему это так — отдельная большая тема). Однако я понимаю, что программный код для создания HTML используется часто и это проливает свет на пример использования SAX для переформатирования XML в другую полезную форму.

Для этого примера у меня есть два правильных файла Recipe XML: example4.xml, мой стандартный ужасный Jell-O; и новый рецепт для Nanner Pah, example3.xml, сильный удар по всем обедам в Лютеранской церкви, на которые я ходил будучи ребёнком.

Я решил, что хочу использовать LAX, чтобы написать программу, которая генерирует два файла: хорошо отформатированную страницу для поваренной книги и список необходимых покупок для рецепта, также привлекательно отформатированный. Чтобы довести это до конца, я создал два класса: RecipeWriter и ShoppingListWriter. Я детально рассмотрю каждый класс и затем покажу, как вы можете использовать их оба с LAX.

Форматирование рецепта

Класс RecipeWriter имеет начало, конец и textOf methods для каждого типа тэгов в Recipe DTD. А сейчас обсудим, как они работают, так что вы можете почувствовать, что делает каждый класс. Вы можете ознакомиться непосредственно с исходным кодом для RecipeWriter.java.

Конструктор RecipeWriter, который в качестве аргумента берёт имя файла, создаёт файл и открывает его для чтения. Следующий метод вызывает запись HTML в выходной файл и в конечном счёте endRecipe() закрывает файл.

Тэг верхнего уровня в Recipe XML <Recipe>, но RecipeWriter не имеет метода startRecipe() так что событие будет пропущено. Когда LAX встречает символы внутри тэга <Name>, он ищет метод textOfName() RecipeWriter'а который вызывается с текстом имени рецепта. textOfName() вызывает titlePrint(), который устанавливает HTML страницу, устанавливает фоновое изображение и открывает TABLE (которая будет закрыта endRecipe()). startDescription(), startIngredients() и startInstructions() все продуцируют строки в таблицу со связанными цветами фона и большим текстом заголовка.

Эта поваренная книга спроектирована, чтобы использоваться и вегетарианцами и невегетарианцами, так отметим, что RecipeWriter имеет булевскую переменную, названную _isVegetarian, которая установлена на "false", если присутствует какой-нибудь невегетарианский компонент, встреченный startIngredients. После завершения анализа endRecipe()проверяет этот флаг и ставит указание возле рецепта, если рецепт вегетарианский. Подобно этому, startItem проверяет необязательный OPTIONAL атрибут и пишет "(optional)" после каждого необязательного ингридиента.

Можете посмотреть результаты выполнения RecipeWriter в example4.xml в example4-recipe.html и example3.xml в example3-recipe.html.

Форматирование списка покупок

В то же время, как рецепт отформатирован RecipeWriter, LAX также поддерживает экземпляр ShoppingListWriter, который создаёт другой файл. Можете самостоятельно ознакомиться с исходный кодом для ShoppingListWriter.java.

Подобно RecipeWriter, ShoppingListWriterсоздаёт и открывает свой выходной файл в своём конструкторе. Так как список покупок преимущественно имеет отношение к <Ingredients>, он не пишется до тех пор, пока LAX не вызовет startIngredients() (startName() сохраняет имя в поле экземпляра для использования в startIngredients). Программа строит HTML таблицу наверху фона и пишет все необязательные атрибуты красным (так если у вас не ъватает денег для полной покупки, вы будете знать без чего можно обойтись).

Можете видеть результаты ShoppingListWriter в example4-list.html и example3-list.html. Сейчас вы не можете объединить и отсортировать эти два списка — n рецептов дают вам nсписков. Но нет причины, в силу которой вы не можете написать класс, который делает часть или все эти действия.

Главная программа LAX

Листинг 6 показывает метод main()для LAX. Вы можете посмотреть полный исходный текст в Lax.java.

Листинг 6. Метод main() LAX

133 public static void main(String args[]) {
134 if (args.length < 1) {
135 System.err.println("Usage: 
	lax inputFile.xml [parserClass]");
136 System.exit(1);
137 }
138
139 String sInputFile = args[0];
140 String sRecipeFile;
141 String sShoppingListFile;
142 String sBase = sInputFile;
143
144 if (sBase.length() > 4 && 
	sBase.toLowerCase().endsWith(".xml")) {
145 sBase = sBase.substring(0, sBase.length() — 4);
146 } else {
147 sInputFile = sBase + ".xml";
148 }
149 sRecipeFile = sBase + "-recipe.html";
150 sShoppingListFile = sBase + "-list.html";
151
152 Lax lax = new Lax();
153
154 ShoppingListWriter slw = 
	new ShoppingListWriter(sShoppingListFile);
155 lax.addHandler(slw);
156
157 RecipeWriter rw = new RecipeWriter(sRecipeFile);
158 lax.addHandler(rw);
159
160 lax.parseDocument(true, lax, sInputFile);
161
162
163 }

Строки со 139 по 150 производят имена выходных файлов, которые используются для создания ShoppingListWriter (строка 154) и RecipeWriter (строка 157) для LAX. Строка 152 создаёт экземпляр LAX, и в строках 155 и 158 LAX получает объекты ShoppingListWriter и RecipeWriter Затем строка 160 анализирует файл, а копия parseDocument() берётся непосредственно из SimpleValidatingSaxParser.

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

Использование XML и SAX таким образом открывает массу возможностей. Немного пофантазировав, просто нарисовать с своём воображении сервлет, который читает директорию файлов XML и создаёт страницы ссылок для форматирования рецептов и списков покупок. Форматрированные рецепты и списки покупок могут быть даже созданы на лету сервлетом из XML. Дополненный новой информацией файл XML затем может автоматически обновлять рецепт и список покупок — они никогда не будут асинхронными. Эта устойчивость данных — одно из преимуществ испольщования XML для представления информации и затем форматирование XML стилями для различных представлений.

Заключительные замечания про XML и DTD

Заметим, что в примере с рецептими содержимое рецептов было отделено от его представления; XML представляет информацию в рецепте, пока классы обработчика LAX форматируют и отображают информацию. Другой класс LAX может отображать документ совершенно другим способом или даже читать его вслух, пока лежащий в основе XML документ не изменится полностью. Это отделение содержимого от представления — одна из ключевых особенностей архитектуры документообрабатывающих систем.

В дополнение, пока идёт создание, DTD может выглядеть, как простое предположение, это действительно одна из сложных составляющих создания больших интегрированных систем управления документооборота. Синтаксис DTD не так сложен и вы можете привыкнуть к нему, но получение правильного DTD требует длительного анализа и часто следствием плохого проектирования DTD бывает крах проекта. Проектирование DTD имеет много общего с проектированием базы данных, особенно в терминах нормальных форм представления информации.

DTD чрезвычайно важны для описания стандартов форматов документов для обмена информацией между открытыми системами. Много групп и консорций сейчас работают над XML DTD для векторной графики к химическим формулам и обувным деталям.

Заключение

Эта статья покрывает много материала: вы узнали, что такое DTD и как с ним работать, вы открыли LAX (который, я думаю, делает SAX для вас даже проще в использовании) и вы увидели, как вы можете использовать только XML документ в разных контекстах. Надеюсь, эти маленькие примеры дали вам пищу для размышлений про то, как вы можете использовать эту технологию.

SAX — блестящий способ использовать XML во многих приложений, но более сложные преобразования данных XML совершенно необходимы для получения из узлов всего "дерева документа". Вы можете также использовать XML как преобразующий механизм; то есть для создания документов, которые представляют произвольные структуры объектов и переделывают их в объектные структуры. Для этих целей может быть полезна Объектная Модель Документа (Document Object Model (DOM)). В следующей статье этой серии вы узнаете, как использовать DOM для более сложного преобразования XML документов.

Об авторе

Mark Johnson работает в Ft. Collins, Colo., как архитектор и разработчик Velocity днем, и как писатель JavaWorld ночью – очень поздней ночью.

Ресурсы

Скачайте исходники для этой статьи в одном из форматов:

Reprinted with permission from the April 2000 edition of JavaWorld magazine. Copyright © ITworld.com, Inc., an IDG Communications company.
View the original article at: http://www.javaworld.com/javaworld/ jw-04-2000/jw-0407-advsax_p.html

[an error occurred while processing this directive]
[an error occurred while processing this directive] Перевод на русский © Николай Малеванный, 2000
< Вернуться на caйт :: Copyright © 1999 — 2010, IT • archiv.