ITPub博客

首页 > > > 实现 Castor 数据绑定--转-4

实现 Castor 数据绑定--转-4

作者:longmanma 时间:2020-01-06 09:37:28 0 删除 编辑

清单 8. 使用自定义作者类的 Book 类

package ibm.xml.castor;import java.util.LinkedList;import java.util.List;public class Book { /** The book's ISBN */ private String isbn; /** The book's title */ private String title; /** The authors' names */private List authors;public Book(String isbn, String title, Listauthors) { 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 voidsetAuthors(List authors){this.authors = authors;} public ListgetAuthors(){ returnauthors;} public void addAuthor(Author author){authors.add(author);}}

测试上述代码需要对BookMarshaller略加修改:

清单 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>

Castor 同样能办到。它说明了如何同时编组Book和Author类(再看看authors列表)。Castor 甚至为Author增加了totalSales属性,可以试试看结果如何。

Castor 对泛型的处理

也许听起来像是全天候的推销频道,但 Castor 确实支持泛型和参数化列表。因此如果习惯 Java 5 或 Java 6,可以这样修改Book:

清单 11. 处理泛型和参数化列表的 Book 类

package ibm.xml.castor;import java.util.LinkedList;import java.util.List;public class Book { /** The book's ISBN */ private String isbn; /** The book's title */ private String title; /** The authors' names */ privateList<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 = newLinkedList<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; } publicList<Author>getAuthors() { return authors; } public void addAuthor(Author author) { authors.add(author); }}

这样作者列表就只接受Author实例了,这是一个非常重要的改进。可以对BookMarshaller作类似的修改,重新编译,并得到相同的 XML 输出。换句话说,可以为 Castor 建立非常具体的类。

类变了但 Castor 没有变

在最终讨论解组之前,还需要做一项重要的观察。前面对类作的所有修改都没有改变编组代码!我们增加了泛型、自定义类、集合等等,但是编组 XML,Castor API 只需要一次简单的调用。太令人吃惊了!

 

回页首

解组

花了很多时间详细讨论完编组以后,解组非常简单。有了 XML 文档,并保证具有和数据匹配的 Java 类之后,剩下的工作交给 Castor 就行了。我们来解组前面生成的两个 XML 文档。如清单 12 所示。

清单 12. 解组图书

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. 解组遇到的错误和堆栈记录

