Проверка правильности XML и оценка XPath в J2SE 5.0

http://java.sun.com/developer/technicalArticles/xml/validationxpath/

Одними из новых особенностей, добавленных в 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() позволяет создавать объекты SchemaFactory используя заданную 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.

Таблица 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.

Таблица 2. Некоторые операторы XPath
Путь Описание
../author Выбирает все элементы author, являющиеся дочерними для контекста узла.
* Выбирает все дочерние элементы контекста узла
*/author Выбирает все потомков элемента author для текущего контекста узла
/book/author Выбирает все элементы authorявляющиеся дочерними для элемента book, которые в свою очередь являются дочерними для корневого узла документа.
./book/author Выбирает все элементы authorявляющиеся дочерними для элемента book, которые в свою очередь являются дочерними для для текущего контекста узла.

Кроме элементов, пути XPath могут работать с атрибутами, текстом, комментариями и другими узлами DOM дерева. В таблице 3 приведено несколько примеров.

Таблица 3. Примеры использования путей XPath
Путь Описание
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

Исходный код

Здесь вы можете загрузить исходный код данных примеров.
Теги: xml xpath