Проверка правильности XML и оценка XPath в J2SE 5.0
Одними из новых особенностей, добавленных в Java 2 Platform, Standard Edition (J2SE) 5.0 (кодовое название Tiger), являются пакет проверки правильности XML – javax.xml.validation
и библиотеки XPath – javax.xml.xpath
. До выхода версии Tiger классы SAXParser и DocumentBuilder Java API обработки XML (JAXP) использовались для проверки правильности XML. Новый API проверки правильности XML позволяет разделить процедуры проверки правильности и анализа XML документов. Данное нововведение позволяет, помимо прочего, поддерживать различные схемы. Давайте подробнее рассмотрим проверку правильности XML.
Проверка правильности XML
Наиболее простым способом проверки правильности XML Документа является использование объекта. Данный объект производит проверку правильности, применяя объект Schema
из которого был создан объект Validator
. Объекты Schema
обычно создаются из объектов SchemaFactory
. Статичный объект newInstance()
позволяет создавать объекты SchemaFactor
y используя заданную XML схему. Нижеприведенный код демонстрирует данный процесс:
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(new File("mySchema.xsd"));
Validator validator = schema.newValidator();
Проверка правильности происходит при вызове метода validate()
объекта Validator
. Как минимум, данный метод требует передачи объектаjavax.xml.transform.Source
, который может быть либо SAXSource
, либо DOMSource
, в зависимости от ваших предпочтений.
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = parser.parse(new File("myXMLDocument.xml"));
validator.validate(new DOMSource(document));
Ниже приведен пример кода, показывающий способ проверки правильности документа XML при помощи XML схемыWorld Wide Web Consortium (W3C) XML Schema , иногда именуемой WXS.
try {
// Преобразование XML документа в дерево DOM.
DocumentBuilder parser =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = parser.parse(new File("myXMLDocument.xml"));
// Создание объекта SchemaFactory спосбного работать с WXS схемами.
SchemaFactory factory =
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// Загрузка WXS схемы, представленной элементом Schema.
Source schemaFile = new StreamSource(new File("mySchema.xsd"));
Schema schema = factory.newSchema(schemaFile);
// Создание объекта Validator, используемого для проверки
// правильности
// документа.
Validator validator = schema.newValidator();
// Поверка правильности дерева DOM.
validator.validate(new DOMSource(document));
} catch (ParserConfigurationException e) {
// обработка ошибок
} catch (SAXException e) {
// обработка ошибок в случае неправильности документа
} catch (IOException e) {
// обработка ошибок
}
Заметьте, что при вызове метода newInstance()
ему передается константа, определяющая тип предаваемой схемы. В нашем примере единственной необходимой схемой является W3C XML Schema. Данная схема задает типовую систему определения структуры данных XML документа. WXS поддерживается консорциумом W3C и является W3C рекомендацией.
Давайте, выполним вышеприведенный код для следующего XML файла:
<?xml version="1.0"?> <birthdate> <month>January</month> <day>21</day> <year>1983</year> </birthdate>
Также, добавим следующий документ W3C XML Schema, в качестве схемы проверки правильности нашего XML документа:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd" /> <xs:element name="birthdate"> <xs:complexType> <xs:sequence> <xs:element name="month" type="xs:string" /> <xs:element name="day" type="xs:int" /> <xs:element name="year" type="xs:int" /> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
Если проверка правильности прошла успешно, то программа завершится без ошибок. Однако, давайте внесем опечатку в элемент month
.
<amonth>January</amonth>
В данном случае, объект Validator
вызовет ошибку SAXException
. Ниже представлены первые несколько строк сообщения об ошибке:
ERROR: 'cvc-complex-type.2.4.a: Invalid content was
found starting with element 'amonth'. One of '{"":month}'
is expected.'
org.xml.sax.SAXParseException: cvc-complex-type.2.4.a:
Invalid content was found starting with element 'amonth'.
One of '{"":month}' is expected.
At ...(Util.java:109)
at ...(ErrorHandlerAdaptor.java:104)
Основы XML схем
Все реализации объекта SchemaFactory
требуют поддержки W3C XML Schema. Если вы не знакомы со спецификацией W3C XML Schema, ниже приводится ее краткое описание.
В XML схемах задаются определения, которые могут быть как простыми, так и сложными. На высоком уровне, сложный тип заключает в себе другие элементы, в то время как простой не содержит их. (Данные типы имеют и другие различия, однако, в данной статье они не рассматриваются.) В качестве примера, давайте создадим схему, определяющую элемент fullname
, содержащий элементы firstname
, middlename
и lastname
, в соответствующем порядке.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="fullname"> <xs:complexType> <xs:sequence> <xs:element name="firstname" type="xs:string"/> <xs:element name="middlename" type="xs:string"/> <xs:element name="lastname" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
Корневым элементом каждой XML схемы является элемент schema
. В вышеприведенном определении схемы используется атрибут xmlns
, задающий, что элементы и типы данных, используемые схемой, принадлежат пространству имен "http://www.w3.org/2001/XMLSchema"
.
Элементы и атрибуты простого типа
Элементы и атрибуты простого типа не включают в себя других элементов и атрибутов. Они определяют только текст различных типов. Данные типы могут быть одними из определенных типов XML схемы или же они могут быть заданы вами самостоятельно. Вы можете добавить ограничения для типа данных, тем самым, ограничивая его содержимое. Также вы можете задать шаблон, которому будут соответствовать вводимые данные. Далее даны несколько примеров простых элементов:
<xs:element name="name" type="xs:string"/> <xs:element name="age" type="xs:integer"/> <xs:element name="birthdate" type="xs:date"/>
Здесь приводятся наиболее употребимые типы данных, используемые в XML схемах:
xs:string
xs:decimal
xs:integer
xs:boolean
xs:date
xs:time
Для простых элементов могут быть заданы значения по умолчанию и фиксированные значения. Значение по умолчанию присваивается элементу, в том случае, когда не задано другое значение. Фиксированное значение присваивается, также, автоматически и не может быть изменено. Например:
<xs:element name="firstname" type="xs:string" default="joe"/> <xs:element name="firstname" type="xs:string" fixed="unknown"/>
Так же как вы определяете элементы, вы можете определять и атрибуты, задавая их имя, тип, значения по умолчанию и фиксированные значения. По умолчанию атрибуты являются не обязательными, но вы можете задать их обязательно применение при помощи атрибута use
.
<xs:attribute name="lang" type="xs:string" use="optional"/> <xs:attribute name="lang" type="xs:string" use="required"/>
Элементы сложного типа
Сложным элементом является элемент, содержащий другие элементы и атрибуты. Давайте рассмотрим сложный элемент fullname
, содержащий другие элементы firstname
, middlename
и lastname
.
<fullname> <firstname>Robert</firstname> <middlename>Franklin</middlename> <lastname>Collins</lastname> </fullname>
Вы можете определить данную последовательность двумя способами. Первый способ: элемент fullname
может быть объявлен напрямую, как показано ниже. Заметьте, что дочерние элементы firstname
, middlename
и lastname
заключены в тег sequence
. Тем самым задается последовательность использования дочерних элементов: первый – firstname
, второй – middlename
, третий – lastname
.
<xs:element name="fullname"> <xs:complexType> <xs:sequence> <xs:element name="firstname" type="xs:string"/> <xs:element name="middlename" type="xs:string"/> <xs:element name="lastname" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element>
Второй способ: мы можем использовать в элементе fullname
атрибут type
, ссылающийся на другой сложный тип. Ниже приведен пример создания сложного типа и ссылки на него из элемента fullname
:
<xs:element name="fullname" type="personinfo"/> <xs:complexType name="personinfo"> <xs:sequence> <xs:element name="firstname" type="xs:string"/> <xs:element name="middlename" type="xs:string"/> <xs:element name="lastname" type="xs:string"/> </xs:sequence> </xs:complexType>
Преимуществом данного способа является то, что несколько элементов могут ссылаться на один сложный тип. Вы также можете на основе уже существующего сложного типа создать новый сложный тип, используя расширения, как например:
<xs:element name="contact" type="fullpersoninfo"/> <xs:complexType name="personinfo"> <xs:sequence> <xs:element name="firstname" type="xs:string"/> <xs:element name="middlename" type="xs:string"/> <xs:element name="lastname" type="xs:string"/> </xs:sequence> </xs:complexType> <xs:complexType name="fullpersoninfo"> <xs:complexContent> <xs:extension base="personinfo"> <xs:sequence> <xs:element name="address" type="xs:string"/> <xs:element name="city" type="xs:string"/> <xs:element name="country" type="xs:string"/> </xs:sequence> </xs:extension> </xs:complexContent> </xs:complexType>
В схеме W3C XML Schema существуют и другие полезные свойства, представленные в Таблице 1.
Индикатор | Функция |
---|---|
all |
Определяет, что каждый из дочерних элементов должен употребляться хотя бы раз, но в любой последовательности. |
choice |
Определяет, что может употребляться любая из альтернатив |
sequence |
Определяет, что дочерние элементы должны появляться в определенном порядке |
@maxOccurs |
Определяет максимальное количество случаев употребления элемента |
@minOccurs |
Определяет минимальное количество случаев употребления элемента |
group |
Используется для определения соответствующих наборов элементов |
attributeGroup |
Используется для определения соответствующих наборов элементов |
Данный раздел является кратким описанием возможностей XML схемы и только затрагивает такие свойства как ограничения и расширения. Дополнительную информацию вы можете найти на странице, посвященной W3C XML Schema.
Оценка выражений XPath в JDK 1.5
Другим пакетом, добавленным в средства XML в JDK 1.5, является java.xml.xpath
. Данный пакет предоставляет API для оценки выражений, созданных на основе XML Path Language (XPath) версии 1.0. XPath позволяет выбирать узлы дерева объектной модели документов (DOM) XML. В XPath также определены правила преобразования узлов в переменные со значениями boolean, double или string. В документации Javadocs приводится следующая информация: «XPath возник в 1999 году в качестве расширения языков XSLT и Xpointer, но вскоре стал популярным как самостоятельный язык, так как одно выражение XPath способно заменить большое количество линий кода DOM API.
Описание XPath
Давайте рассмотрим выражения XPath и способы их применения. Ниже приведен пример простого выражения XPath:
book/author
Данное выражение известно как путь. Оно выбирает все элементы author
, являющиеся дочерними для элемента book
. Элемент book
является дочерним для текущего контекста узла. Например, если текущим контекстом узла является элемент library, то применение выражения XPath – book/author
выберет оба элемента author
из приведенного далее кода:
<book> <author name="Author A"/> <author name="Author B"/> </book> </library>
Корневым узлом может быть любой узел древа XML DOM, в том числе и корневой узел.
Заметьте, что элемент author должен быть прямым потомком элемента book
. Существует специальный оператор //
, выбирающий узлы любой глубины в XML документе для текущего контекста узла. Например, следующий код позволяет выбрать все элементы author
для контекста узла.
В таблице 2 представлены некоторые полезные операторы XPath.
Путь | Описание |
---|---|
../author |
Выбирает все элементы author , являющиеся дочерними для контекста узла. |
* |
Выбирает все дочерние элементы контекста узла |
*/author |
Выбирает все потомков элемента author для текущего контекста узла |
/book/author |
Выбирает все элементы author являющиеся дочерними для элемента book , которые в свою очередь являются дочерними для корневого узла документа. |
./book/author |
Выбирает все элементы author являющиеся дочерними для элемента book , которые в свою очередь являются дочерними для для текущего контекста узла. |
Кроме элементов, пути XPath могут работать с атрибутами, текстом, комментариями и другими узлами DOM дерева. В таблице 3 приведено несколько примеров.
Путь | Описание |
---|---|
author/@name |
Выбирает атрибут name элемента author |
author/node() |
Выбирает любой тип узла (текст, комментарий, или инструкцию обработки). |
author/text() |
Выбирает текстовые узлы элемента author |
author/comment() |
Выбирает все комментарии в элементе author . |
author/processing-instruction() |
Выбирает все инструкции обработки в элементе author . |
Выражения XPath позволяют сузить количество узлов, выбранных при помощи путей XPath. Выражения записываются в виде – [expression]
. Следующий пример выбирает все элементы foo
, содержащие атрибут include
, имеющий значение true
.
//foo[@include='true']
Выражения могут следовать друг за другом для более четкого выбора:
//foo[@include='true'][@class='bar']
Использование XPath API
Следующий пример иллюстрирует применение XPath API для выбора хотя бы одного узла из XML документа.
XPath xpath = XPathFactory.newInstance().newXPath();
String expression = "/birthdate/year";
InputSource inputSource = new InputSource("myXMLDocument.xml");
NodeSet nodes = (NodeSet) xpath.evaluate(expression, inputSource, XpathConstants.NODESET);
Заметьте, что XPath API позволяет преобразовывать, выбранные узлы в объекты, следующих типов Boolean
, Number
и String
. Возвращаемый тип определяется параметром QName в вызове метода оценки выражения XPathExpression.evaluate()
, как показано в примере выше (третий параметр). Дозволенные значения QName
определены в виде констант в классе XPathConstants:
XPathConstants.NODESET
XPathConstants.NODE
XPathConstants.STRING
XPathConstants.BOOLEAN
XPathConstants.NUMBER
В случае выбора типа Boolean
, возвращается значение Boolean.TRUE
, если выбран один или более узлов. В другом случае возвращается значение Boolean.FALSE
. Тип String
удобен для получения данных из текстовых узлов, узлов атрибутов, узлов комментариев или узлов инструкции обработки. Наконец, при выборе типа Number
, производится попытка преобразования узла текста в тип double
.
Для XML документа, представленного в начале вы можете применить следующий код для выбора элемента year в виде узла, строки и числа:
try {
DocumentBuilder builder =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = builder.parse(new File("myXMLDocument.xml"));
XPath xpath = XPathFactory.newInstance().newXPath();
String expression = "/birthdate/year";
Node birthdateNode = (Node) xpath.evaluate(expression, document, XPathConstants.NODE);
System.out.println("Node is: " + birthdateNode);
String birthdateString = (String) xpath.evaluate(expression, document, XPathConstants.STRING);
System.out.println("String is: " + birthdateString);
Double birthdateDouble = (Double) xpath.evaluate(expression, document, XPathConstants.NUMBER);
System.out.println("Double is: " + birthdateDouble);
} catch (ParserConfigurationException e) {
System.err.println("ParserConfigurationException caught...");
e.printStackTrace();
} catch (XPathExpressionException e) {
System.err.println("XPathExpressionException caught...");
e.printStackTrace();
} catch (SAXException e) {
System.err.println("SAXException caught...");
e.printStackTrace();
} catch (IOException e) {
System.err.println("IOException caught...");
e.printStackTrace();
}
Результатом исполнения данного примера будет:
Node is: [year: null]
String is: 1983
Double is: 1983.0