Лабораторна робота 3
Робота з XML-документами
1 Завдання на лабораторну роботу
1.1 Індивідуальне завдання
Спроектувати та реалізувати класи для представлення сутностей лабораторної роботи № 5 курсу "Основи програмування" попереднього семестру. Слід створити два похідних класи від класу, який представляє основну сутність. Перший клас повинен підтримувати читання даних з відповідно підготовленого XML-документу, зберігання даних в структурах, які автоматично створюються за допомогою технології зв'язування даних, та запис даних в інший XML-документ після сортування. Для уникнення дублювання даних у програмі слід також перевизначити клас, який представляє другу сутність. Другий похідний клас для представлення основної сутності повинен реалізовувати XML-серіалізацію створених раніше об'єктів, десеріалізацію, а також сортування і серіалізацію в інший файл. Похідні класи повинні реалізовувати спільний інтерфейс, створений під час виконання індивідуального завдання лабораторної роботи № 1.
Окрім роботи з файлами повинно бути реалізоване виведення результатів у консольне вікно.
1.2 Використання технологій SAX і DOM
Підготувати XML-документ з даними про студентів академічної групи. За допомогою технології SAX здійснити читання даних з XML-документу і виведення даних на консоль. За допомогою технології DOM здійснити читання даних з того ж XML-документу, модифікацію даних і запис їх в новий документ.
1.3 Реалізація серіалізації й десеріалізації в XML
Описати класи Студент і Академічна група (з полем - масивом студентів). Створити об'єкти, здійснити їх серіалізацію й десеріалізацію в XML.
2 Методичні вказівки
2.1 Загальні концепції використання мови XML
Розширювана мова розмічування XML (eXtensible Markup Language) - це незалежний від платформи метод структурування інформації. Оскільки XML відокремлює зміст документу від його структури, його успішно використовують для обміну інформацією. Наприклад, XML можна використовувати для передачі даних між програмою та базами даних, або між базами даних, що мають різні формати.
Файли формату XML - це завжди текстові файли. Синтаксис мови XML багато в чому схожий на синтаксис мови HTML, яка застосовується для розмічування текстів, що публікуються в Internet. Мова XML також може бути безпосередньо застосована для розмітки текстів.
Найчастіше документ у форматі XML починається з так званого префіксу. Префікс для документа в загальному випадку має такий вигляд:
<?xml version="1.0" [інші-атрибути] ?>
Серед можливих атрибутів найбільш корисним є атрибут encoding="кодування"
. Він задає кодування для тексту. Якщо необхідно використовувати звичайні (не UNICODE) символи кирилиці, це можна визначити, наприклад, в такий спосіб:
<?xml version="1.0" encoding="Windows-1251"?>
Після заголовку може міститись інформація про тип документу. Решта XML-документу складається з набору XML-елементів. Елементи розділені тегами. Початкові теги починаються з символу <
з розміщеним далі ім'ям елементу. Кінцеві теги починаються з символів </
, за якими слід ім'я елементу. Початковий і кінцевий теги закінчуються символом >
. Все, що знаходиться між двома тегами, - це вміст елементу. Всі стартові теги повинні бути співставлені кінцевими тегами. Всі значення атрибутів повинні бути вказані в лапках. Документ обов'язково повинен мати кореневий тег, у який вкладають усі інші теги.
На відміну від HTML, XML дозволяє використовувати необмежений набір пар тегів, кожна з яких представляє не те, як включені в неї дані повинні виглядати, а те, що вони означають. XML дозволяє створювати свій набір тегів для кожного класу документів. Таким чином, точніше назвати його не мовою, а метамовою.
Маючи формально описану структуру документа, можна перевірити його коректність. Наявність тегів розмітки дозволяє аналізувати документ як людині, так і програмі. XML-документи, у першу чергу, призначені для програмного аналізу їхнього вмісту.
У наведеному нижче прикладі в XML-файлі зберігаються прості числа.
<?xml version="1.0" encoding="UTF-8"?> <Numbers> <Number>1</Number> <Number>2</Number> <Number>3</Number> <Number>5</Number> <Number>7</Number> <Number>11</Number> </Numbers>
Теги Numbers
та Number
вигадав автор документу. Відступи в тексті файлу використані для поліпшення його сприйняття людиною.
Теги можуть містити в собі атрибути - додаткову інформацію про елементи, яка міститься всередині кутових дужок. Значення атрибутів обов'язково брати у лапки. Наприклад, можна запропонувати тег Message
з атрибутами to
та from
:
<Message to="you" from="me"> <Text> Для чого потрібен XML? </Text> </Message>
Важливим правилом формування XML є обов'язковість вживання кінцевих тегів. Крім того не можна плутати порядок кінцевих тегів. Цей текст містить помилку:
<A> <B> text </A> </B>
Необхідно писати так:
<A> <B> text </B> </A>
Допускається використання порожніх тегів. Такі теги закінчуються символом /. Наприклад, можна вживати <Nothing/>
замість пари <Nothing></Nothing>
.
На відміну від HTML-тегів, XML-теги залежать від регістру, тому <cat>
та <CAT>
- це різні теги.
У XML-документі можна вживати коментарі, синтаксис яких збігається з синтаксисом коментарів HTML-документів:
<!-- Це коментар -->
Програми розпізнавання XML-документів - так звані XML-парсери - здійснюють розбір документа до знаходження першої помилки, на відміну від HTML-парсерів, вбудованих в браузер. Браузери намагаються відобразити документ, навіть, якщо код містить помилки.
XML-документ, який відповідає всім синтаксичним правилам XML, вважається правильно оформленим документом (коректним документом, well-formed document).
2.2 Стандартні підходи до роботи з XML-документами
2.2.1 Загальні концепції. Засоби JAXP
Існує два стандартних підходи до роботи з XML-документами в програмах:
- подіє-орієнтована модель документу (Simple API for XML, SAX), що працює на потоці даних, обробляючи його, коли виникають пов'язані з різними тегами події;
- об'єктна модель документу (Document Object Model, DOM), що дозволяє створити в пам'яті колекцію пов'язаних з тегами об'єктів, організованих в ієрархію.
Базований на подіях підхід не дозволяє розробнику змінювати дані в вихідному документі. В разі необхідності коригування частини даних документ треба повністю оновити. На відміну від нього DOM забезпечує API, який дозволяє розробнику додавати або видаляти вузли в будь-якій точці дерева в застосунку.
Обидва підходи використовують поняття парсеру. Парсер (parser) - це програмний застосунок, призначений для того, щоб аналізувати документ шляхом розділення його на лексеми (tokens). Парсер може ініціювати події (як у SAX), або будувати в пам'яті дерево даних.
Для реалізації стандартних підходів до роботи з XML в Java SE використовують засоби Java API for XML Processing (JAXP, інтерфейс програмування застосунків Java для роботи з XML). JAXP надає засоби валідації і розбору XML-документів. Для реалізації об'єктної моделі документа JAXP включає програмний інтерфейс DOM, SAX реалізований однойменною програмним інтерфейсом. На додаток до них надано програмний інтерфейс Streaming API for XML (StAX, потоковий API для XML), а також засоби XSLT (XML Stylesheet Language Transformations, мова перетворення XML-документів).
2.2.2 Використання Simple API for XML і StAX
Simple API for XML (SAX, простий програмний інтерфейс для роботи з XML) надає послідовний механізм аналізу XML-документу. Аналізатор, який реалізує інтерфейс SAX (SAX Parser), обробляє інформацію з XML документу як єдиний потік даних. Цей потік даних доступний тільки в одному напрямку, тобто, раніше оброблені дані неможливо повторно прочитати без повторного аналізу. Більшість програмістів збігається в думці, що обробка XML документів з використанням SAX, в цілому, швидше, ніж під час використання DOM. Це пояснюється тим, що потік SAX вимагає набагато меншого обсягу пам'яті в порівнянні з побудовою повного дерева DOM.
SAX аналізатори реалізують з використанням підходу, керованого подіями (event-driven approach) коли програмісту необхідно описати оброблювачі подій, які викликаються аналізаторами під час обробки XML-документу.
Засоби Java SE для роботи з SAX реалізовані в пакетах javax.xml.parsers
і org.xml.sax
, а також у вкладених в них пакетах. Для створення об'єкта класу javax.xml.parsers.SAXParser
слід скористатися класом javax.xml.parsers.SAXParserFactory
, що надає відповідні фабричні методи. SAX парсер не створює в пам'яті представлення документу XML. Замість цього, SAX парсер інформує клієнтів про структуру документу XML, використовуючи механізм зворотного виклику. Можна самостійно створити клас, який реалізує низку необхідних інтерфейсів, зокрема org.xml.sax.ContentHandler
. Однак більш простий і рекомендований спосіб - використовувати клас org.xml.sax.helpers.DefaultHandler
, створивши похідний клас і перекривши його методи, які повинні викликатися під час виникнення різних подій в процесі аналізу документу. Найбільш часто перекриваються такі методи:
-
startDocument()
іendDocument()
- методи, які викликаються на початку і наприкінці аналізу XML-документу startElement()
іendElement()
- методи, які викликаються на початку і наприкінці аналізу елемента документу-
characters()
- метод, що викликається під час отримання текстового вмісту елемента XML-документу.
Наведений нижче приклад ілюструє використання SAX для читання документу. Припустимо, в каталозі проекту створено файл Hello.xml такого змісту:
<?xml version="1.0" encoding="UTF-8" ?> <Greetings> <Hello Text="Привіт, це атрибут!"> Привіт, це текст! </Hello> </Greetings>
Примітка. Під час збереження файлу слід вказати кодування UTF-8.
Код програми, яка читає дані з XML, буде таким:
package ua.in.iwanoff.java.third; import java.io.IOException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; public class HelloSAX extends DefaultHandler { @Override public void startDocument() { System.out.println("Opening document"); } @Override public void endDocument() { System.out.println("Done"); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { System.out.println("Opening tag: " + qName); if (attributes.getLength() > 0) { System.out.println("Атрибути: "); for (int i = 0; i < attributes.getLength(); i++) { System.out.println(" " + attributes.getQName(i) + ": " + attributes.getValue(i)); } } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { System.out.println("Closin tag: " + qName); } @Override public void characters(char[] ch, int start, int length) throws SAXException { String s = new String(ch).substring(start, start + length).trim(); if (s.length() > 0) { System.out.println(s); } } public static void main(String[] args) { SAXParser parser = null; try { parser = SAXParserFactory.newInstance().newSAXParser(); } catch (ParserConfigurationException | SAXException e) { e.printStackTrace(); } if (parser != null) { InputSource input = new InputSource("Hello.xml"); try { parser.parse(input, new HelloSAX()); } catch (SAXException | IOException e) { e.printStackTrace(); } } } }
Оскільки метод characters()
викликається для кожного тегу, вміст є сенс виводити, якщо рядок не порожній.
StAX був розроблений як щось середнє між інтерфейсами DOM і SAX. В даному програмному інтерфейсі використовується метафора курсору, що представляє точку входу в межах документу. Застосунок переміщує курсор вперед, читаючи інформацію, отримуючи інформацію від синтаксичного аналізатора за необхідності.
2.2.3 Використання Об'єктної моделі документу
DOM є серією Рекомендацій, що виробляються Консорціумом World Wide Web (W3C). DOM починалася як спосіб ідентифікації і маніпулювання елементами на HTML-сторінці (DOM Level 0).
Діюча Рекомендація DOM (DOM Level 3) є API, який визначає об'єкти, представлені в XML-документі, а також методи і властивості, які використовуються для доступу до них і маніпулювання ними.
Починаючи з DOM Рівня 1, DOM API містить інтерфейси, які представляють різні типи інформації, що можуть бути знайдені в XML-документі. Він також включає в себе методи, необхідні для роботи з цими об'єктами. Можна навести деякі найбільш вживані методи стандартних інтерфейсів DOM.
Інтерфейс Node
є основним типом даних DOM. Він визначає низку корисних методів для отримання даних про вузли та навігації вузлами:
getFirstChild()
іgetLastChild()
повертають перший або останній дочірній елемент цього вузла;getNextSibling()
іgetPreviousSibling()
повертають наступного або попереднього "брата" цього вузла;getChildNodes()
повертає посилання на список типуNodeList
дочірніх елементів цього вузла; за допомогою методів інтерфейсуNodeList
можна отримати i-й вузол (методitem(i)
) і загальну кількість таких вузлів (методgetLength()
);getParentNode()
повертає "батьківський" вузол;getAttributes()
повертає асоціативний масив типуNamedNodeMap
атрибутів цього вузла;hasChildNodes()
повертаєtrue
, якщо вузол має дочірні елементи.
Існує низка методів, що забезпечують модифікацію XML-документу – insertBefore()
, replaceChild()
, removeChild()
, appendChild()
тощо.
Крім Node
, DOM також визначає кілька підінтерфейсів інтерфейсу Node
:
-
Element
– представляє елемент XML в вихідному документі; в елемент входить пара тегів (що відкривається і що закривається) і весь текст між ними; Attr
- представляє атрибут елемента;Text
– вміст елемента;Document
– представляє весь XML-документ; тільки один об'єкт типуDocument
існує для кожного XML-документу; маючи об'єктDocument
, можна знайти корінь дерева DOM за допомогою методуgetDocumentElement()
; від кореня можна маніпулювати всім деревом.
Додатковими типами вузлів є:
Comment
– представляє коментар в XML-файлі;ProcessingInstruction
– представляє інструкцію обробки;CDATASection
– представляє розділCDATA
.
XML-парсери вимагають створення екземпляра певного класу. Недоліком цього є те, що під час зміни парсеров необхідно змінювати вихідний код. Для деяких парсерів іноді можна використовувати так звані фабричні класи. За допомогою статичного методу newInstance()
створюється екземпляр "фабричного" об'єкта, за допомогою якого створюється об'єкт класу, що реалізує інтерфейс DocumentBuilder
. Такий об'єкт безпосередньо є необхідним парсером: реалізує методи DOM, які потрібні для аналізу і обробки XML-файлу. Під час створення об'єкта-парсера можуть генеруватися винятки, які необхідно перехоплювати. Далі можна створювати об'єкт типу Document
, завантажувати дані з файлу з ім'ям, наприклад, fileName
і здійснювати його аналіз:
try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(fileName); . . .
Після обходу й модифікації дерева його можна зберегти в іншому файлі.
Використання DOM розглянемо на прикладі попереднього файлу (Hello.xml). Наведена нижче програма виводить на консоль текст атрибуту і тексту, змінює їх і зберігає зміни в новому XML-документі:
package ua.in.iwanoff.java.third; import java.io.*; import org.w3c.dom.*; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; public class HelloDOM { public static void main(String[] args) throws Exception { Document doc; // посилання на об'єкт "документ" // Створюємо "будівник документів" за допомогою "фабричного методу": DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); doc = db.parse(new File("Hello.xml")); // Знаходимо кореневий тег: Node rootNode = doc.getDocumentElement(); // Переглядаємо всі "дочірні" теги: for (int i = 0; i < rootNode.getChildNodes().getLength(); i++) { Node currentNode = rootNode.getChildNodes().item(i); if (currentNode.getNodeName().equals("Hello")) { // Переглядаємо всі атрибути: for (int j = 0; j < currentNode.getAttributes().getLength(); j++) { if (currentNode.getAttributes().item(j).getNodeName().equals("Text")) { // Знайшли потрібний атрибут. Виводимо текст атрибуту - вітання: System.out.println(currentNode.getAttributes().item(j).getNodeValue()); // Змінюємо вміст атрибуту: currentNode.getAttributes().item(j).setNodeValue("Привіт, тут був DOM!"); // Подальший пошук є недоцільним: break; } } // Змінюємо текст: System.out.println(currentNode.getTextContent()); currentNode.setTextContent("\n Привіт, тут теж був DOM!\n"); break; } } // Створення об'єкта-перетворювача (в цьому випадку - для запису в файл). // Використовуємо фабричний метод: Transformer transformer = TransformerFactory.newInstance().newTransformer(); // Запис у файл: transformer.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(new File("HelloDOM.xml")))); } }
Після виконання програми у теці проекту можна буде знайти такий файл (HelloDOM.xml):
<?xml version="1.0" encoding="UTF-8" standalone="no"?><Greetings> <Hello Text="Привіт, тут був DOM!"> Привіт, тут теж був DOM! </Hello> </Greetings>
У наведеному прикладі для збереження зміненого документу в файлі використано клас javax.xml.transform.Transformer
. У загальному випадку цей клас використовують для реалізації так званого XSLT-перетворення. XSLT (eXtensible Stylesheet Language Transformations) - мова перетворень XML-документів у інші XML-документи або інші об'єкти, такі як HTML, звичайний текст тощо. XSLT-процесор приймає один або кілька XML-документів джерела, а також один або кілька модулів перетворення, і обробляє їх для отримання вихідного документу. Перетворення містить набір правил шаблону: інструкції та інші директиви, якими керується XSLT-процесор під час генерації вихідного документу.
2.3 Використання схеми документу
Структуровані дані, які можуть бути представленими у формі XML-файлу, потребують додаткової інформації. Найбільш розповсюдженими є два основних формати представлення такої інформації - Визначення шаблону документу (DTD) та Схема документу (XSD).
DTD (Document Template Definition) - набір правил, що дозволяють однозначно визначити структуру певного класу XML-документів. DTD не є XML-документом. DTD дуже простий, але не описує типів елементів. Директиви DTD можуть бути присутніми як у заголовку самого XML-документу (internal DTD), так і в іншому файлі (external DTD). Наявність DTD не є обов'язковою. Проте, XML-документ, що містить DTD, або вказує на нього, вважається "правильним" (valid).
Наприклад, ми маємо такий XML-файл:
<?xml version="1.0" encoding="UTF-8"?> <Pairs> <Pair> <x>1</x> <y>4</y> </Pair> <Pair> <x>2</x> <y>2</y> </Pair> . . . </Pairs>
DTD-файл, який описує структуру цього документа, матиме такий вигляд:
<?xml version="1.0" encoding="UTF-8"?> <!ELEMENT Pair (x, y)> <!ELEMENT x (#PCDATA)> <!ELEMENT y (#PCDATA)> <!ELEMENT Pairs (Pair+)>
Знак плюс в останньому рядку означає що елементів Pair
всередині тегу Pairs
може бути один або багато. Крім того, можна також використовувати * (0 або багато), знак питання (0 або 1). Відсутність знака означає, що може бути присутнім саме один елемент.
XML Schema - це альтернативний DTD спосіб завдання структури документу. Схема зручніше, ніж DTD тим, що опис структури документу виконано на самому XML, у той час як DTD використовує директиви, синтаксис яких не має нічого спільного з синтаксисом XML. Крім того, XML схема своїми можливостями істотно перевершує DTD. Наприклад, у схемі можна вказувати типи тегів та атрибутів, визначати обмеження тощо.
XML-документ, який є правильно оформленим, посилається на граматичні правила та повністю їм відповідає, має назву валідного (valid) документу.
Для того щоб запобігти конфліктам імен тегів, у XML можна створювати так звані простори імен. Простір імен визначає префікс, пов'язаний з певною схемою документу та додається до тегів. Власний простір імен визначається за допомогою конструкції на кшталт такої:
<root xmlns:pref="http://www.someaddress.org/">
У цьому прикладі root
- кореневий тег XML-документу, pref
- префікс, який визначає простір імен, "http://www.someaddress.org/
" - якась адреса, наприклад, доменне ім'я автора схеми. Програми, які обробляють XML-документи, ніколи не перевіряють цю адресу. Вона необхідна лише для забезпечення унікальності простору імен.
Безпосередньо схема використовує простір імен xs
.
Використання схеми документу можна продемонструвати на такому прикладі. Припустимо, ми маємо такий XML-файл:
<?xml version="1.0" encoding="Windows-1251" ?> <Student Name="Джон" Surname="Сміт"> <Marks> <Mark Subject="Математика" Value="4"/> <Mark Subject="Фізика" Value="5"/> <Mark Subject="Програмування" Value="3"/> </Marks> <Comments> Не наш студент </Comments> </Student>
Створення файлу схеми слід починати зі стандартної конструкції:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> </xs:schema>
Проміж тегами <xs:schema>
та </xs:schema>
буде розташована інформація про схему документу. Для того, щоб описати теги документу, всередині його можна додавати стандартні теги. Для складних тегів, у які вкладаються інші, або які мають параметри:
<xs:element name="ім'я тегу"> <xs:complexType> . . . </xs:complexType> </xs:element>
Всередині тегу можна розмістити список елементів:
<xs:sequence> . . . </xs:sequence>
Можна розмістити посилання на інший тег:
<xs:element ref="ім'я іншого тегу"/>
Для елементів, які безпосередньо містять дані, використовують такий тег
<xs:element name="ім'я тегу" type="ім'я типу"/>
Наведена нижче таблиця містить деякі стандартні типи даних, що використовуються в схемі:
Ім'я |
Опис |
xs:string |
Рядок символів як послідовність 10646 символів Unicode або ISO/IEC, включаючи пропуск, символ табуляції, повернення каретки і переведення рядка |
xs:boolean |
Бінарні логічні значення: true або false ,1 або 0. |
xs:float |
32-бітове число з плаваючою комою |
xs:double |
64-бітове число з плаваючою комою |
xs:anyURI |
Універсальний ідентифікатор ресурсу (Uniform Resource Identifier) |
Тег
<xs:attribute name="ім'я атрибуту" type="ім'я типу" />
дозволяє описувати атрибути.
Існує також велика кількість додаткових параметрів тегів. Параметр maxOccurs
задає максимальну кількість входжень елемента, minOccurs
задає мінімальну кількість входжень елемента, unbounded
визначає необмежену кількість входжень, required
визначає обов'язкове входження, mixed
визначає елемент, який має змішаний тип та ін.
Для нашого студента можна запропонувати такий файл схеми (Student.xsd):
<?xml version="1.0"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Student"> <xs:complexType> <xs:sequence> <xs:element name="Comments" type="xs:string"/> <xs:element name="Marks"> <xs:complexType> <xs:sequence> <xs:element ref="Mark" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="Name" type="xs:string" /> <xs:attribute name="Surname" type="xs:string" /> </xs:complexType> </xs:element> <xs:element name="Mark"> <xs:complexType> <xs:attribute name="Subject" type="xs:string" /> <xs:attribute name="Value" type="xs:string" /> </xs:complexType> </xs:element> </xs:schema>
2.4 Технологія зв'язування даних
Використання мови Java передбачає зручний спосіб роботи з XML-файлами - механізм зв'язування даних. Цей механізм передбачає генерацію набору класів, які описують елементи файлу, та створення відповідної структури об'єктів у пам'яті.
Засіб зв'язування даних XML містить компілятор схеми, що транслює схему в набір специфічних для схеми класів з відповідними методами доступу і зміни (тобто get
і set
). Він також містить механізм маршалізації (запису структурованих даних в XML-документ), підтримує демаршалізацію XML документів у відповідну структуру взаємозалежних екземплярів. Автоматично створеною структурою даних можна користуватися без ручного розміщення даних у списках або масивах.
Традиційно першою технологією зв'язування даних була технологія Castor. Пізніше був стандартизований API JAXB (Java Architecture for XML Binding). Версія 2 специфікації JAXB припускає як генерацію класів за схемою, так і генерацію схеми за існуючою структурою класів.
Для підтримки стандарту технологій API JAXB в середовищі Eclipse Oxygen повинні бути встановлені засоби Dali Java Persistence Tools (JAXB Support). Якщо в Eclipse не встановлені необхідні програмні засоби, їх можна додати за допомогою головного меню Eclipse Help | Install New Software, далі в рядку Work with: обираємо Oxygen - http://download.eclipse.org/releases/oxygen
. Далі знаходимо в списку потрібні програмні засоби і натискаємо Next, на сторінці Install Details знову натискаємо Next, далі слід погодитися з умовами ліцензії й натиснути Finish. Після завантаження нового програмного забезпечення слід перезавантажити Eclipse.
Примітка: відповідні плагіни можна також завантажити у попередніх версіях Eclipse, які будуть вказані у рядку Work with:.
Технологія зв'язування даних найбільш часто застосовується для генерації класів за наявною схемою. Спочатку в теці проекту треба створити файл схеми (*.xsd
).
Для роботи генератора класів необхідно використовувати JDK, а не JRE. Якщо засоби JDK не встановлені на комп'ютері, їх можна завантажити зі сторінки http://www.oracle.com/technetwork/java/javase/downloads/index-jsp-138363.html#javasejdk. Після встановлення (копіювання) JDK в опціях Eclipse (Window | Preferences) обираємо Java | Installed JREs, далі Add... | Next | Directory... Необхідно вибрати встановлену раніше JDK і натиснути Finish. Після цього в списку встановлених JRE обираємо JDK, яку слід встановити за умовчанням і натиснути OK.
Якщо проект було створено раніше, йому слід встановити JDK як JRE за умовчанням в опціях проекту У головному меню викликаємо функцію Project | Properties | Java Build Path далі на закладці Libraries обираємо JRE System Library, натискуємо кнопку Edit... , переключаємо System Library в Alternate JRE, у відповідному рядку вибираємо встановлену раніше JDK.
Для нормальної роботи з XML-документами, які містять кириличні символи, в опціях проекту слід встановити кодову таблицю UTF-8 (Project | Properties | Resource | Text file encoding | Other та вибрати UTF-8).
Далі необхідно обрати файл xsd
в дереві Package Explorer. У контекстному меню вибираємо Generate | JAXB Classes. Далі у вікні майстра генерації класів вказуємо проект, пакет та інші додаткові відомості, якщо необхідно. У разі успішного завершення генерації в зазначеному пакеті з'являться згенеровані класи.
Розглянемо такий приклад. В теці проекту створюємо, XML-документ Hello.xml (New | File контекстного меню проекту). Цей файл бажано відкрити за допомогою текстового редактора (Open With | Text Editor) і додати такий текст:
<?xml version="1.0" encoding="UTF-8" ?> <Greetings> <Hello Text="Привіт, XML!" /> </Greetings>
Цьому документу відповідає файл схеми Hello.xsd, який ми також створюємо в теці проекту. Цей файл також бажано відкрити за допомогою текстового редактора і додати такий текст:
<?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Greetings"> <xs:complexType> <xs:sequence> <xs:element name="Hello"> <xs:complexType> <xs:attribute name="Text" type="xs:string" use="required" /> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
Засобами Dali Java Persistence Tools здійснюємо генерацію класів. У дереві проекту (у відповідному пакеті) з'являються файли Greetings.java
і ObjectFactory.java
.
Клас Greetings
представляє кореневий тег XML-документу й містить всередині вкладений клас Hello
. Взагалі всім вкладеним тегам відповідають вкладені класи, розташовані всередині класу, який відповідає за кореневий тег. Відповідно клас Greetings
містить поле типу Greetings.Hello
і надає методи getHello()
і setHello()
. Щодо класу Greetings.Hello
, то відповідно до схеми XML-документу цей клас містить поле text
типу String для представлення відповідного атрибуту, а також функції getText()
і setText()
. Анотації в коді керують поданням даних в XML-документі.
Клас ObjectFactory
надає так звані фабричні методи для створення об'єктів згенерованих класів: createGreetings()
і createGreetingsHello()
. Оскільки під час генерації класів завжди створюється клас з ім'ям ObjectFactory
, класи, які відповідають різним схемам, слід розташовувати в різних пакетах.
Тепер у функції main()
можна здійснити дії щодо завантаження документа , читання і зміни значення атрибута і запису в новий файл:
package ua.in.iwanoff.java.third; import java.io.*; import javax.xml.bind.*; public class HelloJAXB { public static void main(String[] args) { try { // Через об'єкт класу JAXBContext забезпечується доступ до JAXB API: JAXBContext jaxbContext = JAXBContext. newInstance("ua.in.iwanoff.java.third"); // пакет з необхідними класами // Зчитуємо дані з файлу й завантажуємо в об'єкт згенерованого класу: Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); Greetings greetings = (Greetings)unmarshaller.unmarshal(new FileInputStream("Hello.xml")); // Виводимо старе значення атрибута: System.out.println(greetings.getHello().getText()); // Змінюємо значення атрибута: greetings.getHello().setText("Привіт, JAXB!"); // Створюємо об'єкт-Marshaller для виведення в файл: Marshaller marshaller = jaxbContext.createMarshaller(); // "Включаємо" форматування: marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); // Зберігаємо об'єкт у новому файлі: marshaller.marshal(greetings, new FileWriter("HelloJAXB.xml")); } catch (JAXBException | IOException e) { e.printStackTrace(); } } }
Новий файл HelloJAXB.xml буде таким:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Greetings> <Hello Text="Привіт, JAXB!"/> </Greetings>
Як видно з прикладу, технологія зв'язування даних забезпечує більш якісне форматування XML-документа.
Примітка: Для підтримки стандартних технологій API JAXB в середовищі IntelliJ IDEA Community Edition необхідно виконати деякі налаштування. Один із шляхів реалізації технології JAXB - підключення утиліти xjc.exe
, що входить в набір засобів JDK. Цю утиліту можна запускати в командному рядку, проте доцільніше налаштувати контекстне меню. У вікні Settings обираємо Tools | External Tools і натискаємо кнопку "+". У діалоговому вікні Edit Tool вводимо ім'я (Name:) нової команди Generate JAXB Classes
, шлях до утиліти xjc.exe
(Program:), який на конкретному комп'ютері слід вибрати в діалоговому вікні вибору файлів (кнопка "...") і параметри (Parameters:), які в нашому випадку будуть такими:
-p $FileFQPackage$ -d "$SourcepathEntry$" "$FilePath$"
Для того, щоб створена команда працювала коректно, файл схеми слід розташувати у новому пакеті, в якому потім з'являться згереновані файли.
2.5 Серіалізація в XML-файли
Головним недоліком описаної раніше бінарної серіалізації є необхідність роботи з двійковими (нетекстовими) файлами. Зазвичай такі файли використовують не для довгострокового зберігання даних, а для одноразового зберігання і відновлення об'єктів. Безумовно, більш зручною й керованою є серіалізація в текстовий файл, зокрема, в XML-документ. Існує декілька підходів до серіалізації й десеріалізації, побудованої на XML. Найбільш простим є підхід, заснований на використанні класів java.beans.XMLEncoder
і java.beans.XMLDecoder
. Найбільш природне застосування цих класів - зберігання та відтворення елементів графічного інтерфейсу. Але можна також зберігати об'єкти інших класів, які відповідають специфікації Java Beans.
Java Bean -
це клас, що відповідає таким вимогам:
- клас відкритий (
public
) - відсутні відкриті дані (відкритими можуть бути тільки методи)
- клас повинен містити усталений конструктор (конструктор без аргументів)
- клас повинен реалізовувати інтерфейс
java.io.Serializable
- пара методів з іменами
setNnn()
іgetNnn()
утворюють властивість з іменемnnn
і відповідним типом. Для властивостей типуboolean
використовують "is
" замість "get
".
Раніше були реалізовані класи Line
і Point
. XML-серіалізація не вимагає реалізації інтерфейсу Serializable
. Однак класи повинні бути відкритими, мати відкриті функції доступу (геттери і сеттери) до закритих полів. Клас Point
:
package ua.in.iwanoff.java.third; public class Point { private double x, y; public void setX(double x) { this.x = x; } public void setY(double y) { this.y = y; } public double getX() { return x; } public double getY() { return y; } }
Клас Line
:
package ua.in.iwanoff.java.third; public class Line { private Point first = new Point(), second = new Point(); public void setFirst(Point first) { this.first = first; } public Point getFirst() { return first; } public Point getSecond() { return second; } public void setSecond(Point second) { this.second = second; } }
Можна запропонувати такий код, який забезпечує XML-серіалізацію:
package ua.in.iwanoff.java.third; import java.beans.XMLEncoder; import java.io.*; public class XMLSerialization { public static void main(String[] args) { Line line = new Line(); line.getFirst().setX(1); line.getFirst().setY(2); line.getSecond().setX(3); line.getSecond().setY(4); try (XMLEncoder xmlEncoder = new XMLEncoder(new FileOutputStream("Line.xml"))) { xmlEncoder.writeObject(line); xmlEncoder.flush(); } catch (IOException e) { e.printStackTrace(); } } }
Після виконання програми ми отримаємо такий XML-файл:
<?xml version="1.0" encoding="UTF-8"?> <java version="1.8.0_66" class="java.beans.XMLDecoder"> <object class="ua.in.iwanoff.labs.fifth.Line"> <void property="first"> <void property="x"> <double>1.0</double> </void> <void property="y"> <double>2.0</double> </void> </void> <void property="second"> <void property="x"> <double>3.0</double> </void> <void property="y"> <double>4.0</double> </void> </void> </object> </java>
Тепер можна здійснити десеріалізацію за допомогою такого коду:
package ua.in.iwanoff.java.third; import java.beans.XMLDecoder; import java.io.*; public class XMLDeserialization { public static void main(String[] args) { try (XMLDecoder xmlDecoder = new XMLDecoder(new FileInputStream("Line.xml"))) { Line line = (Line)xmlDecoder.readObject(); System.out.println(line.getFirst().getX() + " " + line.getFirst().getY() + " " + line.getSecond().getX() + " " + line.getSecond().getY()); } catch (IOException e) { e.printStackTrace(); } } }
Існують також інші (нестандартні) реалізації XML-серіалізації.
3 Приклади програм
3.1 Використання технології DOM
Припустимо, підготовлений XML-документ з даними про континент (Continent.xml):
<?xml version="1.0" encoding="UTF-8"?> <ContinentData Name="Європа"> <CountriesData> <CountryData Name="Україна" Area="603700" Population="46314736" > <CapitalData Name="Київ" /> </CountryData> <CountryData Name="Франція" Area="547030" Population="61875822" > <CapitalData Name="Москва" /> </CountryData> <CountryData Name="Німеччина" Area="357022" Population="82310000" > <CapitalData Name="Берлін" /> </CountryData> </CountriesData> </ContinentData>
Примітка: помилка зі столицею Франції зроблена навмисне.
Необхідно засобами DOM прочитати дані, виправити помилку і зберегти в новому файлі. Програма матиме такий вигляд:
package ua.in.iwanoff.java.third; import java.io.*; import org.w3c.dom.*; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; public class ContinentWithDOM { public static void main(String[] args) { try { Document doc; DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); doc = db.parse(new File("Continent.xml")); Node rootNode = doc.getDocumentElement(); mainLoop: for (int i = 0; i < rootNode.getChildNodes().getLength(); i++) { Node countriesNode = rootNode.getChildNodes().item(i); if (countriesNode.getNodeName().equals("CountriesData")) { for (int j = 0; j < countriesNode.getChildNodes().getLength(); j++) { Node countryNode = countriesNode.getChildNodes().item(j); if (countryNode.getNodeName().equals("CountryData")) { // Знаходимо атрибут за іменем: if (countryNode.getAttributes().getNamedItem("Name").getNodeValue().equals("Франція")) { for (int k = 0; k < countryNode.getChildNodes().getLength(); k++) { Node capitalNode = countryNode.getChildNodes().item(k); if (capitalNode.getNodeName().equals("CapitalData")) { capitalNode.getAttributes().getNamedItem("Name").setNodeValue("Париж"); break mainLoop; } } } } } } } Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.transform(new DOMSource(doc), new StreamResult(new FileOutputStream(new File("CorrectedConinent.xml")))); } catch (Exception e) { e.printStackTrace(); } } }
2.6 Використання JAXB
Попередню задачу можна розв'язати за допомогою технології JAXB. Для JAXB-класів створюємо окремий вкладений пакет continent
всередині поточного пакету. У нього поміщаємо схему документу (Continent.xsd):
<?xml version="1.0" encoding="utf-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="ContinentData"> <xs:complexType> <xs:sequence> <xs:element name="CountriesData"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name="CountryData"> <xs:complexType> <xs:sequence> <xs:element name="CapitalData"> <xs:complexType> <xs:attribute name="Name" type="xs:string" use="required" /> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="Name" type="xs:string" use="required" /> <xs:attribute name="Area" type="xs:unsignedInt" use="required" /> <xs:attribute name="Population" type="xs:unsignedInt" use="required" /> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="Name" type="xs:string" use="required" /> </xs:complexType> </xs:element> </xs:schema>
Тепер можна згенерувати класи. Програма, що використовує ці класи, матиме такий вигляд:
package ua.in.iwanoff.java.third; import java.io.*; import javax.xml.bind.*; import ua.in.iwanoff.java.third.continent.*; public class ContinentWithJAXB { public static void main(String[] args) { try { JAXBContext jaxbContext = JAXBContext.newInstance("ua.in.iwanoff.java.third.continent"); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); ContinentData data = (ContinentData)unmarshaller.unmarshal(new FileInputStream("Continent.xml")); // Працюємо зі списком елементів CountryData: for (ContinentData.CountriesData.CountryData c : data.getCountriesData().getCountryData()) { if (c.getName().equals("Франція")) { c.getCapitalData().setName("Париж"); break; } } Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal(data, new FileWriter("CorrectedContinent.xml")); } catch (JAXBException | IOException e) { e.printStackTrace(); } } }
3.3 Класи "Країна" та "Перепис населення"
Припустимо, необхідно доповнити програму роботи з країнами та переписами населення, створену в лабораторній роботі № 1 засобами читання і запису даних в XML-документи. Можна реалізувати два підходи (і відповідно два похідних класи, які представляють основну сутність) - через використання засобів JAXB і через XML-серіалізацію.
До раніше створеного проекту додаємо пакет ua.in.iwanoff.java.third
. Можна скористатися раніше створеним інтерфейсом FileIO
(див. приклад до лабораторної роботи № 1).
Для реалізації версії програми, яка працює з XML-документами через JAXB спочатку необхідно розробити документ і його схему. Можна запропонувати, наприклад, такій XML-документ (Ukraine.xml
). Його слід розташувати у кореневій теці проекту:
<?xml version="1.0" encoding="UTF-8"?> <CountryData Name="Україна" Area="603628" > <CensusData Year="1959" Population="41869000" Comments="Перший післявоєнний перепис" /> <CensusData Year="1970" Population="47126500" Comments="Нас побільшало" /> <CensusData Year="1979" Population="49754600" Comments="Просто перепис" /> <CensusData Year="1989" Population="51706700" Comments="Останній радянський перепис" /> <CensusData Year="2001" Population="48475100" Comments="Перший перепис у незалежній Україні" /> </CountryData>
Для генерації класів через технологію JAXB створюємо новий підпакет xml
всередині пакету ua.in.iwanoff.java.third
. У цьому пакеті розташовуємо файл схеми документу (Country.xsd
):
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="CountryData"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name="CensusData"> <xs:complexType> <xs:attribute name="Year" type="xs:int" use="required" /> <xs:attribute name="Population" type="xs:int" use="required" /> <xs:attribute name="Comments" type="xs:string" use="required" /> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="Name" type="xs:string" use="required" /> <xs:attribute name="Area" type="xs:double" use="required" /> </xs:complexType> </xs:element> </xs:schema>
Примітка: Для проекту слід встановити кодову таблицю UTF-8 через властивості проекту. На жаль, перевизначення кодової таблиці для раніше створеного проекту може призвести до втрати кириличних даних у попередньо написаному програмному коді. В цьому випадку більш коректним є створення нового проекту зі встановленою кодовою таблицею UTF-8 і перенесення необхідного коду через буфер обміну.
Далі генеруємо необхідні класи засобами JAXB. Будуть створені класи ObjectFactory
і CountryData
. Останній клас описує дані про країну відповідно до описаної схеми. Всередині цього класу можна знайти вкладений статичний клас CensusData
. Посилання на цей клас можна використати у новому класі XMLCensus
, який представлятиме окремий перепис для випадку читання даних з XML-файл. Цей клас фактично адаптує CensusData
до вимог ієрархії раніше створених класів. Код класу XMLCensus
буде таким:
package ua.in.iwanoff.java.third; import ua.in.iwanoff.java.third.xml.CountryData; import ua.in.iwanoff.oop.first.AbstractCensus; public class XMLCensus extends AbstractCensus { CountryData.CensusData censusData; public XMLCensus(CountryData.CensusData censusData) { this.censusData = censusData; } @Override public int getYear() { return censusData.getYear(); } @Override public void setYear(int year) { censusData.setYear(year); } @Override public int getPopulation() { return censusData.getPopulation(); } @Override public void setPopulation(int population) { censusData.setPopulation(population); } @Override public String getComments() { return censusData.getComments(); } @Override public void setComments(String comments) { censusData.setComments(comments); } }
Починаємо створення класу XMLCountry
. Найбільш цікавим з автоматично згенерованих класів є клас CountryData
. Доцільно описати поле класу XMLCountry
для роботи з даними XML-файлу -
посилання на кореневий елемент:
private CountryData countryData = new CountryData();
Для зберігання даних буде застосована структура об'єктів автоматично згенерованих класів. Ця структура з'являється у пам'яті після читання даних з XML-документу. Доступ до окремих даних здійснюватиметься через методи автоматично згенерованих класів. Для сортування створюємо тимчасовий масив. Весь вихідний текст файлу XMLCountry.java
матиме такий вигляд:
package ua.in.iwanoff.java.third; import ua.in.iwanoff.java.first.FileIO; import ua.in.iwanoff.java.third.xml.CountryData; import ua.in.iwanoff.oop.first.AbstractCensus; import ua.in.iwanoff.oop.first.AbstractCountry; import ua.in.iwanoff.oop.first.CompareByComments; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.util.Arrays; public class XMLCountry extends AbstractCountry implements FileIO { private CountryData countryData = new CountryData(); @Override public String getName() { return countryData.getName(); } @Override public void setName(String name) { countryData.setName(name); } @Override public double getArea() { return countryData.getArea(); } @Override public void setArea(double area) { countryData.setArea(area); } @Override public AbstractCensus getCensus(int i) { return new XMLCensus(countryData.getCensusData().get(i)); } @Override public void setCensus(int i, AbstractCensus census) { countryData.getCensusData().get(i).setYear(census.getYear()); countryData.getCensusData().get(i).setPopulation(census.getPopulation()); countryData.getCensusData().get(i).setComments(census.getComments()); } @Override public boolean addCensus(AbstractCensus census) { CountryData.CensusData censusData = new CountryData.CensusData(); boolean result = countryData.getCensusData().add(censusData); setCensus(censusesCount() - 1, census); return result; } @Override public boolean addCensus(int year, int population, String comments) { CountryData.CensusData censusData = new CountryData.CensusData(); censusData.setYear(year); censusData.setPopulation(population); censusData.setComments(comments); return countryData.getCensusData().add(censusData); } @Override public int censusesCount() { return countryData.getCensusData().size(); } @Override public void clearCensuses() { countryData.getCensusData().clear(); } @Override public void sortByPopulation() { Collections.sort(countryData.getCensusData(), Comparator.comparing(CountryData.CensusData::getPopulation)); } @Override public void sortByComments() { Collections.sort(countryData.getCensusData(), Comparator.comparing(CountryData.CensusData::getComments)); } @Override public AbstractCensus[] getCensuses() { AbstractCensus[] censuses = new AbstractCensus[censusesCount()]; for (int i = 0; i < censusesCount(); i++) { censuses[i] = new XMLCensus(countryData.getCensusData().get(i)); } return censuses; } @Override public void setCensuses(AbstractCensus[] censuses) { clearCensuses(); for (AbstractCensus census : censuses) { addCensus(census); } } @Override public void readFromFile(String fileName) throws JAXBException, FileNotFoundException { JAXBContext jaxbContext = JAXBContext.newInstance("ua.in.iwanoff.java.third.xml"); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); countryData = (CountryData) unmarshaller.unmarshal(new FileInputStream(fileName)); } @Override public void writeToFile(String fileName) throws JAXBException, IOException { JAXBContext jaxbContext = JAXBContext.newInstance("ua.in.iwanoff.java.third.xml"); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); marshaller.marshal(countryData, new FileWriter(fileName)); } public CountryData.CensusData getCensusData(int i) { return countryData.getCensusData().get(i); } public static void main(String[] args) { XMLCountry country = new XMLCountry(); try { country.readFromFile("Ukraine.xml"); country.testCountry(); country.writeToFile("ByComments.xml"); } catch (FileNotFoundException e) { System.out.println("Read failed"); e.printStackTrace(); } catch (IOException e) { System.out.println("Write failed"); e.printStackTrace(); } catch (JAXBException e) { e.printStackTrace(); System.out.println("Wrong format"); } } }
Як видно з наведеного вище тексту, для сортування переписів слід здійснити сортування списку об'єктів типу CensusData
у структурі об'єктів, яка була створена під час десеріалізації. Для перевірки умови сортування можна скористатись стандартним статичним методом Comparator.comparing()
і посиланням на метод обчислення критерію. Використання посилань на методи розглянуто в лабораторій роботі № 3 курсу "Об'єктно-орієнтоване програмування".
Після виконання програми в кореневій теці проекту автоматично створюється файл ByComments.xml
, в якому дані про переписи розташовані за зростанням кількості населення.
Тепер можна реалізувати варіант з XML-серіалізацією і десеріалізацією. Можна скористатися реалізацією класу "Країна" с масивом всередині (клас CountryWithArray
з прикладу до лабораторної роботи № 1 курсу "Об'єктно-орієнтоване програмування"). Від класу створюємо похідний - XMLSerializedCountry
. Він реалізовуватиме інтерфейс FileIO
. Як і у випадку бінарної серіалізації, об'єкт не може реалізувати сам себе. Тому функція readFromFile()
інтерфейсу не буде реалізована. Десеріалізацію виконуватимо в окремій статичній функції deserialize()
. Код класу XMLSerializedCountry
буде таким:
package ua.in.iwanoff.java.third; import ua.in.iwanoff.java.first.FileIO; import ua.in.iwanoff.oop.first.CountryWithArray; import java.beans.XMLDecoder; import java.beans.XMLEncoder; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class XMLSerializedCountry extends CountryWithArray implements FileIO { @Override public void readFromFile(String fileName) throws Exception { throw new UnsupportedOperationException(); } public static XMLSerializedCountry deserialize(String fileName) throws FileNotFoundException { XMLDecoder xmlDecoder = new XMLDecoder(new FileInputStream(fileName)); XMLSerializedCountry country = (XMLSerializedCountry)xmlDecoder.readObject(); return country; } @Override public void writeToFile(String fileName) throws IOException { XMLEncoder xmlEncoder = new XMLEncoder(new FileOutputStream(fileName)); xmlEncoder.writeObject(this); xmlEncoder.flush(); } }
Окремо створюємо клас XMLCountrySerializer
, у функції main()
якого здійснюємо створення країни і її серіалізацію. Код буде таким:
package ua.in.iwanoff.java.third; import java.io.IOException; public class XMLCountrySerializer { public static void main(String[] args) { XMLSerializedCountry country = new XMLSerializedCountry(); country.createCountry(); try { country.writeToFile("UkraineSerialized.xml"); } catch (IOException e) { System.err.println("Write failed"); e.printStackTrace(); } } }
Після виконання програми в кореневій теці проекту створюється файл UkraineSerialized.xml
.
Для десеріалізації і тестування створюємо окремий клас XMLCountryDeserializer
:
package ua.in.iwanoff.java.third; import java.io.FileNotFoundException; import java.io.IOException; public class XMLCountryDeserializer { public static void main(String[] args) { XMLSerializedCountry country = null; try { country = XMLSerializedCountry.deserialize("UkraineSerialized.xml"); country.testCountry(); country.writeToFile("ByCommentsSerialized.xml"); } catch (FileNotFoundException e) { System.err.println("Read failed"); e.printStackTrace(); } catch (IOException e) { System.err.println("Write failed"); e.printStackTrace(); } } }
Після виконання цієї програми в кореневій теці проекту можна буде знайти файл ByCommentsSerialized.xml
.
4 Вправи для контролю
- Створити схему документу та XML-документ, який описує дані про користувача. Згенерувати класи за допомогою технології JAXB.
- Створити схему документу та XML-документ, який описує дані про книгу. Згенерувати класи за допомогою технології JAXB.
- Створити схему документу та XML-документ, який описує дані про місто. Згенерувати класи за допомогою технології JAXB.
- Створити схему документу та XML-документ, який описує дані про кінофільм. Згенерувати класи за допомогою технології JAXB.
- Описати класи Факультет та Інститут (з полем - масивом факультетів). Створити об'єкти, здійснити їх серіалізацію й десеріалізацію в XML.
5 Контрольні запитання
- Для яких цілей використовуються XML-документи?
- Які обмеження накладаються на структуру XML-документу, синтаксис і розташування тегів?
- Чим відрізняються технології SAX і DOM?
- Яким чином здійснюється читання і запис XML-документів?
- Що таке XSLT?
- Чим відрізняється валідний (valid) та правильно оформлений (well-formed) XML-документ?
- Чим відрізняються шаблони документу і схеми документу?
- Чи є шаблон документу XML-документом?
- Чи є схема документу XML-документом?
- Для чого в XML-документах необхідні простори імен?
- Що таке маршалізація і демаршалізація?
- У чому переваги технології зв'язування даних?
- Які є стандартні й нестандартні технології зв'язування даних?
- Які налаштування проекту слід встановити для використання засобів Dali Java Persistence Tools?
- Які класи відповідають специфікації Java Beans?
- Які є недоліки й переваги XML-серіалізації?