这种情况非常适合使用映射文件。它使我们能够使用这种 XML(包含我们需要的数据,但是结构不符合希望),仍然能够把文档中的数据放到 Java 对象中。而且,映射文件本身并不难编写。
Castor 中的映射是通过使用映射文件(mapping file)?实现的。映射文件仅仅是一个 XML 文档,它提供了如何在 Java 代码和 XML 之间进行转换的相关信息。因为您熟悉 XML,所以您会发现编写映射文件是非常容易的。实际上,对于简单的映射(只需修改元素名和 Java 类或成员变量的名称),只需一点儿时间就能编写好映射文件。
然后,当进行编组和解组时(前两篇文章已经介绍过如何在程序中进行编组和解组),Castor 会使用这个文件。只需对 Castor 多做一个 API 调用;其他代码都是一样的。
Castor 映射文件的开始是一个普通的 XML 文档,然后是根元素?mapping
。还可以引用 Castor 映射 DTD。这样就可以检验文档,确保结构和语法没有任何问题。这会大大简化对映射的调试。
清单 5 给出最基本的映射文件。
这个文件显然没有实质性内容,但它是所有映射文件的起点。
建立基本的映射文件之后,差不多总是先要把一个 Java 类映射到一个 XML 元素。在这个示例中,需要把?Book
?类映射到?book
?元素中的数据。映射文件首先考虑类,所以需要添加一个?class
?元素,并在这个元素的?name
?属性中指定完全限定的 Java 类名,比如:
现在,可以使用?map-to
?元素和?xml
?属性指定这个类要映射到的 XML 元素。这个元素嵌套在 XML 元素映射到的 Java 类(完全限定,包括包名)的?class
?元素中,比如:
这非常简单。实际上,到目前为止,这个映射文件只实现 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">
在这里要注意两点。首先,提供了属性名(在这个示例中是 “title”)。这是属性(property)?名,而不是成员变量名。换句话说,Castor 通过调用?set[PropertyName]()
?来使用这个属性名。如果提供属性名 “foo”,Castor 就会尝试调用?setfoo()?
— 这可能不是您希望的情况。因此,要仔细注意大小写,并使用属性?名而不是 Java 类中的变量名。
要注意的第二点是?type
?属性。这个属性向 Castor 说明数据究竟是什么类型的。在这个示例中,这很简单;但是在某些情况下,希望将以 XML 文本式数据存储的数字存储为整数、小数或者只是字符串,这时指定正确的数据类型就很重要了。另外,类型应该使用完全限定的 Java 类名,比如?java.lang.String
。
在?bind-xml
?元素中,指定要绑定到的 XML 元素的名称(在这个示例中是 “title”),并使用?node
?属性指定是绑定到元素还是绑定到属性。这样就可以轻松地使用元素和属性数据,只需在映射文件中稍做修改即可。
但是,这里需要解决一个问题:书名和 ISBN 嵌套在?book-info
?元素中,而不是直接嵌套在?book
?元素中。所以需要在映射文件中指出这一情况。
当遇到这种情况时 — 一个类中的一个字段并不直接映射到与这个类对应的 XML 元素中的数据 — 就需要在?bind-xml
?元素中使用?location
?属性。这个属性的值应该是 XML 文档中包含您希望绑定到的数据的元素。如果绑定到元素数据,它就应该是目标元素的父?元素;如果绑定到属性数据,它就应该是包含这个属性的?元素。
因此,在这个示例中,希望把?Book
?类的书名属性绑定到?title
?元素的值,而这个元素嵌套在?book-info
?元素中。下面是映射方法:
<field name="Title" type="java.lang.String">
<bind-xml name="title" node="element" <strong>location="book-info"</strong>
</field>
然后为书的 ISBN 添加另一个字段映射:
现在,Book
?类的属性都已经设置好了。该处理?Author
?类了。
按照相同的方式对其他类进行映射。惟一的差异是在其他类中不需要使用?map-to
?元素。
需要把?Author
?类中的字段映射到?author
?元素。请记住,下面是要处理的 XML 片段:
惟一需要注意的是,这里并不用两个元素分别包含名字和姓氏,而是使用一个带两个属性的元素。但是,前面已经使用过?location
?属性(需要用这个属性指定?name
?元素是映射到的位置)和?node
?属性(可以在这里指定要绑定到属性数据,而不是元素)。所以在映射文件中需要以下代码:
现在,您应该很容易看懂这些代码。这里指定了映射到的类(Author
)和这个类上要映射的属性(FirstName
?和?LastName
)。对于每个属性,指定要查看的 XML 元素(都是?name
)并指定需要的是属性数据。
如果看一下??的 XML,就会注意到并没有指定?Author
?类应该映射到哪个 XML 元素。这是一个问题,因为 Castor 不会猜测您的意图;只要使用映射文件,最好指定所有映射信息。
如果每本书只有一位作者,那么在?Book
?类中可能有一个?Author
?属性。在这种情况下,可以在映射文件中插入以下代码:
<field name="Title" type="java.lang.String">
<bind-xml name="title" node="element" location="book-info" />
</field>
<field name="Isbn" type="java.lang.String">
<bind-xml name="isbn" node="element" location="book-info" />
</field>
<strong><field name="Author" type="ibm.xml.castor.Author"></strong><strong><bind-xml name="author" /></strong><strong></field></strong>
<class name="ibm.xml.castor.Author">
<field name="FirstName" type="java.lang.String">
<field name="LastName" type="java.lang.String">
<bind-xml name="last" node="attribute" location="name" />
</field>
在这种情况下,把这一映射放在映射文件的图书部分中,因为映射的是属于?Book
?类的一个属性。映射的类型指定为ibm.xml.castor.Author
,并指定 XML 元素?author
。这样的话,Castor 的映射系统就会使用?class
?元素中的?Author
?类的定义处理作者的属性。
但是,问题在于?Book
?类中没有?Author
?属性。相反,这个类中有一个?Authors
?属性,其中包含?Author
?实例的集合。因此,必须让 Castor 把每个?author
?元素映射到一个?Author
?实例(这一步差不多已经完成了),然后把所有实例组合成?Book
?的?Authors
?属性。
为了映射图书和作者的关系,需要把几个元素(XML 文档中的每个?author
)映射到一个集合,然后把这个集合分配给?Book
?的?Authors
?属性。
首先使用?field
?元素,因为确实要映射到?Book
?的一个字段。还要把?name
?属性的值指定为 “Authors”,因为这是?Book
?中将映射到的属性:
接下来,需要提供属性的类型。您可能认为这应该是集合类型。但是,实际上希望指定集合中每个成员的类型。所以类型应该是 ibm.xml.castor.Author。您将会获得 ibm.xml.castor.Author 类的实例,Castor 将把这些实例放到?Authors
?属性中:
type="ibm.xml.castor.Author">
下面是关键之处:使用?collection
?属性指定这个属性是一个集合。这个属性的值是集合的类型。Castor 当前只支持两个值:vector(代表列表类型)和?array(代表数组类型)。通过标准的 Java 集合 API(比如?next()
?等调用)访问第一种集合;管理第二种集合的方法与 Java 数组相似,按照索引来访问它们,比如?ar[2]
。在这个示例中,因为 Java 类型是?List
,所以使用?vector:
collection="vector">
如果指定了?collection
?属性,Castor 就知道应该用与?type
?属性对应的值构建这个集合。因此,这里的?Authors
?属性应该是ibm.xml.castor.Author
?类型的实例的集合。
现在只剩下一步了:指定获取这些?Author
?实例的来源。这要使用?bind-xml
?元素:
所有工作都完成了;现在形成了一个完整的映射文件。最终的文件应该像清单 6 这样。
<field name="Title" type="java.lang.String">
<bind-xml name="title" node="element" location="book-info" />
</field>
<field name="Isbn" type="java.lang.String">
<bind-xml name="isbn" node="element" location="book-info" />
</field>
<field name="Authors" type="ibm.xml.castor.Author" collection="vector">
<bind-xml name="author" />
</field>
<class name="ibm.xml.castor.Author">
<field name="FirstName" type="java.lang.String">
<field name="LastName" type="java.lang.String">
<bind-xml name="last" node="attribute" location="name" />
</field>
最后,需要在解组过程中使用这个映射文件。以前,我们静态地使用?Unmarshaller
?类,通过调用?Unmarshaller.unmarshal()
?把 XML 转换为 Java 代码。但是,因为现在要使用映射文件,所以需要创建一个?Unmarshaller
?实例并设置一些选项。清单 7 给出的类处理从 XML 文档到 Java 对象的解组过程。
import java.io.FileReader;
import java.util.Iterator;
import java.util.List;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.xml.Unmarshaller;
public class BookMapUnmarshaller {
public static void main(String[] args) {
Mapping mapping = new Mapping();
try {
mapping.loadMapping("book-mapping.xml");
FileReader reader = new FileReader("book.xml");
<strong>Unmarshaller unmarshaller = new Unmarshaller(Book.class);</strong><strong>unmarshaller.setMapping(mapping);</strong>
Book book = (Book)unmarshaller.<strong>unmarshal(reader);</strong>
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());
}
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace(System.err);
}
}
}
与前两篇文章中的解组器相比,这里的更改非常少。首先,创建一个?Unmarshaller
?实例,使用的参数是 Book.class。这告诉解组器要解组的顶级类是哪个类。注意,这个顶级 Java 类对应于使用?map-to
?元素的 mapping 元素。然后设置映射,最后调用?unmarshal()
?的非静态版本。
现在完成了!这个过程与以前的过程差异并不大。作为练习,您可以自己试着编写把 Java 代码编组为 XML 的代码。请参考前一篇文章中的BookMarshaller
?类并设置映射文件,然后尝试在 XML 和 Java 代码之间来回转换。
数据绑定最终关注的是数据,而不是存储数据的格式。对于大多数 Java 程序员来说,处理 Java 对象是很容易的,而通过数据绑定,能够同样轻松地把来自各种来源(尤其是 XML)的数据转换为 Java 对象。另外,数据绑定环境中的映射甚至更进了一步:在填充 Java 对象时,可以非常灵活地处理数据源格式。因此,如果您喜欢数据绑定,那么一定也会喜欢映射;它使您能够绑定那些与您需要的命名约定不太相符的 XML 文档,也能够使用与您的 Java 对象不相符的结构。
对于数据人员,映射会带来同样的好处。当调用 Java 方法并保存在命名古怪的 XML 风格的变量中,或者 XML 文档中有多个元素全部映射到同一个类,那么不需要构建中间层就可以从 Java 类中取得所需的数据。最重要的是灵活性,能够对数据做您?想做的事情,而不受框架或工具的限制。
您已经看到了 Castor 在 XML 环境中提供了什么。但是,这仅仅触及到了 Castor 的皮毛。在下一篇文章中,将进一步扩展简单的 XML 数据绑定并研究 Castor 的 SQL 数据绑定设施。我们将把数据从 Java 类转移到 SQL 数据库中,再转移回来,而且不需要使用 JDBC。请复习一下 XML 和 SQL 知识,下个月我们将进一步体验数据绑定的威力。
学完下一篇文章(本系列的最后一篇)之后,您就能够用相同的 API 在 XML、Java 和 SQL 数据库之间进行转换。这甚至会带来比映射文件更大的灵活性。对于所有数据存储格式,可以使用单一 API 和相似的调用处理数据库、Java 对象和 XML 文档。实际上,对于那些了解 C# 的程序员来说,这听起来非常?像 LINQ(LINQ 是 Visual C# 2008 中最新最热门的技术之一)。相似的功能已经用 Java 技术实现了,而且具有一个稳定的 API。很棒,不是吗?所以请继续研究 Castor,绑定数据,试试您能实现哪些功能。享受创造的乐趣吧!我们网上相见。
原文:http://www.ibm.com/developerworks/cn/xml/x-xjavacastor3/index.html
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!