IBM®
Перейти к тексту
    в России и странах СНГ [изменить]    Условия использования
 
 
   
    Главная страница    Продукты    Услуги и решения    Поддержка и загрузка    Мой профиль    
Перейти к тексту

developerWorks Россия  >  XML | Технология Java  >

Связывание с данными с помощью Castor: Часть 2. Маршаллинг и демаршаллинг в XML

Использование Castor для связывания с XML-данными

developerWorks
Опции документа

Опции документа, требующие включения JavaScript, не отображаются

Обсудить

Исходные тексты примера


Выскажите мнение об этой странице

Помогите нам улучшить содержание


Уровень сложности: сложный

Бретт МакЛафлин, автор и редактор, O'Reilly Media, Inc.

24.10.2008

В предыдущей статье рассказывалось о загрузке, установке и конфигурировании Castor. В этой статье мы поговорим о трансформировании Java™-классов в XML и обратно, о принципах работы Castor, а также о том, как правильно проектировать классы, чтобы их можно было легко использовать вместе с Castor API.

Предварительные требования

Прежде чем перейти к делу, убедитесь, что выполнены все необходимые предварительные условия. Проще всего этого добиться, обратившись к предыдущей статье серии (ссылку, как и всегда, можно найти в разделе Ресурсы), в которой приводится простая последовательность действий по загрузке, установке и проверке функционирования Castor на элементарных примерах.

Кроме того, если в вашем текущем проекте есть классы, которые вы хотели бы научиться преобразовывать в XML и обратно, то можете использовать их при изучении Castor. Конечно, есть тестовые примеры к этой и предыдущей статьям, но они не заменят опыта, полученного от применения Castor к вашим собственным проектам. Начните с простых классов, описывающих людей, CD, книги или любые другие объекты реального мира. Затем, как только вы перейдете к секции, описывающей отображение (mapping), вы сможете начать работать с более сложными объектами.



В начало


Маршаллинг 101

Основной операцией в Castor является маршаллинг - преобразование экземпляров Java-классов в фрагменты XML. Имя самого класса при этом выступает в качестве контейнера - элемента XML верхнего уровня. Например, при преобразовании объектов класса Book скорее всего получится XML-документ с корневым элементом "book".

Документ XML будет также включать все свойства класса. Например, свойство title, содержащее значение “Power Play”, будет выглядеть в XML следующим образом:

<title>Power Play</title>

Таким образом, нетрудно понять, как будет выглядеть документ XML, представляющий собой простой Java-класс.



В начало


Преобразуются только экземпляры классов

Другие статьи серии

Необходимо сделать несколько замечаний перед тем как переходить к написанию кода для маршаллинга. Во-первых, в XML преобразовываются только экземпляры (объекты) классов, а не сами классы. Класс представляет собой некую структуру, что соответствует описанию модели в терминах XML, например, в DTD и XML-схеме. Сам по себе класс не содержит никаких данных, он всего лишь описывает структуру, определяющую хранение и доступ к данным.

Класс можно инстанциировать, т.е. создать объект-экземпляр класса. Инстанциирование может быть непосредственным, а также производиться с помощью фабрики либо другого механизма. Далее в поля созданного объекта можно помещать данные. Каждый экземпляр является самостоятельным объектом: несмотря на то, что его структура идентична структуре других экземпляров того же класса, его данные всегда хранятся отдельно. Рисунок 1 иллюстрирует разницу между классами и их экземплярами.


Рисунок 1. Классы описывают структуру, а экземпляры содержат данные
Your class gives your data form, and instances populate that data

Таким образом, при маршаллинге преобразовываются экземпляры. В дальнейшем мы покажем, как можно управлять структурой XML-документов, используя модель ограничений и файлы отображения. Пока же старайтесь уловить соответствие между структурой XML (элементами и атрибутами) и структурой Java-классов (их свойствами).

Пример простого маршаллинга

В листинге 1 приведен пример простого класса Book, который будет использоваться далее в этой статье.


Листинг 1. Класс Book
        
package ibm.xml.castor;

public class Book {

  /** ISBN-код книги */
  private String isbn;
  /** название книги */
  private String title;
  /** Имя автора */
  private String authorName;

  public Book(String isbn, String title, String authorName) {
    this.isbn = isbn;
    this.title = title;
    this.authorName = authorName;
  }