[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 要求使用无参数的构造器

Castor 主要通过反射和调用Class.forName(类名).newInstance()这样的方法进行解组,因而 Castor 不需要了解很多就能实例化类。但是,它还要求类必须能通过不带参数的构造器实例化。

好消息是Book和Author类只需简单修改就能避免这些错误。只需要为两个类增加不带参数的构造器,public Book() { }和public Author() { }。重新编译并运行代码。

想想这意味着什么。有了无参数的构造器,不仅仅是 Castor,任何类或者程序都能创建类的新实例。对于图书和作者来说,可以创建没有 ISBN、标题和作者的图书,或者没有姓和名的作者 — 这肯定会造成问题。如果 Castor 没有更先进的特性(下一期再讨论)这种缺陷就不可避免,因此必须非常注意类的使用。还必须考虑为任何可能造成NullPointerException错误的数据类型设置具体的值(字符串可以设为空串或者默认值,对象必须初始化等等)。

第二步:null 引起的更多问题

为Book和Author类增加无参数构造器,重新运行解组器。结果如下:

清单 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

看来还有问题没解决(不是说解组很简单吗?先等等,后面还要说)。值得注意的是,有几个字段为空。把这些字段和 XML 比较,会发现这些字段在 XML 文档都有正确的表示。那么是什么原因呢?

为了找出根源,首先注意图书的标题都设置正确。此外仔细观察第二本书The Book of the Dead。虽然初看起来似乎两本书的作者都没有设置,但问题不在这里。事实上,第二本书正确引用了两个Author对象。因此它引用了这两个对象,但是这些对象本身没有设置姓和名。因此图书的标题设置了,作者也一样。但是 ISBN 是空的,作者的姓和名也是空的。这是另一条线索:如果显示作者图书的总销售量,这些值就不会是空。

发现了吗?空值字段都没有 setter(或 mutator)方法。没有setIsbn()、setFirstName()等等。这是因为类被设计成需要构造信息(一旦书有了自己的 ISBN,这个 ISBN 实际上就不能改变了,基本上就意味着一本书形成了,因此最好要求构造/实例化新图书的时候提供新的 ISBN)。

还记得吗?Castor 使用反射机制。恰恰因为只有对象提供无参数构造器的时候它才能创建对象,因为没有setFieldName()方法就无法设置字段的值。因此需要为Book和Author类增加一些方法。下面是需要添加的方法(实现很简单,留给读者自己完成):

setIsbn(String isbn)(Book类)

setFirstName(String firstName)(Author类)

setLastName(String lastName)(Author类)

增加上述方法之后重新编译就行了。

第三步:成功解组

再次尝试运行解组器,将得到如下所示的结果:

清单 15. 增加作者和 ISBN setter 方法后的解组结果

[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 解组过程无关。Castor 用起来非常简单,只要类的结构符合 Castor 的要求 — 每个类都要有无参数构造器,每个字段都要有 get/set 方法。

 

回页首

代价是值得的

现在的类和最初相比没有很大变化。Book和Author没有面目全非,功能上没有很大不同(实际上没有什么区别)。但是现在设计还有一点问题。图书必须有 ISBN(至少在我看来),因此能够创建没有 ISBN 的图书 — 无参数构造器 — 令我感到困扰。此外,我也不愿意别人随便改动图书的 ISBN,这样不能正确地表示图书对象。

作者的修改不那么严重,因为对于作者这个对象来说姓名可能不是很重要的标识符。名字可以改,现在甚至姓都可以改。另外,两位作者如果同名,目前也没有区分的现成办法。但即使增加了更具唯一性、更合适的标识符(社会安全号码、驾驶证号、其他标识符),仍然需要无参数构造器和字段 setter 方法。因此问题仍然存在。

这就变成了效益和控制的问题:

Castor 是否提供了足够的好处 — 简化数据绑定 — 足以抵得上设计所要做出的让步?

对代码库是否有足够的控制权,避免滥用为 Castor 增加的方法?

只有您才能代表您的企业回答这些问题。对于多数开发人员来说,权衡的结果倾向于 Castor,就必须做出适当的让步。对于那些沉醉于设计或者由于特殊需求无法做出这些让步的少数人,可能更愿意自己编写简单的 XML 序列化工具。无论如何,应该将 Castor 放到您的工具包里。如果它不适合当前的项目,也许下一个用得上。

 

回页首

结束语

到目前为止还没有提到的 Castor 的一大特性是映射文件。我们是直接从 Java 代码翻译成 XML。但是有几个假定:

需要把 Java 类中的所有字段持久到 XML。

需要在 XML 中保留类名和字段名。

有一个类模型和需要反序列化的 XML 文档中的数据匹配。

对于企业级编程,这些假定都有可能不成立。如果将字段名和类名存储到 XML 文档中被认为是一种安全风险,可能会过多泄露应用程序的结构,该怎么办?如果交给您一个 XML 文档需要转化成 Java 代码,但希望使用不同的方法名称和字段名称,怎么办?如果只需要少数类属性存储到 XML 中,怎么办?

所有这些问题的答案都是一个:Castor 提供了进一步控制 Java 代码和 XML 之间映射关系的方式。下一期文章我们将详细介绍映射。现在,不要只考虑编组和解组,还要花时间想想为了让 Castor 正常工作所作的那些修改意味着什么。在思考的同时,不要忘记回来看看下一期文章。到时候再见。

原文:http://www.ibm.com/developerworks/cn/xml/x-xjavacastor2/index.html

第 3 部分: 模式之间的映射

您现在应该具备的(回顾)

与前一篇文章一样,本文也对您系统的设置情况和您的技能做一些假设。首先,需要按照本系列的第 1 部分中的描述下载并安装 Castor 的最新版本,设置类路径和相关的 Java 库(参见参考资料中本系列第一篇文章的链接)。然后,按照第 2 部分中的描述,熟悉 Castor 的基本编组和解组设施。

所以,您应该能够使用 Castor 提取出 XML 文档中的数据,并使用自己的 Java 类处理数据。用数据绑定术语来说,这称为解组(unmarshalling)。反向的过程称为编组(marshalling):您应该能够把 Java 类的成员变量中存储的数据转换为 XML 文档。如果您还不熟悉 Castor 的解组器和编组器,那么应该阅读第 2 部分(参见参考资料中的链接)。

 

回页首

非理想环境下的数据绑定

初看上去,您似乎已经掌握了有效地使用 Castor 所需了解的所有过程:设置、编组和解组。但是,您到目前为止学到的所有东西只适用于所谓的理想环境。在这样的环境中,每个人编写的 XML 都是完美的,其中的元素名是有意义的,比如 “title” 和 “authorAddress”,而不是 “t” 或 “aa”。Java 类是按照有组织的方式创建的,采用单数作为类名(比如 “Book”),采用单数名词作为成员变量名(比如 “isbn” 和 “price”)。另外,数据类型也是正确的:没有开发人员把price的数据类型设置为int而不是float,或者使用char数组存储字符串数据(这是 C 语言的做法)。

但是,大多数程序员所处的环境并不完美(我真想找到一个能够把我送到完美世界的魔法衣厨)。在大多数程序员所处的环境中有许多不理想的情况:XML 文档常常有糟糕的元素名和属性名,还要应付名称空间问题。元素数据存储在属性中,一些数据甚至由管道符或分号分隔。

Java 类是继承的,对它们进行重新组织在时间和工作量方面的成本可能会超过带来的好处。这些类常常无法简洁地映射到 XML 模式(认为 XML 和数据人员会与 Java 程序员相互妥协的想法也是非常不可思议的),而且在某些情况下,即使实现了简洁映射,也肯定不会跨所有类和数据。XML 元素名可能不合适,许多 Java 变量名也可能不合适。甚至可能遇到使用 Hungarian 表示法的名称,按照这种表示法,所有成员变量都以 “m” 开头,比如mTitle。这很不好看。

在这些情况下,您目前学到的数据绑定方法就无能为力了。XML 文档中可能会出现 Hungarian 风格的元素名,Java 类中也可能出现没有意义的结构。这种情况是无法接受的。如果不能按照您希望的方式获取和操作 XML 文档的数据,那么 Castor(或任何数据绑定框架)又有什么意义呢?

 

回页首

灵活数据绑定的目标

首先要注意,在 Castor 或任何其他数据绑定框架中,使用映射文件都要花一些时间。必须先学习一些新语法。尽管映射文件使用 XML 格式(大多数框架都是这样的),但是您需要学习一些新元素和属性。还必须做一些测试,确保 XML 和 Java 代码之间的相互转换产生您希望的结果。最后,如果亲自指定映射,而不是让框架处理映射,就可能在数据绑定中遇到更多的错误。例如,如果希望让框架把 XML 中的fiddler元素映射到 Java 代码中的violin属性,但是错误地声明这个属性是在player类中(应该是在Player类中),那么就会遇到错误。因此,在亲自指定映射时,必须非常注意拼写、大小写、下划线、单引号和双引号。

在学习使用映射文件之前,应该确定确实需要这么做。如果掌握了映射文件,但是却不使用它,那就是浪费时间。但是,映射确实有一些优点。

Java 代码不再受 XML 命名方式的限制

前面曾经提到,在把 XML 转换为 Java 代码时,大小写可能会导致错误。在 XML 中,最常用的做法是名称全部小写并加连字符,比如first-name。有时候,甚至会看到first_name。这样的名称会转换为很难看的 Java 属性名;没人愿意在代码中调用getFirst-name()。实际上,在大多数由程序员(而不是 XML 开发人员或数据管理员)编写的文档中,往往使用驼峰式(camel-case)命名法,比如firstName。通过使用映射文件,很容易把 XML 风格的名称(比如first-name)映射为 Java 风格的名称(比如firstName)。最棒的一点是,不需要强迫 XML 人员像 Java 程序员那样思考,这往往比学习新的映射语法困难得多。

XML 不再受 Java 命名方式的限制

是的,这似乎很明显。既然可以调整 XML 到 Java 的命名转换,反过来肯定也可以:在把 Java 类和属性包含的数据转换为 XML 时,可以修改 Java 名称。但是,有一个更重要,也更微妙的好处:不再受到 Java 类名和包名的限制。

这很可能成为一个组织问题。例如,在大多数情况下,XML 中的嵌套元素转换为类结构,最内层的嵌套元素转换成类属性(成员变量)。看一下清单 1 中的 XML:

清单 1. 代表图书的 XML

<?xml version="1.0" encoding="UTF-8"?>

<book>

<authors total-sales="0">

<last-name>Finder</last-name>

<first-name>Joseph</first-name>

</authors>

<isbn>9780312347482</isbn>

<title>Power Play</title>

</book>

Castor(或任何其他数据绑定框架)可能假设您需要一个Book类,这个类引用几个Author类实例。author 类应该有成员变量lastName和firstName(这里会出现前面提到的命名问题,Author中的成员变量应该是last-name,还是lastName?对于名字也有这个问题)。但是,如果这不是您希望的结果,应该怎么办?例如,您可能在一个称为Person或Professional的类中存储所有作家、会议演讲人和教授。在这种情况下就真有麻烦了,而且您不会愿意全面修改 XML 元素的结构和名称来解决这个问题。实际上,在这种情况下,要想原样保持 XML,使用映射是惟一的办法。

映射允许我们在 Java-XML 转换的两端指定命名方式。我们不希望由于 XML 文档的原因修改 Java 代码,同样不愿意修改 XML 结构来适应 Java 类和成员变量。另外,Java 包也会增加复杂性。尽管在 Castor 中包并不是大问题,但是仍然必须在编组的 XML 中存储 Java 类和包的相关信息,这对于业务逻辑(Java 类)和数据(XML)的隔离很不利。映射可以解决所有这些问题。

映射允许在现有环境中添加数据绑定

前两个问题(对 XML 和 Java 代码的限制)实际上与一个更大的问题相关。大多数情况下,您已经有了一组 Java 对象和一个或多个 XML 文档。因此,不具备前两篇文章中的那种自由度:不能让 Castor 根据它自己的规则把 Java 代码解组为 XML,或者为 XML 文档生成 Java 类。

相反,更为常见的情况是,您需要把一种新技术 — 数据绑定 — 添加到现有的结构中。在这种情况下,映射文件就是使用数据绑定的关键。在两个 “端点”(当前的对象模型和当前的 XML 结构)固定的情况下,映射使我们仍然能够把这两者的数据联系起来。简而言之,良好的映射系统使数据绑定能够在真实环境中发挥作用,而不仅仅停留在理论上。

 

回页首

一个映射场景示例

我们先来看一个简单的映射场景。在前一篇文章中,我们开发了Book和Author类。清单 2 是Book类。

清单 2. Book 类

package ibm.xml.castor;

import java.util.LinkedList;

import java.util.List;

public class Book {

/** The book's ISBN */

private String isbn;

/** The book's title */

private String title;

/** The authors' names */

private List<Author> authors;

public Book() { }

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 void setIsbn(String isbn) {

this.isbn = isbn;

}

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);

}

}

