В текстовых блоках нельзя использовать символы
<, >, &
</description>
Раздел CDATAЕсли необходимо включить в XML-документ данные (в качестве содержимого элемента), которые содержат символы '<', '>', '&', '‘' и '“', чтобы не заменять их на соответствующие определения, можно все эти данные включить в раздел CDATA. Раздел CDATA начинается со строки "<[CDATA[", а заканчивается "]]>", при этом между ними эти строки не должны употребляться. Объявить раздел CDATA можно, например, так:<data><[CDATA[ 5 < 7 ]]></data>Корректность XML-документа определяют следующие два компонента:
· синтаксическая корректность (well-formed): то есть соблюдение всех синтаксических правил XML;
· действительность (valid): то есть данные соответствуют некоторому набору правил, определённых пользователем; правила определяют структуру и формат данных в XML. Валидность XML документа определяется наличием DTD или XML-схемы XSD и соблюдением правил, которые там приведены.
DTD
Для описания структуры XML-документа используется язык описания DTD (Document Type Definition). В настоящее время DTD практически не используется и повсеместно замещается XSD. DTD может встречаться в достаточно старых приложениях, использующих XML и, как правило, требующих нововведений (upgrade).
DTD определяет, какие теги (элементы) могут использоваться в XML-документе, как эти элементы связаны между собой (например, указывать на то, что элемент <student> включает дочерние элементы <name>, <telephone> и <address>), какие атрибуты имеет тот или иной элемент.
Это позволяет наложить четкие ограничения на совокупность используемых элементов, их структуру, вложенность.
Наличие DTD для XML-документа не является обязательным, поскольку возможна обработка XML и без наличия DTD, однако в этом случае отсутствует средство контроля действительности (validness) XML-документа, то есть правильности построения его структуры.
Чтобы сформировать DTD, можно создать либо отдельный файл и описать в нем структуру документа, либо включить DTD-описание непосредственно в документ XML.
В первом случае в документ XML помещается ссылка на файл DTD:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><! DOCTYPE students SYSTEM "students.dtd">Во втором случае описание элемента помещается в XML-документ:
<?xml version="1.0" ?>
<! DOCTYPE student [<!ELEMENT student (name, telephone, address)><!--далее идет описание элементов name, telephone, address -->]>Описание элементаЭлемент в DTD описывается с помощью дескриптора !ELEMENT, в котором указывается название элемента и его содержимое. Так, если нужно определить элемент <student>, у которого есть дочерние элементы <name>, <telephone> и <address>, можно сделать это следующим образом:<!ELEMENT name (#PCDATA)>
<!ELEMENT telephone (#PCDATA)>
<!ELEMENT address (country, city, street)>
В данном случае были определены три элемента: name, telephoneи addressи описано их содержимое с помощью маркера PCDATA. Это говорит о том, что элементы могут содержать любую информацию, с которой может работать программа-анализатор (PCDATA– parsed character data). Есть также маркеры EMPTY– элемент пуст и ANY – содержимое специально не описывается.
При описании элемента <student>, было указано, что он состоит из дочерних элементов <name>, <telephone> и <address>. Можно расширить это описание с помощью символов ‘+’(один или много), ‘*’(0 или много), ‘?’(0 или 1), используемых для указания количества вхождений элементов. Так, например,
<!ELEMENT student (name, telephone, address)>означает, что элемент student содержит один и только один элемент name, telephone и address. Если существует несколько вариантов содержимого элементов, то используется символ ‘|’ (или). Например:<!ELEMENT student (#PCDATA | body)>В данном случае элемент studentможет содержать либо дочерний элемент body, либо PCDATA.Описание атрибутов
Атрибуты элементов описываются с помощью дескриптора !ATTLIST, внутри которого задаются имя атрибута, тип значения, дополнительные параметры и имеется следующий синтаксис:<!ATTLIST название_елемента название_атрибута тип_атрибута значение_по_умолчанию >Например:<!ATTLIST student
login ID #REQUIRED
faculty CDATA #REQUIRED>
В данном случае у элемента <student>определяются два атрибута: login, faculty. Существует несколько возможных значений атрибута, это:
CDATA – значением атрибута является любая последовательность символов;
ID – определяет уникальный идентификатор элемента в документе;
IDREF (IDREFS) – значением атрибута будет идентификатор (список идентификаторов), определенный в документе;
ENTITY (ENTITIES) – содержит имя внешней сущности (несколько имен, разделенных запятыми);
NMTOKEN(NMTOKENS) – слово (несколько слов, разделенных пробелами).
Опционально можно задать значение по умолчанию для каждого атрибута. Значения по умолчанию могут быть следующими:
#REQUIRED – означает, что атрибут должен присутствовать в элементе;
#IMPLIED – означает, что атрибут может отсутствовать, и если указано значение по умолчанию, то анализатор подставит его.
#FIXED – означает, что атрибут может принимать лишь одно значение, то, которое указано в DTD.
defaultValue –значение по умолчанию, устанавливаемое парсером при отсутствии атрибута. Если атрибут имеет параметр #FIXED, то за ним должно следовать defaultValue.
Если в документе атрибуту не будет присвоено никакого значения, то его значение будет равно заданному в DTD. Значение атрибута всегда должно указываться в кавычках.
Определение сущности
Сущность (entity) представляет собой некоторое определение, чье содержимое может быть повторно использовано в документе. Описывается сущность с помощью дескриптора !ENTITY:
<!ENTITY company 'Sun Microsystems'><sender>&company;</sender>Программа-анализатор, которая будет обрабатывать файл, автоматически подставит значение Sun Microsystems вместо &company.
Для повторного использования содержимого внутри описания DTD используются параметрические (параметризованные) сущности.
<!ENTITY % elementGroup “firstName, lastName,gender, address, phone”>
<!ELEMENT employee (%elementGroup;)>
<!ELEMENT contact (%elementGroup)>
В XML включены внутренние определения для символов. Кроме этого, есть внешние определения, которые позволяют включать содержимое внешнего файла:
<!ENTITY logotype SYSTEM "/image.gif" NDATA GIF87A>Файл DTD для документа students.xml будет иметь вид:
<?xml version='1.0' encoding='UTF-8'?>
<!ELEMENT students (student)+>
<!ELEMENT student (name, telephone, address)>
<!ATTLIST student
login ID #REQUIRED
faculty CDATA #REQUIRED
>
<!ELEMENT name (#PCDATA)>
<!ELEMENT telephone (#PCDATA)>
<!ELEMENT address (country, city, street)>
<!ELEMENT country (#PCDATA)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT street (#PCDATA)>
Схема XSD
Схема XSD представляет собой более строгое, чем DTD, описание XML-документа. XSD-схема, в отличие от DTD, сама является XML-документом и поэтому более гибкая для использования в приложениях, задания правил документа, дальнейшего расширения новой функциональностью. В отличиеи от DTD, эта схема содержит много базовых типов (44 типа) и имеет поддержку пространств имен (namespace). С помощью схемы XSD можно также проверить документ на корректность.
Схема XSD первой строкой должна содержать декларацию XML
<?xml version="1.0" encoding="UTF-8"?>Любая схема своим корневым элементом должна содержать элемент schema.
Для создания схемы нужно описать все элементы: их тип, количество повторений, дочерние элементы. Сам элемент создается элементом element, который может включать следующие атрибуты:
ref – ссылается на определение элемента, находящегося в другом месте;
name – определяет имя элемента;
type – указывает тип элемента;
minOccursи maxOccurs – количество повторений этого элемента (по умолчанию 1), чтобы указать, что количество элементов неограниченно, в атрибуте maxOccurs нужно задать unbounded.
Если стандартные типы не подходят, можно создать свой собственный тип элемента. Типы элементов делятся на простые и сложные. Различия заключаются в том, что сложные типы могут содержать другие элементы, а простые – нет.
Простые типы
Элементы, которые не имеют атрибутов и дочерних элементов, называются простыми и должны иметь простой тип данных.
Существуют стандартные простые типы, например string (представляет строковое значение), boolean (логическое значение), integer (целое значение), float (значение с плавающей точкой), ID (идентификатор) и др. Также простые типы можно создавать на основе существующих типов посредством элемента simpleType. Атрибут name содержит имя типа.
Все типы в схеме могут быть объявлены как локально внутри элемента, так и глобально с использованием атрибута name для ccылки на тип в любом месте схемы. Для указания основного типа используется элемент restriction. Его атрибут base указывает основной тип. В элемент restriction можно включить ряд ограничений на значения типа:
minInclusive – определяет минимальное число, которое может быть значением этого типа;
maxInclusive – максимальное значение типа;
length – длина значения;
pattern – определяет шаблон значения;
enumeration – служит для создания перечисления.
Следующий пример описывает тип Login, производный от ID и отвечающий заданному шаблону в элементе pattern.
<simpleType name="Login">
<restriction base="ID">
<pattern value="[a-zA-Z]{3}[a-zA-Z0-9_]+"/>
</restriction>
</simpleType>
Сложные типы
Элементы, содержащие в себе атрибуты и/или дочерние элементы, называются сложными.
Сложные элементы создаются с помощью элемента complexType. Так же как и в простом типе, атрибут name задает имя типа. Для указания, что элементы должны располагаться в определенной последовательности, используется элемент sequence. Он может содержать элементы element, определяющие содержание сложного типа. Если тип может содержать не только элементы, но и текстовую информацию, необходимо задать значение атрибута mixed в true. Кроме элементов, тип может содержать атрибуты, которые создаются элементом
attribute. Атрибуты элемента attribute: name – имя атрибута, type – тип значения атрибута. Для указания, обязан ли использоваться атрибут, нужно использовать атрибут use, который принимает значения required, optional, prohibited. Для установки значения по умолчанию используется атрибут default , а для фиксированного значения – атрибут fixed.
Следующий пример демонстрирует описание типа Student:
<complexType name="Student">
<sequence>
<element name="name" type="string"/>
<element name="telephone" type="decimal"/>
<element name="address" type="tns:Address"/>
</sequence>
<attribute name="login" type="tns:Login"
use="required"/>
<attribute name="faculty" type="string"
use="required"/>
</complexType>
Для объявления атрибутов в элементах, которые могут содержать только текст, используются элемент simpleContent и элемент extension, с помощью которого расширяется базовый тип элемента атрибутом(ами).
<element name="Student">
<complexType>
<simpleContent>
<extension base="string">
<attribute name="birthday" type="string"/>
</extension>
</simpleContent>
</complexType>
</element>
Для расширения/ограничения ранее объявленных сложных типов используется элемент complexContent.
<complexType name=”personType”>
<sequence>
<element name=”firstName” type=”string”/>
<element name=”lastName” type=”string”/>
<element name=”address” type=”string”/>
</sequence>
</complexType>
<complexType name=”studentType”>
<complexContent>
<extension base=”personType”>
<sequence>
<element name=”course” type=”integer”/>
<element name=”faculty” type=”string”/>
</sequence>
</extesion>
</complexContent>
</complexType>
<element name=”Student” type=”studentType”/>
Для задания порядка следования элементов в XML используются такие теги, как <all>, который допускает любой порядок.
<element name=”person”>
<complexType>
<all>
<element name=”firstName” type=”string”/>
<element name=”lastName” type=”string”/>
</all>
</complexType>
</element>
Элемент <choice> указывает, что в XML может присутствовать только один из перечисленных элементов. Элемент <sequence> задает строгий порядок дочерних элементов.
Для списка студентов XML-схема students.xsd может выглядеть следующим образом:
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.com/Students"
xmlns:tns="http://www.example.com/Students">
<element name="students">
<complexType>
<sequence>
<element name="student" type="tns:Student" minOccurs="1" maxOccurs="unbounded" />
</sequence>
</complexType>
</element>
<complexType name="Student">
<sequence>
<element name="name" type="string" />
<element name="telephone" type="decimal" />
<element name="address" type="tns:Address" />
</sequence>
<attribute name="login" type="tns:Login" use="required" />
<attribute name="faculty" type="string" use="required" />
</complexType>
<simpleType name="Login">
<restriction base="ID">
<pattern value="[a-zA-Z]{3}[a-zA-Z0-9_]*"/>
</restriction>
</simpleType>
<complexType name="Address">
<sequence>
<element name="country" type="string" />
<element name="city" type="string" />
<element name="street" type="string" />
</sequence>
</complexType>
</schema>
В приведенном примере используется понятие пространства имен namespace. Пространство имен введено для разделения наборов элементов с соответствующими правилами, описанными схемой. Пространство имен объявляется с помощью атрибута xmlns и префикса, который используется для элементов из данного пространства.
Например, xmlns="http://www.w3.org/2001/XMLSchema" задает пространство имен по умолчанию для элементов, атрибутов и типов схемы, которые принадлежат пространству имен "http://www.w3.org/2001/XMLSchema" и описаны соответствующей схемой.
Атрибут targetNamespace="http://www.example.com/Students" задает пространство имен для элементов/атрибутов, которые описывает данная схема.
Атрибут xmlns:tns="http://www.example.com/Students" вводит префикс для пространства имен (элементов) данной схемы. То есть для всех элементов, типов, описанных данной схемой и используемых здесь же требуется использовать префикс tns, как в случае с типами – tns:Address, tns:Login и т.д.
Действие пространства имён распространяется на элемент, где он объявлен, и на все дочерние элементы.
Тогда для проверки документа объекту-парсеру следует дать указание использовать DTD или схему XSD, и в XML-документ вместо ссылки на DTD добавить вместо корневого элемента <students> элемент <tns:students> вида:
<tns:students xmlns:tns="http://www.example.com/Students"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.com/Students students.xsd ">
Следующий пример выполняет проверку документа на корректность средствами языка Java.
/* пример # 13 : проверка корректности документа XML: XSDMain.java */
package chapt16.xsd;
import java.io.IOException;
import org.xml.sax.SAXException;
import org.apache.xerces.parsers.DOMParser;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import chapt16.xsd.MyErrorHandler;
public class XSDMain {
public static void main(String[] args) {
String filename = "students.xml";
DOMParser parser = new DOMParser();
try {
// установка обработчика ошибок
parser.setErrorHandler(new MyErrorHandler("log.txt"));
// установка способов проверки с использованием XSD
parser.setFeature(
"http://xml.org/sax/features/validation", true);
parser.setFeature(
"http://apache.org/xml/features/validation/schema", true);
parser.parse(filename);
} catch (SAXNotRecognizedException e) {
e.printStackTrace();
System.out.print("идентификатор не распознан");
} catch (SAXNotSupportedException e) {
e.printStackTrace();
System.out.print("неподдерживаемая операция");
} catch (SAXException e) {
e.printStackTrace();
System.out.print("глобальная SAX ошибка ");
} catch (IOException e) {
e.printStackTrace();
System.out.print("ошибка I/O потока");
}
System.out.print("проверка " + filename + " завершена");
}
}
Класс обработчика ошибок может выглядеть следующим образом:
/* пример # 14 : обработчик ошибок : MyErrorHandler.java */package chapt16.xsd;
import java.io.IOException;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXParseException;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;
public class MyErrorHandler implements ErrorHandler {
private Logger logger;
public MyErrorHandler(String log) throws IOException {
//создание регистратора ошибок chapt16.xsd
logger = Logger.getLogger("chapt16.xsd");
//установка файла и формата вывода ошибок
logger.addAppender(new FileAppender(
new SimpleLayout(), log));
}
public void warning(SAXParseException e) {
logger.warn(getLineAddress(e) + "-" +
e.getMessage());
}
public void error(SAXParseException e) {
logger.error(getLineAddress(e) + " - "
+ e.getMessage());
}
public void fatalError(SAXParseException e) {
logger.fatal(getLineAddress(e) + " - "
+ e.getMessage());
}
private String getLineAddress(SAXParseException e) {
//определение строки и столбца ошибки
return e.getLineNumber() + " : "
+ e.getColumnNumber();
}
}
Чтобы убедиться в работоспособности кода, следует внести в исходный XML-документ ошибку. Например, сделать идентичными значения атрибута login. Тогда в результате запуска в файл будут выведены следующие сообщения обработчика об ошибках:
ERROR - 14 : 41 - cvc-id.2: There are multiple occurrences of ID value 'mit'.
ERROR - 14 : 41 - cvc-attribute.3: The value 'mit' of attribute 'login' on element 'student' is not valid with respect to its type, 'login'.
Если допустить синтаксическую ошибку в XML-документе, например, удалить закрывающую скобку в элементе telephone, будет выведено сообщение о фатальной ошибке:
FATAL - 7 : 26 - Element type "telephone2456474" must be followed by either attribute specifications, ">" or "/>".
В Java разработаны серьезные способы взаимодействия с XML. Начиная
с версии Java 6, эти механизмы включены в JDK.
Следующий пример на основе внутреннего класса создает структуру документа XML и сохраняет в ней объект.
/* пример # 15 : создание XML-документа на основе объекта: DemoJSR.java */package chapt16;
import java.io.*;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
public class DemoJSR {
public static void main(String[] args) {
try {
JAXBContext context =
JAXBContext.newInstance(Student.class);
Marshaller m = context.createMarshaller();
Student s = new Student(1, "Bender");//объект
m.marshal(s, new FileOutputStream("stud.xml"));
} catch (FileNotFoundException e) {
System.out.println("XMl-файл не найден");
e.printStackTrace();
} catch (JAXBException e) {
System.out.println("JAXB-исключения");
e.printStackTrace();
}
}
@XmlRootElement
private static class Student {//внутренний класс
private int id;
private String name;
public Student() {
}
public Student(int id, String name) {
this.id = id;
this.name = name;
}
public int getID() {
return id;
}
public String getName() {
return name;
}
public void setID(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
}
}
В результате компиляции и запуска программы будет создан XML-документ :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <student>
<ID>1</ID>
<name>Bender</name>
</student>
Возможно обратное создание на основе XML-схемы классов на языке Java:
/* пример # 16 : описание классов University, Course и перечисления Faculty в XSD-схеме: student.xsd*/<schema xmlns="http://www.w3c.org/2001/XMLSchema"
xmlns:Revealed="http://www.university.net"
targetNamespace="http://www.university.net">
<element name="University">
<complexType>
<sequence>
<element name="faculty" type="Revealed:Faculty"/>
<element name="course" type="Revealed:Course"/>
</sequence>
</complexType>
</element>
<complexType name="Course">
<sequence>
<element name="login" type="string"/>
<element name="name" type="string"/>
<element name="telephone" type="string"/>
</sequence>
</complexType>
<simpleType name="Faculty">
<restriction base="string">
<enumeration value="FPMI"></enumeration>
<enumeration value="MMF"></enumeration>
<enumeration value="Geo"></enumeration>
</restriction>
</simpleType>
</schema>
Запуск выполняется с помощью командной строки:
Xjc student.xsd
В результате будет сгенерирован следующий код классов:
package net.university;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
@XmlEnum
public enum Faculty {
FPMI("FPMI"),
MMF("MMF"),
@XmlEnumValue("Geo")
GEO_F("Geo");
private final String value;
Faculty(String v) {
value = v;
}
public String value() {
return value;
}
public static Faculty fromValue(String v) {
for (Faculty c: Faculty.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v.toString());
}
}
package net.university;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for Course complex type.
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Course", propOrder = {
"login",
"name",
"telephone"
})
public class Course {
@XmlElement(required = true)
protected String login;
@XmlElement(required = true)
protected String name;
@XmlElement(required = true)
protected String telephone;
public String getLogin() {
return login;
}
public void setLogin(String value) {
this.login = value;
}
public String getName() {
return name;
}
public void setName(String value) {
this.name = value;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String value) {
this.telephone = value;
}
}
package net.university;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"faculty",
"course"
})
@XmlRootElement(name = "University")
public class University {
@XmlElement(required = true)
protected Faculty faculty;
@XmlElement(required = true)
protected Course course;
public Faculty getFaculty() {
return faculty;
}
public void setFaculty(Faculty value) {
this.faculty = value;
}
public Course getCourse() {
return course;
}
public void setCourse(Course value) {
this.course = value;
}
}
package net.university;
import javax.xml.bind.annotation.XmlRegistry;
@XmlRegistry
public class ObjectFactory {
public ObjectFactory() {
}
public Course createCourse() {
return new Course();
}
public University createUniversity() {
return new University();
}
}
XML-анализаторы
XML как набор байт в памяти, запись в базе или текстовый файл представляет собой данные, которые еще предстоит обработать. То есть из набора строк необходимо получить данные, пригодные для использования в программе. Поскольку ХML представляет собой универсальный формат для передачи данных, существуют универсальные средства его обработки – XML-анализаторы (парсеры).
Парсер – это библиотека (в языке Java: класс), которая читает XML-документ, а затем предоставляет набор методов для обработки информации этого документа.