  public String getIsbn() {
    return isbn;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getTitle() {
    return title;
  }

  public void setAuthorName(String authorName) {
    this.authorName = authorName;
  }

  public String getAuthorName() {
    return authorName;
  }
}

Скомпилировав этот код, вы получите файл Book.class. Сам класс очень прост - он содержит только 3 свойства: ISBN, название и имя автора (существуют определенные трудности, но на них мы остановимся позднее). Для того чтобы трансформировать экземпляры класса Book в XML с помощью Castor, достаточно практически только этого файла и нескольких строк кода. Пример создания нового экземпляра Book и записи его в XML приведен в листинге 2.


Листинг 2. Класс, выполняющий маршаллинг экземпляра Book
        
package ibm.xml.castor;

import java.io.FileWriter;

import org.exolab.castor.xml.Marshaller;

public class BookMarshaller {

  public static void main(String[] args) {
    try {
      Book book = new Book("9780312347482", "Power Play", "Joseph Finder");
      FileWriter writer = new FileWriter("book.xml");
      Marshaller.marshal(book, writer);
    } catch (Exception e) {
      System.err.println(e.getMessage());
      e.printStackTrace(System.err);
    }
  }
}

Скомпилируйте и запустите эту программу. Она должна создать файл book.xml. Открыв его, вы увидите документ XML, подобный показанному в листинге 3.


Листинг 3. Сгенерированный XML
        
<?xml version="1.0" encoding="UTF-8"?>
<book><author-name>Joseph Finder</author-name>
<isbn>9780312347482</isbn><title>Power Play</title></book>

Переводы строки были добавлены исключительно для улучшения читаемости. Сам сгенерированный документ не содержит символов перевода строки между закрывающим тегом author-name и открывающим тегом isbn.

Информация, не преобразуемая в XML

Перед тем как усложнить наш пример (а ему, несомненно, требуются дополнения), обратите внимание, что не вся информация Java-класса присутствует в документе XML. Отсутствует следующее:

  • Название пакета Java-класса. Название пакета не является частью структуры класса. Это вопрос семантики, связанный с пространствами имен в Java. Таким образом, при демаршаллинге (обратном преобразовании из XML в Java) можно трансформировать этот документ в экземпляр любого класса Book, который содержит те же три свойства вне зависимости от того, в каком пакете он находится.
  • Порядок следования полей. Порядок перечисления записей важен в XML, но не в Java. Поэтому, даже если поля следовали в исходном файле в определенном порядке, в XML порядок будет иным. С точки зрения XML-документа это важно, а для класса Book не имеет никакого значения.
  • Методы. Как и название пакета, методы не являются частью структуры данных. Поэтому они игнорируются при генерировании документа XML.

Здесь, по-видимому, уместен вопрос "ну и что?", не так ли? Какая разница, упущены ли эти детали при маршаллинге, если они все равно не важны? Но дело в том, что они важны, так как благодаря им достигается большая гибкость, чем можно было ожидать. Полученный документ XML можно трансформировать в экземпляр любого Java-класса, удовлетворяющего минимальным требованиям:

  1. Именем класса является “Book” (далее мы покажем, как можно обойти это ограничение, используя файлы отображения).
  2. Класс содержит поля authorName, title и isbn.

И все! Вы можете создать несколько классов, подходящих под эти критерии, которые могут иметь различные дополнительные поля, находиться в разных пакетах или предоставлять разные методы... Вскоре вы убедитесь, насколько гибко можно использовать Castor благодаря игнорированию этих деталей.



В начало


Добавление более сложных типов

Наиболее очевидным недостатком класса Book является невозможность хранения нескольких авторов, но это несложно исправить (листинг 4).


Листинг 4. Класс Book, способный хранить несколько имен авторов
        
package ibm.xml.castor;

import java.util.LinkedList;
import java.util.List;

public class Book {

  /** ISBN-код книги */
  private String isbn;
  /** название книги */
  private String title;
  /** имена авторов */
  private List authorNames;

  public Book(String isbn, String title, List authorNames) {
    this.isbn = isbn;
    this.title = title;
    this.authorNames = authorNames;
  }

  public Book(String isbn, String title, String authorName) {
    this.isbn = isbn;
    this.title = title;
    this.authorNames = new LinkedList();
    authorNames.add(authorName);
  }