清单 3 是Author类。

清单 3. Author 类

package ibm.xml.castor;

public class Author {

private String firstName, lastName;

public Author() { }

public Author(String firstName, String lastName) {

this.firstName = firstName;

this.lastName = lastName;

}

public void setFirstName(String firstName) {

this.firstName = firstName;

}

public void setLastName(String lastName) {

this.lastName = lastName;

}

public String getFirstName() {

return firstName;

}

public String getLastName() {

return lastName;

}

}

注意:与前一篇文章相比,惟一的修改是在Author中删除了总销售额(totalSales变量)。我发现它的意义不大,所以在这个版本中删除了它。

一个比较麻烦的 XML 文档

这一次不使用前一篇文章中的 XML,而是使用一个不太容易映射的 XML 文档。清单 4 给出希望绑定到清单 2和清单 3中的 Java 类的 XML 文档。

清单 4. 用于数据绑定的 XML

<?xml version="1.0" encoding="UTF-8"?>

<book>

<author>

<name first="Douglas" last="Preston" />

</author>

<author>

<name first="Lincoln" last="Child" />

</author>

<book-info>

<isbn>9780446618502</isbn>

<title>The Book of the Dead</title>

</book-info>

</book>

需要映射哪些数据?

在处理映射文件语法或 Castor 的 API 之前,第一个任务是判断需要把 XML(清单 4)中的哪些数据绑定到 Java 类(清单 2和清单 3)。请考虑一会儿。

