| Каталог | Индекс раздела |
©IBM deweloperWork
© А.С.Деревянко (перевод)
В этом продвинутом учебнике рассматриваются более сложные темы манипулирования XML-документами при помощи технологии Java. Автор, Doug Tidwell, показываеит вам, как выполнять такие задачи, как генерация структур данных XML, проверка правильности XML-документов, работа с пространствами имен и интерфейс XML-парсеров с не-XML источниками данных. Как вы и ожидаете, все примеры базируются на открытых стандартах.
В предыдущем учебнике ("XML-программирование в технологии Java. Часть 1"), я показал вам основы разбора XML в языке Java. Я рассмотрел главные API (DOM, SAX и JDOM) и провел вас через несколько примеров, которые демонстрировали основные задачи, общие для большинства XML-приложений. Этот учебник рассмотрит более трудные вещи, которые не рассматривались раньше, такие как:
Как и во вводном учебнике, рассматриваемыми API являются:
Я также рассмотрю несколько подходов к проверке правильности, включая схему XML W3C, RELAX NG и Schematron.
Большинство примеров здесь будут работать с сонетом Шекспира, который появился в предыдущем учебнике. Структура сонета такая:
В различных программных примерах некоторые версии этого документа будут иметь пространства имен, а некоторые будут использовать DTD, схемы XML W3C или других языков схемы Schemas для проверки правильности. Полные примеры см. в следующих файлах:
Как альтернатива, вы можете выгрузить x-java2_code_files.zip и посмотреть эти файлы в текстовом редакторе.
Вам следует установить несколько вещей на вашей машине прежде, чем вы сможете выполнять примеры. (Предполагается, что вы знаете, как компилировать и запускать Java-программы, и что вы знаете, как установить переменную По мере того, как XML становился более изощренным, парсеры должны были тоже становиться более изощренными. И DOM, и SAX, и JDOM определяют наборы свойств парсера.
Некоторые свойства обязательны, некоторые - не обязательны. Каждый из трех API предоставляет похожие методы и исключения для получения и установки свойств парсера.
Например, посмотрим на SAX. API SAX сам определяет методы получения и установки свойств парсера в интерфейсе В дополнение к методам Класс Я опустил одну деталь о методах установки свойств SAX Стандарт SAX 2.0 требует, чтобы парсер распознавал такие два свойства.
Еще одно замечание по поводу этих двух обязательных свойств: от парсера требуется обеспечивать для них методы Чтобы помочь вам избежать Вы можете найти список URIвсех определенных свойств на
saxproject.org/apidoc/org/xml/sax/package-summary.html.
Многие из свойств парсера имеют дело с сущностями и с тем, как они обрабатываются парсером. Я включаю сюда краткий обзор сущностей на случай, если вы не знакомы с ними. В общем случае сущность определяют строку, которая заменяется чем-то еще, когда XML-документ обрабатывается. Здесь перечислены различные типы сущностей.
Общие и параметрические сущности. Общая сущность определяет подстановку, которая используется внутри XML-документа. Параметрическая сущность, с другой стороны, появляется только в DTD и определяет подстановку, которая может использоваться только внутри DTD. (Подробнее о DTD позже.) Общая сущность выглядит так:
При использовании этой сущности строка В этом примере параметрическая сущность basic связана с определенной строкой.
Предположим, есть элементы HTML, где вы используете параметрическую сущность Внутренние и внешние сущности. Внутренняя сущность определяется в XML-файле; внешняя сущность определяется в другом (внешнем) файле. Внешний файл - по большей части другой XML-файл, но он может быть чем-то еще(больше об этом - через минуту). Первая строка здесь определяет внутреннюю сущность, тогда как вторая определяет внешнюю сущность при помощи использования ключевого слова При использовании этого примера строка Предопределенные сущности. Стандарт XML определяет пять сущностей, которые доступны всегда. Это сущности для знака меньше-чем ( Кроме приведенных выше свойств, часто используются следующие свойства:
Когда XML был впервые анонсирован, разочарованные разработчики HTML были возбуждены перспективой создания своих собственных тегов. В конце концов, они все могут создавать теги для описания своих данных вместо того, чтобы приводить свои данные в соответствие ограниченным структурам HTML.
Когда начальная эйфория угасла, стало ясно, что не все так просто, рано или поздно две или более группы определят один и тот же тег. Например, я запускаю онлайновый книжный магазин, я использую тег Чтобы создать заказ на книгу в XML, для меня совершенно разумным будет использовать мои теги для описания заказываемых книг, и совершенно разумным для вас будет использовать ваши теги для описания адреса доставки заказа. Очевидная проблема: как мы различим эти два тега Концептуально пространство имен работает как оператор Java Пространство имен имеет две части: префикс и уникальную строку. Вот фрагмент документа, ктороый использует пространства имен:
Этот документ определяет два пространства имен. Префикс Когда вы определяете пространство имен для данного элемента, это пространство имен может быть использовано этим элементом и любым элементом внутри него. В предыдущем примере я определил все пространства имен, которые я использовал, в корне документа. Я мог определить пространство имен Если бы я хотел печатать как можно больше, я мог бы переопределить пространство имен в каждом отдельном элементе, который использует ее:
Когда используется префикс пространства имен, пространство имен, связанное с префиксом, должно быть определено в этом элементе или в одном из его предков. Определение всех пространств имен в корневом элементе упрощает и укорачивает документ.
Последним приемом является использование атрибута Через минуту я покажу вам версию XML-сонета, квалифицированную пространствами имен. Если вы хотите, вы можете использовать пространство имен по умолчанию для элемента Поскольку никакой из этих элементов не имеет префикса пространства имен, учитывающий пространство имен парсер будет рассматривать все эти элементы как относящиеся к пространству имен Иногда вы хотите проверить значение пространства имен. Например в таблицах стилей XSLT все элементы таблиц стилей должны быть из пространства имен тогда как такой элемент XSLT - нет:
Во втором примере префикс пространства имен тот, какой вы ожидаете, но строка пространства имен неправильная. Последний пример, этот элемент XSLT является правильным:
Здесь префикс не традиционный Наиболее сбивает с толку в пространствах имен то, уникальная строка не используется как URL (да, она так выглядит, но это не так). Общей практикой для групп является использование имен их доменов, чтобы гарантировать уникальность строки. Вы можете подумать (как я сначала думал), что соединенный с Internet XML-парсер будет выгружать DTD или схему с этого URL, но он этого не делает.
Хотя стандарт пространств имен в XML определяет уникальную строку как URI
(Universal Resource Identifier - см. Ресурсы), это на самом деле только строка. Если у вас есть трудности в понимании того, что уникальная строка не является URL, и что XML-парсер никогда не будет использовать эту строку как URL, приготовьтесь. Если вы имеете какие-либо сомнения или если вы все еще сбиты с толку, см. Получение информации пространства имен от парсера.
Даже хотя строка пространства имен не является URL, иногда, если вы введете эту строку в вашем браузере, вы найдете полезную информацию о наборе тегов или типе документа. Это совершенно не обязательно и предназначено для человеческого восприятия, а не для машин. Например, пространство имен Если другие люди захотят использовать элементы, которые определены вами, хорошей идеей будет обеспечить такую информацию на случай, если кто-то захочет загрузить URI пространства имен.
Теперь, когда вы увидели основы пространств имен, я рассмотрю DOM, SAX и
JDOM, чтобы показать, как они учитывают информацию пространства имен. Все эти примеры используют следующую модифицированную версию сонета:
При работе с пространствами имен вам нужно знать несколько порций информации для каждого элемента, квалифицированного пространством имен. Как пример, рассмотрим ниже элемент Локальное (неквалифицированное) имя элемента Префикс пространства имен Квалифицированное имя элемента URI пространства имен Теперь я покажу каждый API, выставляющий эту информацию.
Поддержка пространств имен была добавлена в Объектную Модель Документа в DOM Уровня 2.
Вот список составляющих информации вместе с методами DOM, которые дают вам эту информацию.
Локальное (неквалифицированное) имя элемента Префикс пространства имен Квалифицированное имя элемента URI пространства имен Заметьте, что все эти методы являются частью интерфейса Теперь давайте взглянем на Первым вашим шагом является создание учитывающего пространство имен парсера. Для большинства парсеров учет пространства имен по умолчанию выключен. Поскольку вы используете JAXP, вам нужно установить свойство вашей Используйте метод JAXP Метод Чтобы обрабатывать элементные узлы, следуйте этим трем шагам:
Для обработки узла атрибута просто печатайте подробности о нем. Заметьте, что Когда вы запустите Результат показывает подробности о любых элементах и атрибутах в исходном документе, квалифицированных пространством имен. В части вывода, показанной здесь перечислены атрибут Примечание: В DOM, все атрибуты объявления пространства имен по определению привязаны к пространству имен URI: Другими словами, любой атрибут, который определяет пространство имен ( Полный исходный код см. в DomNS.java.
Чтобы завершить обсуждение DOM и пространств имен, приведем список всех методов DOM, учитывающих пространство имен. За именем каждого метода следует его краткое описание; все подробности о методах и их работе ищите в документации DOM, которую вы получите вместе со своим парсером.
Примечание: Все методы, определенные в интерфейсе Как и в случае с DOM, первый релиз SAX не учитывал пространства имен. В SAX 2 учет пространств имен добавлен через многие методы, я их здесь рассмотрю. Прежде всего, взглянем на то, как вы получаете ваши информационные составляющие:
Локальное (неквалифицированное) имя элемента Префикс пространства имен Квалифицированное имя элемента URI пространства имен Как и следовало ожидать, информация пространства имен обеспечивается через различные события, особенно события Теперь посмотрим на Используйте метод JAXP В Как и следовало ожидать, этот код очень похож не код Когда вы запустите По большей части результат Полный исходный код см. в SaxNS.java.
В обсуждении нахождения пространств имен при помощи парсера DOM (Нахождение пространств имен в дереве DOM) я указывал, что все определения пространств имен обрабатываются как атрибуты, принадлежащие пространству имен Вы должны были заметить, что в выводе Если программе необходима информация об определениях пространств имен, вы можете получить ее через отдельные события. События Одной из причин для обработки Логика здесь очень простая; когда вы получаете событие Когда вы выводите на консоль информацию пространства имен, вы можете использовать метод Полный исходный код см. в SaxNSTwo.java.
Подход, использованный в Чтобы обработать эту проблему (предполагая, что вы рассматриваете ее как проблему), вам понадобиться реализовать стек для каждого URI, проталкивая значение в стек URI по событию Если этот случай имеет для вас значение, пакет Вот как обрабатывать пространства имен, используя объект Теперь взглянем на Чтобы проиллюстрировать обработку пространств имен, вы обработаете все элементы и атрибуты, как и прежде, плюс вы перечислите пространства имен, определенные при обработке каждого элемента. Результат для нашего сонета выглядит так:
Заметьте, что префикс XML всегда определен, он отображается на строку Одним из недостатков Если метод Полный исходный код см. в SaxNSThree.java.
Прежде, чем я перейду к обработке пространства имен при помощи JDOM, вот список классов и интерфейсов SAX, учитывающих пространство имен, вместе с кратким обсуждением каждого. Как всегда, последнее слово о методах и их назначении смотрите в документaции SAX, которую вы получите вместе со своим XML-парсером.
В начале этого обсуждения взглянем на API JDOM для получения четырех основных порций информации для элемента и атрибута:
Имена этих методов прямолинейны. Чтобы проиллюстрировать, как JDOM работает с пространствами имен, я покажу вам Когда я показывал вам, как обрабатывать пространства имен при помощи DOM и SAX, я отмечал, что вы должны создать учитывающий пространство имен парсер. В JDOM пространства имен включены по умолчанию в обеспечивающем SAX-парсере, инкапсулированном в объекте Построим приложение Заметьте, что аргументом Поговорим о Я буду брать эти задачи по порядку в процессе нашего продвижения по исходному коду. Прежде всего, чтобы увидеть, квалифицирован ли текущий элемент пространством имен, проверьте URI пространства имен текущего элемента:
Далее посмотрите, определены ли в этом элементе дополнительные пространства имен. Заметьте, что JDOM обрабатывает объявления пространств имен не так, как DOM. В DOM-приложении объявление пространства имен ( Последнее замечание: если текущий элемент содержит собственное объявление пространства имен (такое, как элемент Во всех методах JDOM, которые возвращают наборы чего-то ( Вашей следующей задачей является поиск всех атрибутов текущего элемента и проверка, не квалифицированы ли какие-то из них пространством имен. Этот код использует Последняя задача - получить потомков этого Полный исходный код см. в JdomNS.java.
Когда XML впервые был введен, схемой его проверки правильности было Определение Типа Документа или DTD. DTD пришли из мира SGML и обычно рассматривали только структуру документа. Они имели ограниченную нотацию для типов данных, но ничего, подобного тому, что вы ожидаете от современного языка программирования.
Например, я могу использовать DTD, чтобы определить, что элемент <postcode> обязательно требуется для элемента <address>, и проверяющий парсер будет отрабатывать это. Любой XML-документ, который содержит <address> без <postcode>, будет помечен нак неправильный. К сожалению, в парсере, использующем DTD для проверки правильности то же случится и с <postcode>B9C 4F8</postcode>, а также и с <postcode>Mad dogs and Englishmen</postcode>. Ясно, что мир XML нуждался в более надежном языке для проверки правильности.
Чтобы восполнить недостачу, Консорциум World Wide Web Consortium (W3C) создал язык схемы XML, как попытку удовлетворить нужды сообщества XML. Схема XML (я буду называть ее так) делится на две части: типы данных и структура документа. Спецификация типов данных определяет несколько базовых типов, а также правила создания новых типов, тогда как спецификация структуры документа определяет набор тегов XML для задания того, какие элементы может содержать документ.
Определение языка разметки для описания содержимого XML-документа является устрашающей задачей и, как и следовало ожидать, не все были/есть осчастливлены схемой XML. В этом учебнике я показу вам два других языка схемы, RELAX NG и Schematron. Поддержка этих языков не такая широко распространенная, как схемы XML, но каждый из них имеет верных и активных последователей.
Одно последнее замечание по поводу DTD: они имеют отличный (и совершенно несовместимый) синтаксис от XML-документов. Это другое наследство от мира SGML. Хотя некоторые люди считают, что разные синтаксисы - это хорошо, большинство пользователей предпочитают язык проверки правильности, определенный как XML. Схема XML, RELAX NG и Schematron - все базируются на XML. Помимо прочего, это означает, что вы можете писать таблицы стилей XSLT, которые конвертируют схему в читабельный для человека документ, который объясняет правила схемы.
Прежде, чем я буду объяснять проверку правильности при помощи различных видов парсеров, взглянем на определение документа, который вы будете использовать. Прежде всего, DTD:
DTD определяет все ваши элементы и атрибуты. Синтаксис выше определяет Далее посмотрим на схему XML, которая определяет тип документа сонета. Значащие части схемы такие:
Здесь определены несколько составляющих. Во-первых, это элемент Когда заходит речь об определении структуры документа, схема XML не является единственным игроком. Другим популярным методом является RELAX NG, проект, возглавляемым признанным специалистом по XML James Clark. RELAX NG в настоящее время разрабатывается техническим сообществом OASIS (см. Ресурсы). Цитата с Web-сайта сообщества: "Целью этого TC является создание спецификации языка схемы, который является простым, легким в изучении и использует синтаксис XML".
В соответствии с целями проекта, синтаксис RELAX NG очень прост. Чтобы определить элемент, вы используете элемент Из этого простого листинга видно, элемент Элемент RELAX NG Примечание: Сообщество RELAX NG определило способ задания атрибута по умолчанию, дополнительную информацию см. в Ресурсы.
Один аспект в RELAX NG, который не так хорош, как в схеме XML, это то, что здесь нет механизма для определения кардинального числа, количества появлений элемента в определенной части вашего XML-документа. В схеме XML вы используете Полный исходный код см. в sonnet.rng.
Документы Schematron используют выражения Xpath для определения содержимого правильного XML-документа. В приведенном здесь примере элемент Schematron Этот пример показывает правило Schematron для элемента Первым ограничением здесь является то, что элемент Однако, Schematron не так удобен при определении последовательностей элементов. Это - отражение синтаксиса XPath; выражения XPath обычно определяют местоположение элемента или группы элементов. Использование XPath для определения последовательности не является приятным. Например, элемент Первое правило то, что элемент Здесь вы фактически перечисляете все комбинации элементов, появление которых допустимо в элементе Полный исходный код см. в sonnetSchematron.xml.
Теперь, когда вы увидели четыре разных способа определения содержимого правильного XML-документа, я покажу вам, как использовать эти определения. Поскольку не все инструменты поддерживают все четыре метода проверки правильности, я буду использовать следующие комбинации инструментов и приемов:
Для проверки правильности XML-документа при помощи using SAX вам нужно определить несколько свойств и для Первые два свойства включают пространства имен и проверку правильности для всех Кроме свойств JAXP, некоторые парсеры определили собственные имена свойств и значения до того, как JAXP стандартизировал их. Хотя вы можете использовать их, я этого не рекомендую. Использование свойств JAXP, как вы делаете это здесь, означает, что вы не привязываетесь к какому-то определенному парсеру.
Теперь, когда вы установили три свойства ваших объектов фабрики и парсера, вы готовы проверять правильность документа. Отредактируйте Когда вы проверите этот документ вашим проверяющим парсером, результат будет:
Полный исходный код см. в SaxValidator.java.
Процесс проверка правильности XML-документа при помощи DOM-парсера похож на проверку при помощи SAX-парсера. Я покажу вам, как создать Вот секция кода, которая устанавливает фабрику парсеров и создает парсер:
Теперь, когда вы установили вашу фабрику парсеров, вы должны сделать еще одну вещь прежде, чем вы создадите ваш парсер: определить язык схемы, который будет использовать парсер. Если If XML-документ использует DTD, вы не устанавливаете никакие специальные свойства для фабрики парсеров. С другой стороны, если документ использует схему XML, вам нужно определить свойство Используйте аргумент командной строки, чтобы определить, собираетесь вы использовать схему XML или DTD.
Теперь вы готовы создавать ваш объект-парсер. Когда парсер создан, вам нужно установить обработчик ошибок:
Простоты ради вы реализуете интерфейс обработчика ошибок в коде Полный исходный код см. в DomValidator.java.
Проверка правильности при помощи Jing относительно очевидна. Поскольку сам Jing является инструментом проверки правильности, вы можете проверять правильность XML-документа по схеме RELAX NG без написания какого-либо кода. Синтаксис командной строки таков:
Первым параметром для исполняемого jar-файла является схема RELAX NG, а вторым - файл XML-документа, который вы хотите проверять. Если ваш документ правильный, вы не получите никаких сообщений, иначе вы получите сообщение, которое скажет вам, где произошла ошибка. Например, если вы удалите элемент if you delete the Хотя это и не самое подробное сообщение на свете, оно дает вам строку и столбец, в которых документ неправильный.
Если вы хотите встроить Jing в ваш собственный код, наилучшим подходом будет использовать код из класса Schematron применяет уникальный подход к определению содержимого документов: он использует таблицы стилей XSLT. Чтобы проверить правильность XML-документа при помощи Schematron, используйте такие три шага:
Если документ правильный, преобразование не генерирует сообщений об ошибках; иначе таблица стилей выработает отчет об ошибках.
Эти три шага показаны на иллюстрации этого процесса:
Вы уже выполнили первый шаг, так что вы готовы преобразовать ваши правила документа Schematron в прикладную таблицу стилей. Чтобы сделать это вам нужно иметь файлы schematron-basic.xsl и skeleton1-5.xsl, доступные на домашней странице проекта Schematron (см. Ресурсы). Предположим, вы используете движок XSLT Xalan, применяйте такую команду для генерации вашей прикладной таблицы стилей:
Это выработает файл Если документ Один приятный аспект Schematron состоит в том, что вы определяете свои собственные сообщения об ошибках. Если вам не нравится приведенное выше сообщение, вы можете изменить его. Schematron является интересным подходом к проверке правильности. Он сейчас находится в процессе стандартизации ISO; дополнительную информацию см. на домашней странице проекта Schematron, там же вы можете выгрузить таблицы стилей, использованные в этом примере.
Doug Tidwell Одна из 13 изначальных колоний, Doug "Штат садов" Tidwell является домом для более 7 миллионов людей. Неугомонный и динамичный, он имеет необычное разнообразие от буколических холмов Трентона до урбанистических районов, относящихся к Нью-Йорку и Филадельфии. Гордый своим естественным наследием, Doug многие годы гордится таким разнообразием живой природы, как knobbed whelk (раковина штата) и восточный щегол (птица штата).
Doug также издавна известен поддержкой современного транспорта. Делавер и Хадсон были двумя исходными национальными супермагистралями, а сегодня его Turnpike является самой совершенной магистралью в стране. Его приверженность современным технологиям общеизвестна, вместе с тем он был первым штатом, который дал возможность своим жителям платить за на парковку и покупать рыболовные лицензии он-лайн. Вы можете связаться с ним по адресу dtidwell@us.ibm.com или посетить его Web-сайт, www.state.nj.us.
О примерах
<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. Получение и установка свойств парсера
Свойства парсера
XMLReader. JAXP обеспечивает те же самые методы в классах SAXParserFactory и SAXParser. Вот как работает этот код:
public static void checkFeatures()
{
try
{
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature
("http://xml.org/sax/features/namespace-prefixes",
true);
if (!spf.getFeature
("http://xml.org/sax/features/validation"))
spf.setFeature
("http://xml.org/sax/features/validation", true);
. . .
}
getFeature() и setFeature() JAXP определяет методы для работы с часто используемыми свойствами пространства имен и проверки правильности.
SAXParserFactory определяет методы setNamespaceAware() и setValidating() для установки этих свойств любого создаваемого SAXParser. Кроме того, и SAXParserFactory, и SAXParser обеспечивают методы isNamespaceAware() и isValidating().
Установка свойств парсера SAX
getFeature() и setFeature(): обработку исключений. Когда вы устанавливаете свойства парсера SAX, вы должны всегда вылавливать два исключения: SAXNotSupportedException
и SAXNotRecognizedException.
catch (SAXNotSupportedException snse)
{
System.out.println
("The feature you requested is not supported.");
}
catch (SAXNotRecognizedException snre)
{
System.out.println
("The feature you requested is not recognized.");
}
SAXNotSupportedException означает, что парсер распознает свойство, которое вы запрашиваете, но не поддерживает его, тогда как SAXNotRecognizedException означает, что парсер никогда не слыхал о свойстве, которое вы запрашиваете.
Свойства парсера SAX
http://xml.org/sax/features/namespaces
Парсер распознает пространства имен: когда это свойство - true, для всех элементов и атрибутов возможны URI пространства имен и неквалифицированные локальные имена. Любой совместимый с SAX 2.0 парсер должен поддерживать значение этого свойства по умолчанию - true.
http://xml.org/sax/features/namespace-prefixes
Парсер обеспечивает поддержку префиксов пространств имен. Если это свойство - true, для элементов и атрибутов возможны префиксы пространств имен, включая xmlns: атрибуты. Любой совместимый с SAX 2.0 парсер должен поддерживать значение этого свойства по умолчанию - false.
get, но не обязательно обеспечивать методы set.
SAXNotRecognizedException, вот список общеподдерживаемых свойств. Они не определены в стандарте SAX 2.0 (этот стандарт определяет только два обязательных свойства). SAX-парсер не обязан поддерживать или распознавать какие-либо из этих свойств, и любой парсер может на свой выбор обладать любыми свойствами из этого списка.
http://xml.org/sax/features/external-general-entities
Определяет, обрабатывает ли парсер общие внешние сущности. Это свойство не имеет значения по умолчанию, хотя, если включена проверка правильности, это свойство будет true.
http://xml.org/sax/features/external-parameter-entities
Определяет, обрабатывает ли парсер внешние параметрические сущности. Это свойство не имеет значения по умолчанию, хотя, если включена проверка правильности, это свойство будет true.
http://xml.org/sax/features/is-standalone
Это свойство определяет, содержит ли XML-декларация standalone="yes". Оно не может быть изменено, оно может быть только запрошено, и оно может быть запрошено только после события startDocument.
http://xml.org/sax/features/lexical-handler/parameter-entities
LexicalHandler парсера будет докладывать о начале и конце параметрических сущностей.
http://xml.org/sax/features/resolve-dtd-uris
Значение true показывает, что ID SYSTEM, используемые для определения деклараций, будут представлены относительно базового URI документа. false показывает, что ID будут представлены как они найдены в документе, программа может использовать метод Locator.getSystemId(), чтобы получить базовый URI документа.
http://xml.org/sax/features/string-interning
Если это свойство - true, все URI имен XML и пространства имен обрабатываются с использованием java.lang.String.intern(). Использование intern() делает сравнение строк более быстрым, чем вызов java.lang.String.equals().
http://xml.org/sax/features/use-attributes2
http://xml.org/sax/features/use-locator2
http://xml.org/sax/features/use-entity-resolver2
Значение true показывает, что парсер использует измененные интерфейсы
org.xml.sax.ext.Attributes2, org.xml.sax.ext.Locator2 или
org.xml.sax.ext.EntityResolver2, соответственно.
http://xml.org/sax/features/validation
Задает, проверяет ли парсер правильность документа. Если это свойство - true,
external-general-entities и external-parameter-entities автоматически устанавливаются в true.
http://xml.org/sax/features/xmlns-uris
Если установлено свойство namespace-prefixes, это свойство управляет тем, находятся ли декларации пространств имен в самих пространствах имен. Спецификация Пространства имен в XML устанавливает, что декларации пространств имен не должны быть в любом пространстве имен, тогда как спецификация DOM Уровня 1 помещает декларации пространств имен в пространство имен http://www.w3.org/2000/xmlns/. Если это свойство - true, декларации пространств имен находятся в пространстве имен; если это свойство - false, нет.
Кратко о сущностях
<!ENTITY corp "International Business Machines Corporation">
&corp; заменяется строкой International Business Machines Corporation, где бы она не появилась. Параметрическая сущность выглядит так:
<!DOCTYPE article [
<!ENTITY % basic "a|b|code|dl|i|ol|ul|#PCDATA">
<!ELEMENT body (%basic;)*>
%basic;, это означает, что элемент может содержать текст (#PCDATA сокращение для "разбираемые символьные данные ") или элементы <a>, <b>, <code>, <dl>, <i>, <ol> и <ul>.
Использование этой параметрической сущности в DTD может сократить объем печатанья.
SYSTEM и ссылки на внешний файл:
<!ENTITY auth "Doug Tidwell">
<!ENTITY BoD SYSTEM "http://www.ibm.com/board_of_directors.html">
&auth; будет заменена текстом Doug Tidwell, тогда как строка &BoD; будет заменена содержимым файла board_of_directors.html. Первая сущность здесь является внутренней общей сущностью, в вторая - внешней общей сущностью.
<), больше-чем (>), амперсанда (&), апострофа или одинарной кавычки (') и двойной кавычки (").
Установка свойств парсера DOM
DocumentBuilderFactory в JAXP имеет меньший набор свойств, чем SAXParserFactory. Наиболее важные методы такие.
setValidating(boolean)
Установка свойства фабрики проверки правильности.
isValidating()
Возвращает true, если фабрика создает проверяющие парсеры, иначе - false.
setNamespaceAware(boolean)
Установка свойства фабрики учета пространств имен.
isNamespaceAware()
Возвращает true, если фабрика создает парсеры, учитывающие пространство имен, иначе - false.
setIgnoringElementContentWhitespace(boolean)
Установка свойства пропусков фабрики. Если оно true, парсеры, создаваемые фабрикой не будут создавать узлы для игнорируемых пропусков в документе.
isIgnoringElementContentWhitespace()
Возвращает true, если фабрика создает парсеры, игнорирующие пропуски, иначе - false.
setCoalescing(boolean) и isCoalescing()
Фабрика создает парсеры, которые конвертируют секции CDATA в текстовые узлы. (CDATA - сокращение для "символьные данные", и обозначает здесь неразбираемый текст.)
setExpandEntityReferences(boolean) и
isExpandEntityReferences()
Фабрика создает парсеры, которые расширяют узлы ссылок на сущности.
setIgnoringComments(boolean) и isIgnoringComments()
Фабрика создает парсеры, которые игнорируют комментарии.
Раздел 3. Обзор пространств имен
Введение в пространства имен
<title> для названия книги. Вы в своем бизнесе можете хранить адреса ваших покупателей в XMLиспользуя тег <title> для титула покупателя.
<title>? Ответом являются пространства имен.
package. Два класса могут иметь одно и то же имя, пока они в двух разных пакетах. Например, и JDOM, и DOM определяют класс Document. Чтобы было ясным, какой класс я хочу использовать, я комбинирую имя пакета с именем класса, как в org.jdom.Document и org.w3c.dom.Document.
<bookOrder xmlns:lit="http://www.literarysociety.org/books"
xmlns:addr=http://www.usps.com/addresses >
. . .
<lit:title>My Life in the Bush of Ghosts</lit:title>
. . .
<shipTo>
<addr:title>Ms.</addr:title>
<addr:firstName>Linda</addr:firstName>
<addr:lastName>Lovely</addr:lastName>
. . .
lit связан со строкой http://www.literarysociety.org/books, а префикс addr связан со строкой http://www.usps.com/addresses. Когда вы используете элемент <lit:title> или <addr:title>, ясно, какой из элементов <title> вы используете.
Еще подробности о пространствах имен
addr в элементе <shipTo>:
<shipTo xmlns:addr="http://www.usps.com/addresses">
<addr:title>Ms.</addr:title>
<addr:firstName>Linda</addr:firstName>
<addr:lastName>Lovely</addr:lastName>
. . .
<shipTo>
<addr:title xmlns:addr="http://www.usps.com/addresses">
Ms.
</addr:title>
<addr:firstName xmlns:addr="http://www.usps.com/addresses">
Linda
</addr:firstName>
<addr:lastName xmlns:addr="http://www.usps.com/addresses">
Lovely
</addr:lastName>
. . .
xmlns без определения префикса вообще. Это определяет пространство имен по умолчанию для текущего элемента и всех элементов-потомков, которые не имеют префикса пространства имен. Я мог бы закодировать элемент <title> так:
<title xmlns="http://www.literarysociety.org/books">
My Life in the Bush of Ghosts
</title>
<author>:
<author
xmlns="http://www.literarysociety.org/authors">
<lastName>Shakespeare</lastName>
<firstName>William</firstName>
<nationality>British</nationality>
<yearOfBirth>1564</yearOfBirth>
<yearOfDeath>1616</yearOfDeath>
</author>
http://www.literarysociety.org/authors. Вне элемента <author> пространство имен по умолчанию уже не определено.
сравнение двух пространств имен
http://www.w3.org/1999/XSL/Transform. Обычно эта строка пространства имен связывается с префиксом xsl, но это не обязательно. Когда вы убеждаетесь, что пространство имен для данного элемента корректно, вам нужно проверять строку пространства имен, а не префикс пространства имен. Другими словами, такой элемент XSLT является правильным:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:stylesheet xmlns:xsl="http://i-love-stylesheets.com">
<xqdkera:stylesheet
xmlns:xqdkera="http://www.w3.org/1999/XSL/Transform">
xsl, но это неважно. При сравнении двух пространств имен, префикс не имеет значения.
Общее ошибочное представление о пространствах имен
xmlns:addr="cranberries" является правильным, пока cranberries является уникальным среди всех определений пространств имен в данном документе. Все подробности о правильных URI см в стандарте URI (RFC2396) в Ресурсы.
Одно последнее (потенциально сбивающее с толку) замечание
http://www.w3.org/2003/05/soap-envelope/role/next определено в спецификации SOAP 1.2. Если вы введете в ваш браузер http://www.w3.org/2003/05/soap-envelope/role/next, вы увидите страницу HTML, которая указывает вам на дополнительную информацию о спецификации SOAP 1.2.
Раздел 4. Разбор с пространствами имен
Получение информации пространства имен от парсера
<sonnet pt:type="Shakespearean"
xmlns:pt="http://www.literarysociety.org/poemtypes" >
<auth:author
xmlns:auth="http://www.literarysociety.org/authors">
<auth:lastName>Shakespeare</auth:lastName>
<auth:firstName>William</auth:firstName>
<auth:nationality>British</auth:nationality>
<auth:yearOfBirth>1564</auth:yearOfBirth>
<auth:yearOfDeath>1616</auth:yearOfDeath>
</auth:author>
<title>Sonnet 130</title>
<lines>
. . .
</lines>
</sonnet>
<auth:lastName>.
lastName - имя элемента без какого-либо префикса пространства имен
auth
auth:lastName - имя элемента, включая любой префикс пространства имен
http://www.literarysociety.org/authors
DOM и пространства имен
Node.getLocalName()
Node.getPrefix()
Node.getNodeName()
Node.getNamespaceURI()
Node. Знайте, что использование этих методов имеет некоторые сложности:
getLocalName(), getPrefix() или getNamespaceURI() на чем-либо другом, кроме Element или Attribute, результат будет null.
getLocalName(), getPrefix() или getNamespaceURI() на элементе, созданном методом DOM Уровня 1, таком как Document.createElement(), результат будет null. (Чтобы создать узел, который может быть использован с этим методом, используйте вместо этого метод DOM Уровня 2 Document.createElementNS().)
Element и Attribute getNodeName() всегда возвращает имя элемента или атрибута. Если элемента или атрибут имеет имя, квалифицированное пространством имен, имя узла будет auth:lastName или что-то похожее; если нет, имя узла будет lastName. Чтобы определить, квалифицировано ли пространствм имен имя данного элемента, вы должны посмотреть, является ли getPrefix() не-null или искать двоеточие в имени узла.
Создание парсера DOM, учитывающего пространство имен
DomNS, Java-программу, аналогичную по структуре программе DomOne из вводного учебника по XML-программированию на Java. Вы увидите здесь два основных отличия: вам необходимо создать учитывающий пространство имен DOM-парсер и добавить код для обработки информации пространства имен в дереве DOM. Вместо того, чтобы просто выводить разобранный документ на консоль, вы печатаете информацию о любых элементах в дереве DOM, квалифицированных пространством имен.
DocumentBuilderFactory перед созданием вашего DOM-парсера:
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(uri);
if (doc != null)
printNamespaceInfo(doc.getDocumentElement());
setNamespaceAware() вашего класса фабрики для включения учета пространства имен; с этого момента любой DOM-парсер, создаваемый фабрикой, будет учитывать пространство имен. После установке парсера вызывайте рекурсивно метод printNamespaceInfo(), чтобы найти все элементы и атрибуты в вашем документе, квалифицированные пространством имен.
Нахождение пространств имен в дереве DOM
printNamespaceInfo() ищет любые элементы и атрибуты в дереве DOM и печатает подробности о тех узлах, где метод getPrefix() возвращает не-null строку. Вот этот код:
case Node.ELEMENT_NODE:
{
if (node.getPrefix() != null)
{
System.out.println("\nElement " + node.getNodeName());
System.out.println("\tLocal name = " +
node.getLocalName());
System.out.println("\tNamespace prefix = " +
node.getPrefix());
System.out.println("\tNamespace URI = " +
node.getNamespaceURI());
}
if (node.hasAttributes())
{
NamedNodeMap attrs = node.getAttributes();
for (int i = 0; i < attrs.getLength(); i++)
if ((attrs.item(i).getPrefix()) != null)
printNamespaceInfo (attrs.item(i));
}
if (node.hasChildNodes())
{
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++)
printNamespaceInfo (children.item(i));
}
break;
}
case Node.ATTRIBUTE_NODE:
{
System.out.println("\nAttribute " +
node.getNodeName() + "="
+ node.getNodeValue());
System.out.println("\tLocal name = " +
node.getLocalName());
System.out.println("\tNamespace prefix = " +
node.getPrefix());
System.out.println("\tNamespace URI = " +
node.getNamespaceURI());
break;
}
printNamespaceInfo().
printNamespaceInfo() для всех дочерних элементов.
printNamespaceInfo() предполагает, что он вызывается только для узлов, квалифицированных пространствами имен.
DomNS на файле sonnetNamespaces.xml, вы получите такой результат:
C:\adv-xml-prog>java DomNS sonnetnamespaces.xml
Attribute pt:type=Shakespearean
Local name = type
Namespace prefix = pt
Namespace URI = http://www.literarysociety.org/poemtypes
Attribute xmlns:pt=http://www.literarysociety.org/poemtypes
Local name = pt
Namespace prefix = xmlns
Namespace URI = http://www.w3.org/2000/xmlns/
Element auth:author
Local name = author
Namespace prefix = auth
Namespace URI = http://www.literarysociety.org/authors
Attribute xmlns:auth=http://www.literarysociety.org/authors
Local name = auth
Namespace prefix = xmlns
Namespace URI = http://www.w3.org/2000/xmlns/
Element auth:lastName
Local name = lastName
Namespace prefix = auth
Namespace URI = http://www.literarysociety.org/authors
. . .
pt:type, элемент <auth:author> и элемент <auth:lastName>.
xmlns:pt и xmlns:auth отображаются на пространство имен http://www.w3.org/2000/xmlns/. Это пространство имен используется DOM-парсерами для пространства имен по умолчанию. Раздел 4 спецификации пространства имен в XML устанавливает, "Префикс xmlns используеться только для связывания пространства имен и сам не привязан ни к какому имени пространства имен." Однако, когда реализовывался DOM Уровня 2, Раздел 1.1.8 спецификации DOM Уровня 2 изменил это.
http://www.w3.org/2000/xmlns/. Это те атрибуты, в которых префикс пространства имен или квалифицированное имя - "xmlns". Хотя во время написания, это не являлось частью спецификации пространства имен в XML, планировалось включить это в будущие исправления.
xmlns:abc="...") или пространство имен по умолчанию (xmlns="...") будет сам отображаться на пространство имен http://www.w3.org/2000/xmlns/. Когда вы разбираете тот же документ с пространством имен SAX, определения не рассматриваются как атрибуты, так что никакое пространство имен не определено для самих определений.
Методы DOM, учитывающие пространство имен
Document.createAttributeNS(...)
Создает атрибут с заданным пространством имен и квалифицированным именем.
Document.createElementNS(...)
Создает элемент с заданным пространством имен и квалифицированным именем.
Document.getElementsByTagNameNS(...)
Возвращает NodeList со всеми узлами-потомками, которые соответствуют данному пространству имен и локальному имени.
Element.getAttributeNodeNS(...)
Возвращает узел Attribute с данным пространством имен и локальным именем атрибута.
Element.getAttributeNS(...)
Возвращает значение атрибута данным пространством имен и локальным именем атрибута.
Element.getElementsByTagNameNS(...)
Возвращает NodeList со всеми узлами-потомками, которые соответствуют данному пространству имен и локальному имени.
Element.hasAttributeNS(...)
Возвращает true, если этот элемент имеет атрибут с данным пространством имен и локальным именем, иначе - false.
Element.removeAttributeNS(...)
Для данного пространства имен и локального имени удаляет из элемента все атрибуты с этим пространством имен и локальным именем.
Element.setAttributeNodeNS(...)
Добавляет данный квалифицированный пространством имен объект Attr в данный элемент.
Element.setAttributeNS(...)
Для данного пространства имен и локального имени атрибута добавляет в данный элемент квалифицированный пространством имен атрибут с заданным значением.
Node.getLocalName()
Возвращает локальное (неквалифицированное) имя данного узла.
Node.getNamespaceURI()
Возвращает строку пространства имен, связанную с данным узлом, или null, если элемент или атрибут не связан с пространством имен.
Node.getNodeName()
Возвращает имя данного узла. Если узел квалифицирован пространством имен, этот метод возвращает префикс и имя элемента; иначе он возвращает только имя элемента.
Node.getPrefix()
Возвращает префикс пространства имен для данного узла или null, если элемент или атрибут в исходном XML не имеет префикса.
Node.setPrefix(...)
Устанавливает префикс пространства имен для данного узла.
Node возвращают полезную информацию только для Element и Attribute. Вызов этих методов на других типах узлов возвращает null.
SAX и пространства имен
Параметр localName (второй параметр) событий startElement и endElement.
Недоступен непосредственно. Префиксом пространства имен является все до двоеточия в параметре qName (третий параметр) событий startElement и endElement.
Параметр qName (третий параметр) событий startElement и endElement.
Параметр uri (первый параметр) событий startElement и endElement.
startElement и endElement.
Создание парсера SAX, учитывающего пространства имен
SaxNS, Java-программу, похожую на DomNS. Как и в случае DomNS, вашим первым шагом является создание учитывающего пространство имен парсера. Установите свойство объекта SAXParserFactory, создающего SAXParser, и вы готовы начать разбор. Вот как создается такой SAXParser, какой вам нужен:
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
SAXParser sp = spf.newSAXParser();
sp.parse(uri, this);
setNamespaceAware() для вашего класса фабрики для включения учета пространства имен; с этого момента любой создаваемый фабрикой SAXParser будет учитывать пространство имен. Если вы имеете установленный парсер, ваши обработчики событий будут получать события от него.
Нахождение пространств имен в событиях SAX
SaxNS вы хотите выводить на консоль подробную информацию о любых элементах, квалифицированных пространством имен. Вы можете найти всю информацию, которая вам нужна в событии startElement. Вот код для обработчика события startElement:
public void startElement(String namespaceURI, String localName,
String qName, Attributes attrs)
{
if (namespaceURI.length() > 0)
{
System.out.println("\nElement " + qName);
System.out.println("\tLocal name = " + localName);
if (qName.indexOf(':') > 0)
System.out.println("\tNamespace prefix = " +
qName.substring(0, qName.indexOf(':')));
else
System.out.println("\tNamespace prefix =");
System.out.println("\tNamespace URI = " + namespaceURI);
}
if (attrs != null)
{
int len = attrs.getLength();
for (int i = 0; i < len; i++)
if (attrs.getURI(i).length() > 0)
{
System.out.println("\nAttribute " +
attrs.getQName(i) + "=" +
attrs.getValue(i));
System.out.println("\tLocal name = " +
attrs.getLocalName(i));
if (qName.indexOf(':') > 0)
System.out.println("\tNamespace prefix = " +
attrs.getQName(i).
substring(0,
attrs.getQName(i).
indexOf(':')));
else
System.out.println("\tNamespace prefix = ");
System.out.println("\tNamespace URI = " +
attrs.getURI(i));
}
}
}
DomNS. Однако, есть несколько отличий:
java.lang.String.indexOf() и java.lang.String.substring() для выделения префикса из квалифицированного имени элемента или атрибута. (Заметьте, что вы должны быть уверены в том, что квалифицированное имя содержит двоеточие; если этот элемент или атрибут использует пространство имен по умолчанию, квалифицированное имя не будет содержать двоеточия.)
getLocalName() и getValue() позволяют вам работать со свойствами данного атрибута.
SaxNS на файле sonnetNamespaces.xml, вы получите такой результат:
C:\adv-xml-prog>java SaxNS sonnetnamespaces.xml
Attribute pt:type=Shakespearean
Local name = type
Namespace prefix = pt
Namespace URI = http://www.literarysociety.org/poemtypes
Element auth:author
Local name = author
Namespace prefix = auth
Namespace URI = http://www.literarysociety.org/authors
Element auth:lastName
Local name = lastName
Namespace prefix = auth
Namespace URI = http://www.literarysociety.org/authors
Element auth:firstName
Local name = firstName
Namespace prefix = auth
Namespace URI = http://www.literarysociety.org/authors
Element auth:nationality
Local name = nationality
Namespace prefix = auth
Namespace URI = http://www.literarysociety.org/authors
SaxNS такой же, как и DomNS. Единственное отличие состоит в том, что SAXParser не сообщает о самих определениях пространств имен (таких, как xmlns:pt="http://www.lit..." ), как атрибутах. Через минуту я покажу вам события SAX, которые обрабатывают определения пространств имен.
События SAX, связанные с пространством имен
http://www.w3.org/2000/xmlns/.
SaxNS определения пространств имен не показывались в выводе вообще. Другими словами, атрибуты для элемента <sonnet> включают атрибут pt:type, но не включают определение самого пространства имен pt.
startPrefixMapping и endPrefixMapping сообщают вам, когда определенное пространство имен определяется и когда пространство имен выходит из сферы видимости. Далее я рассмотрю новое приложение, SaxNSTwo, которое использует эти новые события для обработки пространств имен.
startPrefixMapping и endPrefixMapping является отслеживание различных префиксов и URI пространств имен, на которые они отображаются. В SaxNSTwo я показываю как создать HashMap для отслеживания событий пространства имен по мере их поступление. Вот код, который реализует HashMap и обработчики событий:
private HashMap prefixes = new HashMap();
. . .
public void startPrefixMapping(String prefix, String uri)
{
System.out.println("\nNew namespace:");
System.out.println("\tNamespace prefix = " + prefix);
System.out.println("\tNamespace URI = " + uri);
prefixes.put(uri, prefix);
}
public void endPrefixMapping(String prefix)
{
System.out.println("\nPrefix " + prefix +
" is no longer in scope.");
}
startPrefixMapping, вы добавляете комбинацию префикса и URI в HashMap. Когда вы получаете событие endPrefixMapping, вы удаляете префикс и URI. Последнее усовершенствование, сделанное в SaxNSTwo состоит в добавлении закрытого метода для получения отображения префикса для определенного URI:
private String getPrefix(String url)
{
if (prefixes.containsKey(url))
return prefixes.get(url).toString();
else
return "";
}
getPrefix() для выборки префикс, связанного с данным URI:
if (namespaceURI.length() > 0)
{
System.out.println("\nElement " + qName);
System.out.println("\tLocal name = " + localName);
System.out.println("\tNamespace prefix = " +
getPrefix(namespaceURI) );
System.out.println("\tNamespace URI = " + namespaceURI);
}
Последний подход к обработке пространств имен
SaxNSTwo, очень прямолинейный: когда вы получаете событие startPrefixMapping, вы помещаете элемент в вашу HashMap, а затем выбираете значение, когда оно вам нужно. Если данный URI отображен на два префикса, и эти определения вложены друг в друга, SaxNSTwo не выполнит свою работу. Это не общий случай, но он возможен. SaxNTwo не обрабатывает правильно случай, когда данный префикс отображается на два URI, но проверка ошибок парсера поймает его прежде, чем ваш код сделает что-либо неправильное.
startPrefixMapping, и извлекая значение из стека этого URI по событию endPrefixMapping.
org.xml.sax.helpers обеспечивает специальный класс, NamespaceSupport, для управления пространствами имен по мере того, как они входят в сферу видимости и выходят из нее. Я рассмотрю последний класс, SaxNSThree, который использует объект NamespaceSupport, имеющий дело с пространствами имен.
NamespaceSupport:
startPrefixMapping, вызывайте pushContext() для сохранения текущего набора определенных пространств имен. После этого вызывайте declarePrefix для добавления новых определенных пространств имен к текущему контексту.
popContext, чтобы вернуться в предыдущий набор опоределений пространств имен.
getPrefix().
SaxNSThree. Прежде всего, вот объявление объекта NamespaceSupport и двух обработчиков событий:
private NamespaceSupport ns = new NamespaceSupport();
. . .
public void startPrefixMapping(String prefix, String uri)
{
ns.pushContext();
ns.declarePrefix(prefix, uri);
}
public void endPrefixMapping(String prefix)
{
ns.popContext();
}
C:\adv-xml-prog>java SaxNSThree sonnetnamespaces.xml
<sonnet> : 2 prefixes defined - (xml, pt)
Attribute pt:type=Shakespearean
Local name = type
Namespace prefix = pt
Namespace URI = http://www.literarysociety.org/poemtypes
<auth:author> : 3 prefixes defined - (xml, auth, pt)
Local name = author
Namespace prefix = auth
Namespace URI = http://www.literarysociety.org/authors
<auth:lastName> : 3 prefixes defined - (xml, auth, pt)
Local name = lastName
Namespace prefix = auth
Namespace URI = http://www.literarysociety.org/authors
. . .
<title> : 2 prefixes defined - (xml, pt)
<lines> : 2 prefixes defined - (xml, pt)
. . .
http://www.w3.org/XML/1998/namespace. Объект NamespaceSupport отслеживает каждое определение пространства имен, находящееся в текущий момент в сфере видимости.
NamespaceSupport является то, что он возвращает Enumeration для метода getPrefixes(). (NamespaceSupport использует Enumeration также и в других местах.) Чтобы получить число пространств имен в сфере видимости, вы должны написать некоторый некрасивый код:
private int getPrefixCount()
{
Enumeration e = ns.getPrefixes();
int count = 0;
while (e.hasMoreElements())
{
count++;
e.nextElement();
}
return count;
}
getPrefixes() возвращает объект какого-то рода коллекции Java, вы можете использовать метод size(), чтобы получить число префиксов, определенных на текущий момент.
Объекты SAX, учитывающие пространство имен
org.xml.sax.Attributes
За исключением getLength(), который возвращает число атрибутов, все методы этого класса учитывают пространство имен.
org.xml.sax.ContentHandler
События startElement, endElement, startPrefixMapping и endPrefixMapping учитывают пространство имен.
org.xml.sax.helpers.AttributesImpl
Следующие методы учитывают пространство имен: addAttribute(), getIndex(), getLocalName(), getQName(), getType(), getURI(), getValue(), setAttribute(), setLocalName(), setQName() и setURI().
org.xml.sax.helpers.DefaultHandler
События startElement, endElement, startPrefixMapping и endPrefixMapping учитывают пространство имен. (Эти события определены в интерфейсе ContentHandler, который реализован в DefaultHandler.)
org.xml.sax.helpers.NamespaceSupport
Этот класс существует для управления пространствами имен по мере того, как они входят в сферу видимости и выходят из нее.
org.xml.sax.helpers.XMLFilterImpl
Этот класс реализует интерфейс ContentHandler, так что он включает в себя события, учитывающие пространство имен, startElement, endElement, startPrefixMapping и endPrefixMapping.
JDOM и пространства имен
Element.getName()
Локальное (неквалифицированное) имя элемента
Element.getNamespacePrefix()
Префикс пространства имен
Element.getQualifiedName()
Квалифицированное имя элемента
Element.getNamespaceURI()
URI пространства имен
JdomNS, приложение, которое разбирает XML-файл и выводит информацию пространства имен на консоль.
org.jdom.input.SAXBuilder. Если вам нужно управлять свойствами SAXBuilder, вы можете использовать методы setFeature() или setProperty(). Знайте, что JDOM нуждается в том, чтобы SAX-парсер был конфигурирован определенным образом, так что документация JDOM рекомендует вам использовать эти методы осторожно.
Обработка информации пространства имен при помощи JDOM
JdomNS, разбирающее XML-файл и возвращающее структуру вашего JDOM Document. Как и в DomNS, вы должны обойти эту структуру и найти всю информацию пространств имен. Вы будете использовать здесь рекурсивный подход. Вот как начинается этот код:
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(new File(argv[0]));
if (doc != null)
printNamespaceInfo(doc.getRootElement() );
printNamespaceInfo() является Element JDOM. В DOM, вы имели единственный тип данных (Node), и каждый тип узла был его подклассом. В JDOM вы будете работать только с Element.
printNamespaceInfo(), я покажу вам далее этот метод. Он имеет четыре задачи:
printNamespaceInfo() для каждого потомка.
if (el.getNamespaceURI().length() > 0 )
{
System.out.println("\nElement " + el.getQualifiedName());
System.out.println("\tLocal name = " + el.getName());
System.out.println("\tNamespace prefix = " +
el.getNamespacePrefix());
System.out.println("\tNamespace URI = " +
el.getNamespaceURI());
}
xmlns:tp="http://..." , например) учитывается как другой атрибут. В JDOM объявление пространства имен не рассматривается как атрибут, так что вы должны использовать метод getAdditionalNamespaces().
<auth:author xmlns:auth="..."> в документе-примере), это объявление пространства имен доступно только через текущий элемент, не через getAdditionalNamespaces(). Вот следующий сегмент кода:
Iterator nsIter = el.getAdditionalNamespaces().listIterator();
while (nsIter.hasNext())
{
Namespace ns = (Namespace) nsIter.next();
System.out.println("\nNamespace declaration:");
System.out.println("\tNamespace prefix = " + ns.getPrefix());
System.out.println("\tNamespace URI = " + ns.getURI());
}
getAttributes(), getAdditionalNamespaces(), getChildren() и т.д.), этот набор чего-то возвращается вам, как List, часть API Java Collections. Вы должны использовать ListIterator для движения через List. Последнее замечание о коллекциях узлов: поскольку ListIterator возвращает Object, вы должны преобразовывать тип различных элементов списка в Namespace, Attribute и т.д.
ListIterator, как и следовало ожидать:
Iterator attrIter = el.getAttributes().listIterator();
while (attrIter.hasNext())
{
Attribute attr = (Attribute)attrIter.next();
if (attr.getNamespaceURI().length() > 0 )
{
System.out.println("\nAttribute " +
attr.getQualifiedName() + "=" +
attr.getValue());
. . .
Element и вызвать printNamespaceInfo для каждого из них. Вот как выглядит этот код:
Iterator childIter = el.getChildren().listIterator();
while (childIter.hasNext())
printNamespaceInfo((Element)childIter.next());
Раздел 5. Проверка правильности XML-документов
Обзор проверки правильности
Определение документа при помощи DTD
<!ELEMENT sonnet (author,title?,lines)>
<!ATTLIST sonnet type (Shakespearean | Petrarchan)
"Shakespearean">
<!ELEMENT author (lastName,firstName,nationality,
yearOfBirth?,yearOfDeath?)>
. . .
<!ELEMENT lines (line,line,line,line,
line,line,line,line,
line,line,line,line,
line,line)>
<!ELEMENT line (#PCDATA)>
type атрибутов элемента <sonnet>. Он также определяет допустимые значения (Shakespearean и Petrarchan), а также значение по умолчанию (Shakespearean). Другие синтаксические нотации:
#PCDATA означает, что элемент содержит только текст, без других элементов.
<lines> содержит 14 элементов <line>, вы должны перечислить все 14 элементов. Если элемент <lines> может содержать 10, 12 или 14 элементов <line>, то вы должны перечислить все возможности - 10 элементов <line>, за которыми следует вертикальная черта (|), за которой следуют 12 элементов <line>, за которыми следует вертикальная черта, за которой следуют 14 элементов <line>.
Определение документа при помощи схемы XML
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="sonnet">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="author"/>
<xsd:element ref="title" minOccurs="0"/>
<xsd:element ref="lines"/>
</xsd:sequence>
</xsd:complexType>
<xsd:attribute name="type" type="sonnetType"
default="Shakespearean"/>
</xsd:element>
<xsd:simpleType name="sonnetType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Petrarchan"/>
<xsd:enumeration value="Shakespearean"/>
</xsd:restriction>
</xsd:simpleType>
. . .
<xsd:element name="title" type="xsd:string"/>
<xsd:element name="lines">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="line" minOccurs="14" maxOccurs="14"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="line" type="xsd:string"/>
<sonnet>: его содержимым является элемент <author>, элемент <title> и элемент <lines>. Некоторые замечания по синтаксису:
minOccurs="0", чтобы показать, что элемент <title> является не обязательным.
xsd:string и содержит одно из двух значение, Shakespearean или Petrarchan.
<line> используйте minOccurs="14" и maxOccurs="14".
Определение документа при помощи RELAX NG
<element>. Чтобы определить, что элемент или атрибут не является обязательным, вы используете элемент <optional>. Вот фрагмент определения RELAX NG для примера сонета:
<grammar>
. . .
<start>
<element name="sonnet">
<ref name="typeAttribute"/>
<ref name="author"/>
<ref name="title"/>
<ref name="lines"/>
</element>
</start>
</grammar>
<sonnet> содержит четыре вещи, определенные где-то в файле RELAX NG, как typeAttribute, author, title и lines. typeAttribute - это атрибут с именем type, который содержит одно из двух значений, Shakespearean или Petrarchan. Вот определение:
<define name="typeAttribute">
<attribute name="type">
<choice>
<value>Shakespearean</value>
<value>Petrarchan</value>
</choice>
</attribute>
</define>
<define> работает как функция замещения; везде, где вы ссылаетесь на определение (как в <ref name="typeAttribute">), ссылка заменяется содержимым элемента <define>.
minOccurs="14" и maxOccurs="14", чтобы определить, что элемент <lines> содержит 14 элементов <line>. В RELAX NG вы должны их перечислить:
<define name="lines">
<element name="lines">
<ref name="line"/>
<ref name="line"/>
<ref name="line"/>
. . .
<ref name="line"/>
</element>
</define>
Определение документа при помощи Schematron
<assert> используется для определения правил для сонета. Каждый оператор <assert> имеет атрибут test; если test не true, то текст элемента <assert> выводится как сообщение об ошибке.
<lines>:
<rule context="lines">
<assert test="count(line) = 14">
A sonnet must have 14 <line>s.
</assert>
<assert test="count(line) = count(*)">
The <lines> element can only contain
<line> elements.
</assert>
</rule>
<lines> должен содержать 14 элементов <line>. Второе ограничение значительно более сложное, оно говорит, что является то, что элемент <lines> не может содержать никаких других элементов. Способом задания этого является указание, что общее число элементов <line> должно равняться общему числу всех элементов.
<sonnet> должен содержать элемент <author> элемент, за которым следует не обязательный элемент <title>, за которым следует элемент <lines>. Вот как вы выражаете это в Schematron:
<assert test="count(*) < 4">
The <sonnet> element contains an <author>
element, an optional <title> element, and a
<lines< element.
</assert>
<assert test="*[1] = author">
The first child of the <sonnet> element must be
an <author> element.
</assert>
<assert test="(count(*) = 2 and *[2] = lines) or
(count(*) = 3 and
*[2] = title and *[3] = lines)">
If you use the optional <title> element, the
<sonnet> element must contain the <author>,
<title>, and <lines> elements in that order.
</assert>
<sonnet> не может содержать более трех элементов. Второе правило устанавливает, что первым потомком элемента <sonnet> должен быть элемент <author>. Наконец, третье правило говорит, что после элемента <author> (требуемого вторым правилом) <sonnet> может содержать либо элемент <lines>, либо элемент <title>, за которым следует элемент <lines>.
<sonnet>. В данном случае это не слишком плохо, но очевидно, что это может выходить из-под контроля для более сложных документов. Попытки комбинировать подходы RELAX NG и Schematron находятся в процессе разработки, подробности см. в превосходной статье Eddie Robertsson's в XML.com в Ресурсы.
Проверка правильности документа
Проверка правильности при помощи SAX
SAXParserFactory, и для SAXParser, который им создается. Это отличается от предыдущих примеров; до сих пор вы всегда устанавливали свойства объекта-фабрики а не создаваемого им объекта-парсера. Вот код, который вам нужен:
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setNamespaceAware(true);
spf.setValidating(true);
SAXParser sp = spf.newSAXParser();
sp.setProperty
("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema");
sp.parse(uri, this);
SAXParser, создаваемых SAXParserFactory. Свойство schemaLanguage property определяет, какой язык схемы вы будете использовать для проверки правильности документа. Используемое здесь значение (http://www.w3.org/2001/XMLSchema) определено JAXP. Возможно, что данный парсер может определять другое значение, чтобы показать поддержку RELAX NG, Schematron или какого-то другого языка, хотя никакие популярные парсеры этого не делают по состоянию на июль 2004 г.
sonnetSchema.xml и добавьте элемент <nickname> внутрь элемента <author>:
. . .
<author>
<lastName>Shakespeare</lastName>
<firstName>William</firstName>
<nickname>Shakin' Billy</nickname>
<nationality>British</nationality>
. . .
C:\adv-xml-prog>java SaxValidator sonnetSchema.xml
[Error] sonnetschema.xml:9:15: cvc-complex-type.2.4.a: Invalid
content was found starting with element 'nickname'. One of '{"
":nationality}' is expected.
Your документ is not valid.
Проверка правильности при помощи DOM-парсера
DocumentBuilderFactory, установить некоторые его свойства, а затем создать парсер (DocumentBuilder). Прежде, чем разбирать и проверять правильность XML-документа, вам нужно определить обработчик ошибок для DOM-парсера. Удивительно, но обработчик ошибок является обработчиком ошибок SAX; это отражает тот факт, что DOM-парсеры обычно строятся над SAX-парсерами.
DocumentBuilderFactory dbf =
DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setValidating(true);
schemaLanguage:
if (useSchema)
dbf.setAttribute
("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema");
DocumentBuilder db = dbf.newDocumentBuilder();
db.setErrorHandler(this);
doc = db.parse(uri);
DomValidator. (Технически вы реализуете интерфейс SAX DefaultHandler, который включает в себя ErrorHandler.) Для вас это означает, что вы должны реализовать методы warning(), error() и fatalError().
Проверка правильности при помощи Jing
c:>java -jar c:/jing-20030619/bin/jing.jar sonnet.rng sonnet.xml
<firstName>, вы получите такое сообщение:
c:>java -jar c:/jing-20030619/bin/jing.jar sonnet.rng sonnet.xml
sonnet.xml:6:18: error: required elements missing
c:>
com.thaiopensource.relaxng.util.Driver (следуя, конечно, лицензионному соглашению, распространяемому с Jing).
Проверка правильности при помощи Schematron
sonnetSchematron.xml), который соответствует правилам Schematron для документ.
sonnetRules.xsl), которая настроена для проверки правильности вашего типа документа.
java org.apache.xalan.xslt.Process -in sonnetSchematron.xml
-xsl schematron-basic.xsl -out sonnetRules.xsl
sonnetRules.xsl, таблицу стилей, которая проверяет правильность документа-сонета. Чтобы использовать sonnetRules.xsl, запустите опять движок таблиц стилей:
java org.apache.xalan.xslt.Process -in sonnet.xml
xsl sonnetRules.xsl
sonnet.xml правильный, вы не увидите сообщений об ошибках. Если что-то неправильно, вы увидите вывод одного из элементов <assert>, которые вы закодировали раньше. Например, если вы уберете из сонета один из 14 элементов <line>, вы увидите такое сообщение об ошибке:
In pattern count(line) = 14:
A sonnet must have 14 <line>s.
Раздел 6. Резюме
Резюме
Я обсудил в этом учебнике различные приемы и API. Все они в основе фокусируются на проверке правильности; вам нужно установить свойства парсера на использование проверки правильности, и парсеры должны учитывать пространства имен для проверки правильности XML-документов. Как и в моем предыдущем учебнике, я рассмотрел несколько разных стандартов, API и подходов, так что вы можете выбирать инструменты, которые лучше работают для вас. В последнем учебнике этой серии я рассмотрю создание структур DOM и SAX с самого начала, преобразование структур данных из одного API в другой и некоторые продвинутые свойства API DOM и SAX.
Ресурсы
Обучение
. Doug упоминал, что RELAX NG has a документ DTD Compatibility, который определяет "типы данных и нотации для поддержки некоторых свойств DTD XML 1.0, которые не поддерживаются непосредственно в RELAX NG."
Получение продуктов и технологий
Об авторе
Каталог Индекс раздела