  public String getIsbn() {
    return isbn;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getTitle() {
    return title;
  }

  public void setAuthorNames(List authorNames) {
    this.authorNames = authorNames;
  }

  public List getAuthorNames() {
    return authorNames;
  }

  public void addAuthorName(String authorName) {
    authorNames.add(authorName);
  }
}

Если вы используете Java 5 или 6, то компилятор выдаст предупреждения о выполнении небезопасных операций, потому что используются нетипизированные списки (Lists). Если хотите, можете заменить их на типизированные.

Это небольшое исправление не требует никаких изменений в коде маршаллера. При этом имеет смысл создать экземпляр книги нескольких авторов, чтобы посмотреть, как Castor будет преобразовывать в XML объект, содержащий коллекцию. Измените класс BookMarshaller следующим образом (листинг 5).


Листинг 5. Маршаллинг экземпляра Book, содержащего коллекцию
        
package ibm.xml.castor;

import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;

import org.exolab.castor.xml.Marshaller;

public class BookMarshaller {

  public static void main(String[] args) {
    try {
      Book book = new Book("9780312347482", "Power Play", "Joseph Finder");
      FileWriter writer = new FileWriter("book.xml");
      Marshaller.marshal(book, writer);

      List book2Authors = new ArrayList();
      book2Authors.add("Douglas Preston");
      book2Authors.add("Lincoln Child");
      Book book2 = new Book("9780446618502", "The Book of the Dead",
                            book2Authors);
      writer = new FileWriter("book2.xml");
      Marshaller.marshal(book2, writer);

    } catch (Exception e) {
      System.err.println(e.getMessage());
      e.printStackTrace(System.err);
    }
  }
}

Первая книга записывается в XML точно так же, как и ранее; в этом можно убедиться, повторно открыв файл book.xml. После этого откройте файл book2.xml, и вы увидите, как Castor выполнил маршаллинг коллекции (листинг 6).


Листинг 6. XML-документ, содержащий элементы коллекции
        
<?xml version="1.0" encoding="UTF-8"?>
<book><isbn>9780446618502</isbn><title>The Book of the Dead</title>
<author-names xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:type="java:java.lang.String">Douglas Preston</author-names>
<author-names xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:type="java:java.lang.String">Lincoln Child</author-names>
</book>

Как видите, Castor легко преобразовал список имен авторов. При этом важно, что он не только создал элемент-контейнер для имен, но и заглянул внутрь списка, определив, что элементами списка являются строки (как вы помните, список был нетипизирован, так что Castor взял на себя задачу определения типа каждого элемента). Таким образом, XML содержит достаточно подробную информацию о типах. Это полезно, особенно если XML проходит дополнительную обработку перед тем как преобразуется обратно в Java-объекты.



В начало


Добавление собственных классов

Теперь сделаем наш пример еще немного реалистичнее. Хранение имен авторов в виде строк практически наверняка приведет к дублированию данных, так как большинство авторов написали более одной книги. Поэтому имеет смысл добавить новый класс Author, показанный в листинге 7.


Листинг 7. Класс Author
        
package ibm.xml.castor;

public class Author {

  private String firstName, lastName;
  private int totalSales;

  public Author(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setTotalSales(int totalSales) {
    this.totalSales = totalSales;
  }

  public void addToSales(int additionalSales) {
    this.totalSales += additionalSales;
  }

  public int getTotalSales() {
    return totalSales;
  }
}

Просто, не правда ли? В листинге 8 приведена модифицированная версия класса Book, использующая класс Author для хранения имен авторов.


Листинг 8. Версия класса Book, использующая нестандартный класс для хранения авторов
        
package ibm.xml.castor;

import java.util.LinkedList;
import java.util.List;

public class Book {

  /** ISBN-код книги */
  private String isbn;
  /** название книги */
  private String title;
  /** имена авторов */
  private List authors;

  public Book(String isbn, String title, List authors) {
    this.isbn = isbn;
    this.title = title;
    this.authors = authors;
  }

  public Book(String isbn, String title, Author author) {
    this.isbn = isbn;
    this.title = title;
    this.authors = new LinkedList();
    authors.add(author);
  }