下面简要总结一下这个 XML 文档应该如何映射到 Java 类:

book元素应该映射到Book类的一个实例。

每个author元素应该映射到Author类的一个实例。

每个Author实例应该添加到Book实例中的authors列表中。

对于每个Author实例,firstName应该设置为name元素上的first属性的值。

对于每个Author实例,lastName应该设置为name元素上的last属性的值。

Book实例的 ISBN 应该设置为book-info元素中嵌套的isbn元素的值。

Book实例的书名应该设置为book-info元素中嵌套的title元素的值。

其中一些映射您已经知道如何实现了。例如,book到Book类实例的映射是标准的,Castor 会默认处理这个任务。但是,也有一些新东西,比如说作者。尽管把一个author元素映射到一个Author实例没什么问题,但是没有分组元素,比如authors,它清楚地显示出所有作者属于Book实例中的一个集合。

这里还有一些元素和属性并不直接映射到 Java 对象模型。Author类包含表示名字和姓氏的变量,但是在 XML 中只用一个元素(name)表示作者姓名,这个元素有两个属性。book-info元素中嵌套的书名和 ISBN 并不映射到任何 Java 对象。

这种情况非常适合使用映射文件。它使我们能够使用这种 XML(包含我们需要的数据,但是结构不符合希望),仍然能够把文档中的数据放到 Java 对象中。而且,映射文件本身并不难编写。

 