  public String getIsbn() {
    return isbn;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getTitle() {
    return title;
  }

  public void setAuthors(List authors) {
    this.authors = authors;
  }

  public List getAuthors() {
    return authors;
  }

  public void addAuthor(Author author) {
    authors.add(author);
  }
}

Для того чтобы использовать этот класс в нашем примере, необходимо несколько изменений в классе BookMarshaller (листинг 9).


Листинг 9. Класса BookMarshaller после добавления информации об авторах
        
package ibm.xml.castor;

import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;

import org.exolab.castor.xml.Marshaller;

public class BookMarshaller {

  public static void main(String[] args) {
    try {
      Author finder = new Author("Joseph", "Finder");
      Book book = new Book("9780312347482", "Power Play", finder);
      FileWriter writer = new FileWriter("book.xml");
      Marshaller.marshal(book, writer);

      List book2Authors = new ArrayList();
      book2Authors.add(new Author("Douglas", "Preston"));
      book2Authors.add(new Author("Lincoln", "Child"));
      Book book2 = new Book("9780446618502", "The Book of the Dead",
                            book2Authors);
      writer = new FileWriter("book2.xml");
      Marshaller.marshal(book2, writer);

    } catch (Exception e) {
      System.err.println(e.getMessage());
      e.printStackTrace(System.err);
    }
  }
}

Вот и все! Скомпилируйте пример и запустите маршаллер. Проверьте содержимое обоих файлов (ниже приведен только book2.xml, так как он представляет больший интерес).


Листинг 10. XML, содержащий книги и имена авторов
        
<?xml version="1.0" encoding="UTF-8"?>
<book><authors xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
total-sales="0" xsi:type="java:ibm.xml.castor.Author"><last-name>Preston</last-name>
<first-name>Douglas</first-name></authors><authors 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" total-sales="0"
 xsi:type="java:ibm.xml.castor.Author"><last-name>Child</last-name>
<first-name>Lincoln</first-name></authors><isbn>9780446618502</isbn>
<title>The Book of the Dead</title></book>

Как видите, опять не возникло никаких проблем. С помощью механизма интроспекции (introspection) списка authors Castor самостоятельно определил, что необходимо преобразовывать в XML не только класс Book, но и Author. Более того, свойство totalSales класса Author Castor также включил в маршаллинг, поэтому вы можете увидеть его значения в XML.

Castor и generics

Возможно, статья начинает понемногу превращаться в рекламу, но Castor корректно работает с параметризованными типами, в частности, списками. Поэтому, если вы привыкли к Java 5 или Java 6, то можете скорректировать класс Book следующим образом (листинг 11).


Листинг 11. Версия класса Book, использующая параметризованный список
        
package ibm.xml.castor;

import java.util.LinkedList;
import java.util.List;

public class Book {

  /** ISBN-код книги */
  private String isbn;
  /** название книги */
  private String title;
  /** имена авторов */
  private List<Author> authors;

  public Book(String isbn, String title, List<Author> authors) {
    this.isbn = isbn;
    this.title = title;
    this.authors = authors;
  }

  public Book(String isbn, String title, Author author) {
    this.isbn = isbn;
    this.title = title;
    this.authors = new LinkedList<Author>();
    authors.add(author);
  }

  public String getIsbn() {
    return isbn;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getTitle() {
    return title;
  }

  public void setAuthors(List<Author> authors) {
    this.authors = authors;
  }

  public List<Author> getAuthors() {
    return authors;
  }

  public void addAuthor(Author author) {
    authors.add(author);
  }
}

Теперь список авторов допускает включение экземпляров только класса Author, что является достаточно серьезным преимуществом. Вы можете сделать соответствующие изменения в коде BookMarshaller, перекомпилировать пример, и в результате получите точно такой же XML-документ. Другими словами, Castor можно использовать для работы с вполне реалистичными, хорошо спроектированными классами.

Ваши классы меняются. Castor - нет

Необходимо сделать одно существенное замечание перед тем как перейти к вопросам демаршаллинга. Несмотря на все улучшения и исправления классов, сделанные в предыдущих разделах, нам нигде и никогда не приходилось менять код маршаллинга! Мы добавляли параметризацию типов, собственные классы, коллекции... и при всем этом для маршаллинга было достаточно одного простого вызова через Castor API. Впечатляет, не правда ли?



В начало


Демаршаллинг

После того как мы уделили столько времени различным аспектам маршаллинга, демаршаллинг покажется вам несложным. Все, что требуется – это взять документ XML и убедиться, что поля и имя Java-класса соответствуют данным документа. Остальное возьмет на себя Castor. Давайте произведем демаршаллинг двух XML-документов, сгенерированных выше. Пример приведен в листинге 12.


Листинг 12. Демаршаллинг экземпляров класса Book
        
package ibm.xml.castor;

import java.io.FileReader;
import java.util.Iterator;
import java.util.List;

import org.exolab.castor.xml.Unmarshaller;

public class BookUnmarshaller {