回页首

基本的映射文件

Castor 中的映射是通过使用映射文件(mapping file)实现的。映射文件仅仅是一个 XML 文档,它提供了如何在 Java 代码和 XML 之间进行转换的相关信息。因为您熟悉 XML,所以您会发现编写映射文件是非常容易的。实际上,对于简单的映射(只需修改元素名和 Java 类或成员变量的名称),只需一点儿时间就能编写好映射文件。

然后,当进行编组和解组时(前两篇文章已经介绍过如何在程序中进行编组和解组),Castor 会使用这个文件。只需对 Castor 多做一个 API 调用;其他代码都是一样的。

mapping 元素

Castor 映射文件的开始是一个普通的 XML 文档,然后是根元素mapping。还可以引用 Castor 映射 DTD。这样就可以检验文档,确保结构和语法没有任何问题。这会大大简化对映射的调试。

清单 5 给出最基本的映射文件。

清单 5. 基本的 Castor 映射文件

<?xml version="1.0"?>

<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"

"http://castor.org/mapping.dtd">

<mapping>

<!-- All your mappings go here -->

</mapping>

这个文件显然没有实质性内容,但它是所有映射文件的起点。

用 class 元素对类进行映射

建立基本的映射文件之后,差不多总是先要把一个 Java 类映射到一个 XML 元素。在这个示例中,需要把Book类映射到book元素中的数据。映射文件首先考虑类,所以需要添加一个class元素,并在这个元素的name属性中指定完全限定的 Java 类名,比如:

<class name="ibm.xml.castor.Book">

</class>

现在,可以使用map-to元素和xml属性指定这个类要映射到的 XML 元素。这个元素嵌套在 XML 元素映射到的 Java 类(完全限定,包括包名)的class元素中,比如:

<map-to xml="book" />

这非常简单。实际上,到目前为止,这个映射文件只实现 Castor 的默认操作。除非由于以下两个原因,否则可以删除这个部分并让 Castor 处理这个任务:

需要指定如何填充Book中的某些字段,比如书名和 ISBN。

如果使用映射文件,那么最好指定所有内容的映射方式。这会更明确 XML 和 Java 代码之间的配合。

把字段映射到元素

有了基本的类到元素的映射之后,就可以开始把Book类的字段映射到 XML 文档中的特定元素。Castor 映射文件使用field元素指定要使用的 Java 成员变量,使用其中嵌套的bind-xml元素指定映射到的 XML 元素。因此,下面的代码指定把Book类中的title变量映射到book元素中嵌套的title元素:

<field name="Title" type="java.lang.String"> <bind-xml name="title" node="element" /> </field>

在这里要注意两点。首先,提供了属性名(在这个示例中是 “title”)。这是属性(property)名,而不是成员变量名。换句话说,Castor 通过调用set[PropertyName]()来使用这个属性名。如果提供属性名 “foo”,Castor 就会尝试调用setfoo() — 这可能不是您希望的情况。因此,要仔细注意大小写,并使用属性名而不是 Java 类中的变量名。

要注意的第二点是type属性。这个属性向 Castor 说明数据究竟是什么类型的。在这个示例中,这很简单;但是在某些情况下,希望将以 XML 文本式数据存储的数字存储为整数、小数或者只是字符串,这时指定正确的数据类型就很重要了。另外,类型应该使用完全限定的 Java 类名,比如java.lang.String。

在bind-xml元素中,指定要绑定到的 XML 元素的名称(在这个示例中是 “title”),并使用node属性指定是绑定到元素还是绑定到属性。这样就可以轻松地使用元素和属性数据,只需在映射文件中稍做修改即可。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/2730/viewspace-3131386/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2019-12-18

  • 博文量
    492
  • 访问量
    11987