  public static void main(String[] args) {
    try {
      FileReader reader = new FileReader("book.xml");
      Book book = (Book)Unmarshaller.unmarshal(Book.class, reader);
      System.out.println("Book ISBN: " + book.getIsbn());
      System.out.println("Book Title: " + book.getTitle());
      List authors = book.getAuthors();
      for (Iterator i = authors.iterator(); i.hasNext(); ) {
        Author author = (Author)i.next();
        System.out.println("Author: " + author.getFirstName() + " " +
                                        author.getLastName());
      }

      System.out.println();

      reader = new FileReader("book2.xml");
      book = (Book)Unmarshaller.unmarshal(Book.class, reader);
      System.out.println("Book ISBN: " + book.getIsbn());
      System.out.println("Book Title: " + book.getTitle());
      authors = book.getAuthors();
      for (Iterator i = authors.iterator(); i.hasNext(); ) {
        Author author = (Author)i.next();
        System.out.println("Author: " + author.getFirstName() + " " +
                                        author.getLastName());
      }
    } catch (Exception e) {
      System.err.println(e.getMessage());
      e.printStackTrace(System.err);
    }
  }
}

Скомпилируйте и запустите этот пример. Скорее всего, вы получите совсем не то, что ожидали. Ниже показано мое сообщение об ошибке вместе со стеком вызовов (листинг 13).


Листинг 13. Исключение при демаршаллинге и содержимое стека
        
[bmclaugh:~/Documents/developerworks/castor-2]  
java ibm.xml.castor.BookUnmarshaller
ibm.xml.castor.Book
org.exolab.castor.xml.MarshalException: ibm.xml.castor.Book{File:
           [not available]; line: 2; column: 7}
         at org.exolab.castor.xml.Unmarshaller.
           convertSAXExceptionToMarshalException(Unmarshaller.java:755)
         at org.exolab.castor.xml.Unmarshaller.unmarshal 
(Unmarshaller.java:721)
         at org.exolab.castor.xml.Unmarshaller.unmarshal 
(Unmarshaller.java:610)
         at org.exolab.castor.xml.Unmarshaller.unmarshal 
(Unmarshaller.java:812)
         at ibm.xml.castor.BookUnmarshaller.main 
(BookUnmarshaller.java:14)
Caused by: java.lang.InstantiationException: ibm.xml.castor.Book
         at java.lang.Class.newInstance0(Class.java:335)
         at java.lang.Class.newInstance(Class.java:303)
         at org.exolab.castor.util.DefaultObjectFactory.createInstance(
           DefaultObjectFactory.java:107)
         at org.exolab.castor.xml.UnmarshalHandler.createInstance(
           UnmarshalHandler.java:2489)
         at org.exolab.castor.xml.UnmarshalHandler.startElement(
           UnmarshalHandler.java:1622)
         at org.exolab.castor.xml.UnmarshalHandler.startElement(
           UnmarshalHandler.java:1353)
         at org.apache.xerces.parsers.AbstractSAXParser.startElement 
(Unknown Source)
         at org.apache.xerces.impl.dtd.XMLDTDValidator.startElement 
(Unknown Source)
         at  
org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanStartElement(
           Unknown Source)
         at org.apache.xerces.impl.XMLDocumentScannerImpl 
$ContentDispatcher.
           scanRootElementHook(Unknown Source)
         at org.apache.xerces.impl.
           XMLDocumentFragmentScannerImpl 
$FragmentContentDispatcher.dispatch(
             Unknown Source)
         at  
org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(
           Unknown Source)
         at org.apache.xerces.parsers.XML11Configuration.parse 
(Unknown Source)
         at org.apache.xerces.parsers.XML11Configuration.parse 
(Unknown Source)
         at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
         at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown  
Source)
         at org.exolab.castor.xml.Unmarshaller.unmarshal 
(Unmarshaller.java:709)
         ... 3 more
Caused by: java.lang.InstantiationException: ibm.xml.castor.Book
         at java.lang.Class.newInstance0(Class.java:335)
         at java.lang.Class.newInstance(Class.java:303)
         at org.exolab.castor.util.DefaultObjectFactory.createInstance(
           DefaultObjectFactory.java:107)
         at org.exolab.castor.xml.UnmarshalHandler.createInstance(
           UnmarshalHandler.java:2489)
         at org.exolab.castor.xml.UnmarshalHandler.startElement 
(UnmarshalHandler.java:1622)
         at org.exolab.castor.xml.UnmarshalHandler.startElement 
(UnmarshalHandler.java:1353)
         at org.apache.xerces.parsers.AbstractSAXParser.startElement 
(Unknown Source)
         at org.apache.xerces.impl.dtd.XMLDTDValidator.startElement 
(Unknown Source)
         at  
org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanStartElement(
           Unknown Source)
         at org.apache.xerces.impl.XMLDocumentScannerImpl 
$ContentDispatcher.
           scanRootElementHook(Unknown Source)
         at org.apache.xerces.impl.
           XMLDocumentFragmentScannerImpl 
$FragmentContentDispatcher.dispatch(
             Unknown Source)
         at  
org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(
           Unknown Source)
         at org.apache.xerces.parsers.XML11Configuration.parse 
(Unknown Source)
         at org.apache.xerces.parsers.XML11Configuration.parse 
(Unknown Source)
         at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
         at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown  
Source)
         at org.exolab.castor.xml.Unmarshaller.unmarshal 
(Unmarshaller.java:709)
         at org.exolab.castor.xml.Unmarshaller.unmarshal 
(Unmarshaller.java:610)
         at org.exolab.castor.xml.Unmarshaller.unmarshal 
(Unmarshaller.java:812)
         at ibm.xml.castor.BookUnmarshaller.main 
(BookUnmarshaller.java:14)

Итак, что же произошло? Мы столкнулись с первым из условий, которые необходимо выполнить при использовании Castor для связывания с данными.

Необходимы конструкторы без параметров

Демаршаллинг в Castor реализован на основе механизма рефлексии (reflection), при котором широко используются методы типа Class.forName(здесь должно быть имя вашего класса).newInstance(). Благодаря этому Castor не требует полной информации о классах для их инстанциирования. Однако при этом необходимо, чтобы классы содержали конструкторы без параметров.

К счастью, для исправления ошибки достаточно просто добавить конструкторы без параметров в классы Book и Author (public Book() { } и public Author() { }). Перекомпилируйте и снова запустите пример.

Обратите внимание на возможные последствия включения конструкторов без параметров. Они позволяют любому классу или программе, а не только Castor, создавать экземпляры книг и авторов, не содержащих никаких данных (ISBN, названий, имен или фамилий авторов). Очевидно, что это представляет собой проблему. Единственный способ ее избежать – это начать использовать более сложные возможности Castor, которые будут рассматриваться в следующей статье. В любом случае необходимо очень тщательно продумывать возможные варианты использования ваших классов. В частности, свойствам, обращение к которым может привести к NullPointerException, можно присваивать определенные значения. Например, строковые свойства должны содержать пустые строки или значения по умолчанию, ссылки на объекты должны быть проинициализированы и т.д.

Попытка номер два: проблемы, связанные с null

Добавив конструкторы без параметров в классы Book и Author, перезапустите демаршаллер. Результат будет примерно такой (листинг 14).


Листинг 14. Результаты демаршаллинга после добавления конструкторов без параметров
        
[bmclaugh:~/Documents/developerworks/castor-2] java ibm.xml.castor.BookUnmarshaller
Book ISBN: null
Book Title: Power Play
Author: null null

Book ISBN: null
Book Title: The Book of the Dead
Author: null null
Author: null null

Похоже, что еще не все проблемы решены (демаршаллинг обещал быть простым, помните? Подождите немного, скоро я к этому вернусь). В частности, несколько полей объектов содержат null. При этом если вы обратитесь к XML, то увидите, что все значения присутствуют в соответствующих элементах. Так в чем же дело?

Чтобы понять, что произошло, обратите внимание на то, что названия книг были правильно присвоены свойствам объектов. Посмотрите внимательнее на вторую книгу под названием The Book of the Dead. Может показаться, что авторы не были восстановлены из XML. Однако это не так: экземпляр книги содержит две ссылки на объекты класса Author, но в этих объектах не заданы свойства firstName и lastName. Другими словами, корректно восстанавливаются свойства title и authorList, а isbn, имя и фамилия авторов содержат null. При этом если вы выведете значения свойства totalSales каждого автора, то увидите, что они также содержат правильные значения.

Догадались в чем дело? Поля, содержащие null, не имеют set-методов (так называемых мутаторов). Не определены методы setIsbn(), setFirstName() и т.д. Они раньше не требовались, потому что вся необходимая информация задавалась в конструкторе. При этом значения таких свойств как ISBN не должны меняться после создания объекта, так как по сути это означает появление новой книги. Поэтому лучше вместо изменения свойства просто создавать новый объект с новым ISBN.

Как вы помните, Castor использует reflection. Он не может задать значение свойства без set-метода (setFieldName(), где FieldName – имя поля) по той же причине, по которой он не может создать объект при отсутствии конструктора без параметров. Ниже приведены методы, которые необходимо добавить в классы Book и Author (их реализация тривиальна, так что оставлю ее вам):

  • setIsbn(String isbn) (в классе Book)
  • setFirstName(String firstName) (в классе Author)
  • setLastName(String lastName) (в классе Author)

Все что нужно – это добавить методы и перекомпилировать пример.

Попытка номер три: успешный демаршаллинг

Снова запустите демаршаллер. На этот раз вы должны увидеть следующее (листинг 15).


Листинге 15. Результаты демаршаллинга после добавления set-методов
        
[bmclaugh:~/Documents/developerworks/castor-2] java ibm.xml.castor.BookUnmarshaller
Book ISBN: 9780312347482
Book Title: Power Play
Author: Joseph Finder

Book ISBN: 9780446618502
Book Title: The Book of the Dead
Author: Douglas Preston
Author: Lincoln Child

Это именно то, что и требовалось. Правда, пришлось несколько изменить наши классы. Но, как вы помните, я утверждал, что демаршаллинг не представляет никаких сложностей. Справедливо ли это после того, что нам пришлось сделать, включая все изменения, необходимые, чтобы заставить его работать?

На самом деле справедливо. Все изменения были сделаны в ваших классах, а не в коде демаршаллинга. Castor очень прост в использовании, главное, чтобы классы были спроектированы так, как он этого требует, а именно: каждый класс содержит конструктор без параметров, а также get/set-методы для каждого поля.



В начало


Игра стоит свеч

В итоге пришлось сделать не так уж много изменений. Используются все те же классы Book и Author, а их функциональность изменилась несущественно (на самом деле она вообще не изменилась). Однако дизайн классов не лишен недостатков. Очевидно, что у каждой книги должен быть ISBN, поэтому возможность создания экземпляра книги без ISBN (через конструктор без параметров) вызывает у меня определенное беспокойство. Кроме того, мне не нравится, что кто-то может взять и изменить значение ISBN книги. Это означает, что класс не является корректным представлением объекта реального мира.

Изменения в классе Author выглядят не столь сомнительно, наверное, потому, что имена не обязательно должны идентифицировать объекты. Фамилии могут меняться, как, впрочем, и имена. Но даже если добавить в класс поле-идентификатор, например, номер социального страхования, номер водительских прав или что-то еще, то все равно понадобится конструктор без параметров и соответствующие set-методы. Так что проблема остается.

Итак, встают два вопроса:

  1. Достаточно ли полезен Castor, а именно его механизм для простого связывания с данными, чтобы согласиться на выполнение перечисленных выше условий?
  2. Можно ли защититься от некорректного использования методов, которые приходится добавлять в классы, трансформируемые в XML и обратно с помощью Castor?

Только вы сами можете дать ответы на эти вопросы с учетом процесса разработки в вашей компании. Как правило преимущества Castor перевешивают, и разработчики выполняют необходимые условия. Если же вы настолько ограничены дизайном ваших классов или требованиями конкретного приложения, что не можете их выполнить, то скорее всего можно реализовать собственный вариант сериализации в XML. В любом случае полезно уметь использовать Castor. Даже если он не подходит под требования текущего проекта, не исключено, что его можно будет успешно применить в будущем.



В начало


Что дальше?

Одним из важных вопросов, которые мы обошли вниманием, является использование файлов отображения. Пока нам приходилось иметь дело с непосредственным преобразованием из Java в XML. При этом предполагается, что:

  1. Каждое свойство Java-класс должно сохраняться в XML.
  2. Названия элементов в XML должны в точности соответствовать именам классов и их полей.
  3. При демаршаллинге существует модель классов, которая в точности соответствует данным в XML-документах.

Ни одно из этих предположений полностью не удовлетворяет реалиям промышленного программирования. Например, что делать, если имена полей и классов раскрывают слишком много подробностей о структуре приложения и, как следствие, их нельзя помещать в XML по соображениям безопасности? Или как поступить, если XML-документ был получен извне, и его необходимо трансформировать в объекты Java-классов, чьи имена и поля не соответствуют его элементам? Наконец, что делать, если только часть атрибутов класса должна присутствовать в XML?

Ответ на все эти вопросы один: Castor предоставляет более широкие возможности по управлению отображением Java-классов в XML и обратно. Это станет предметом детального рассмотрения в следующей статье серии. Пока же, в дополнение к изучению маршаллинга и демаршаллинга, старайтесь глубже оценить последствия изменений в дизайне ваших классов, которые пришлось сделать в угоду Castor. И не забывайте проверять, не вышла ли следующая статья. До встречи в сети!




В начало


Загрузка

ОписаниеИмяРазмерМетод загрузки
Исходный код примеров на Java для данной статьиx-xjavacastor2/castor2_source_code.zip2KБHTTP
Скомпилированные примеры для данной статьиx-xjavacastor2/castor2_compiled_code.zip4KБHTTP
Информация о методах загрузки


Ресурсы

Научиться

Получить продукты и технологии
  • Книга "Java и XML, 3-е издание" (Бретт Маклафлин и Джастин Эдельсон (Justin Edelson), O'Reilly Media, Inc.) описывает все аспекты XML, с начала и до конца, включая вопросы связывания с данными и отображения. (EN)

  • В более ранней книге "Java и связывание с XML-данными" (Бретт Маклафлин, O'Reilly Media, Inc.) приводится информация о Castor и общих принципах связывания с данными. (EN)

  • Если вы хотите получить платную поддержку Castor, то ознакомьтесь со списком коммерческих услуг. (EN)

  • Скачайте ознакомительные версии программного обеспечения IBM: Используйте в вашем следующем проекте ознакомительные версии ПО, которые можно скачать прямо с сайта IBM developerWorks. (EN)


Обсудить


Об авторе

Бретт МакЛафлин (Brett McLaughlin) работает с компьютерами со времен Logo (помните маленький треугольник?). За последние несколько лет он стал одним из наиболее известных авторов и программистов сообщества по технологиям Java и XML. Он работал в Nextel Communications над реализацией сложных корпоративных систем, в Lutris Technologies - фактически над созданием серверов приложений, - а с недавнего времени - в O'Reilly Media, Inc., где продолжает писать и редактировать важные и содержательные книги. Его готовящаяся книга "Head Rush Ajax" продолжает отмеченный наградами инновационный подход "Вперед головой" применительно к изучению Ajax. Его недавняя книга Java 1.5 Tiger: Заметки разработчика является первой доступной книгой по новейшей версии технологии Java, а его классическая книга Java и XML остается одной из наиболее авторитетных работ по применению технологий XML в языке Java.




Выскажите мнение об этой странице


Пожалуйста, найдите минутку и заполните форму, чтобы повысить уровень сервиса.



 


 


 


Поделиться этой статьей:

забобрить забобрить memori сохранить в memori




В начало


IBM обладает всеми авторскими правами касательно информации, расположенной на developerWorks. Использование информации приведенной на этом ресурсе без явного письменного разрешения от IBM или первоначального автора запрещены. Если Вы желаете использовать информацию с developerWorks, пожалуйста воспользуйтесь регистрационной формой для того, чтобы связаться с нами запрос на использование материалов developerWorks Россия.
    IBM в России Конфиденциальность Контакты