




已阅读5页,还剩11页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
对于主要关心文档数据内容的应用程序,Java XML 数据绑定是一种代替 XML 文档模型的强大机制。本文中,企业 Java 专家 Dennis Sosnoski 介绍数据绑定,并讨论什么使它如此令人瞩目。然后,他向读者展示了如何利用 Java 数据绑定的开放源代码 Castor 框架来处理日益复杂的文档。如果您的应用程序更多的把 XML 作为数据而不是文档,您就会愿意了解这种处理 XML 和 Java 技术的简单有效的方法。应用程序中使用 XML 文档的多数方法都把重点放在 XML 上:从 XML 的观点使用文档,按照 XML 元素、属性和字符数据内容编程。如果应用程序主要关心文档的 XML 结构,那么这种方法非常好。对于更关心文档中所含数据而非文档本身的许多应用程序而言,数据绑定提供了一种更简单的使用 XML 的方法。文档模型与数据绑定文档模型和数据绑定都在内存中建立文档的表示,都需要在内部表示和标准文本 XML 之间双向转换。两者的区别在于文档模型尽可能保持 XML 结构,而数据绑定只关心应用程序所使用的文档数据。为了说明这一点,图 1 给出了一个简单 XML 文档的数据模型视图。文档成分在这个例子中只有元素和文本节点通过反映原始 XML 文档的结构连接在一起。形成的节点树很容易和原始文档联系,但要解释树中表示的实际数据就不那么容易了。图 1. 文档的文档模型视图如果应用程序使用 XML 文档模型方法,您就需要处理这种类型的树。这种情况下,您将使用节点之间的父子关系在树的上下层之间导航,使用属于同一父节点的子女之间的兄弟关系在树的同一层中导航。您可以非常详尽地处理树结构,当把树序列化为文本时,生成的 XML 文档将反映您所做的修改(比如插入的注释)。现在来看看与图 1 截然不同的图 2,它表示同一文档的数据绑定视图。在这里,转换过程几乎隐藏了原始 XML 文档的所有结构,但是因为只有通过两个对象,更容易看清楚真正的数据,也更很容易访问这些数据。图 2. 文档的数据绑定视图使用这种数据结构就像是一般的 Java 编程甚至根本不需要知道 XML!(哦,还是不要走得太远了我们这些专家顾问还得活)您的项目中至少要有人明白,这种数据结构和 XML 文档之间的映射是如何建立的,但这仍然是向简化迈出的一大步。不仅仅是编程的简化,数据绑定还带来其他的好处。与文档模型方法相比,因为抽掉了许多文档细节,数据绑定通常需要的内存更少。比如前面两个图中所示的数据结构:文档模型方法使用了 10 个单独的对象,与此相比数据绑定只使用了两个。要创建的东西少,构造文档的数据绑定表示可能就更快一些。最后,数据绑定与文档模型相比,应用程序可以更快地访问数据,因为您可以控制如何表示和存储数据。我后面还要讲到这一点。既然数据绑定那么好,为何还要使用文档模型呢?以下两种情况需要使用文档模型: 应用程序真正关注文档结构的细节。比方说,如果您在编写一个 XML 文档编辑器,您就会坚持使用文档模型而非数据绑定。 您处理的文档没有固定的结构。比如实现一种通用的 XML 文档数据库,数据绑定就不是一种好办法。许多应用程序使用 XML 传输数据,但并不关心文档表示的细节。这类应用程序非常适合使用数据绑定。如果您的应用程序符合这种模式,请继续读下去。Castor 框架目前有几种不同的框架支持 Java XML 数据绑定,但还没有标准的接口。这种情况最终会得到改变:Java Community Process (JCP) 的 JSR-031 正在努力定义这方面的标准(请参阅参考资料)。现在让我们选择一个框架并学习使用它的接口。本文选择了 Castor 数据绑定框架。Castor 项目采用 BSD 类型的证书,因此可在任何类型的应用程序(包括完整版权的项目)中使用。 Castor 实际上仅仅有 XML 数据绑定,它还支持 SQL 和 LDAP 绑定,尽管本文中不讨论这些其他的特性。该项目从 2000 年初开始发起,目前处于后 beta 状态(一般可以使用这个版本,但是如果需要问题修正,您可能需要升级到目前的 CVS 版本)。请参阅参考资料部分的 Castor 站点链接,以了解更多的细节并下载该软件。 默认绑定Castor XML 数据绑定很容易上手,甚至不需要定义 XML 文档格式。只要您的数据用类 JavaBean 的对象表示,Castor 就能自动生成表示这些数据的文档格式,然后从文档重构原始数据。那么“类 JavaBean”是什么意思呢?真正的 JavaBean 是可视化组件,可以在开发环境中配置以用于 GUI 布局。一些源于真正 JavaBean 的惯例已经被 Java 团体普遍接受,特别是对于数据类。如果一个类符合以下惯例,我就称之为是“类 JavaBean”的: 这个类是公共的 定义了公共的默认(没有参数)构造函数 定义了公共的getX和setX方法访问属性(数据)值关于技术定义已经扯得太远了,当提到这些类 JavaBean 类时,我将不再重复说明,只是称之为“bean”类。在整篇文章中,我将使用航线班机时刻表作为示例代码。我们从一个简单的 bean 类开始说明它的工作原理,这个类表示一个特定的航班,包括四个信息项: 飞机编号(航空公司) 航班编号 起飞时间 抵达时间下面的清单 1 给出了处理航班信息的代码。清单 1. 航班信息 beanpublic class FlightBeanprivate String m_carrier;private int m_number;private String m_departure;private String m_arrival;public FlightBean() public void setCarrier(String carrier) m_carrier = carrier;public String getCarrier() return m_carrier;public void setNumber(int number) m_number = number;public int getNumber() return m_number;public void setDepartureTime(String time) m_departure = time;public String getDepartureTime() return m_departure;public void setArrivalTime(String time) m_arrival = time;public String getArrivalTime() return m_arrival;您可以看到,这个 bean 本身没有什么意思,因此我想增加一个类并在默认的 XML 绑定中使用它,如清单 2 所示。清单 2. 测试默认的数据绑定import java.io.*;import org.exolab.castor.xml.*;public class Testpublic static void main(String argv) / build a test beanFlightBean bean = new FlightBean();bean.setCarrier(AR);bean.setNumber(426);bean.setDepartureTime(6:23a);bean.setArrivalTime(8:42a);try / write it out as XMLFile file = new File(test.xml);Writer writer = new FileWriter(file);Marshaller.marshal(bean, writer);/ now restore the value and list what we getReader reader = new FileReader(file);FlightBean read = (FlightBean)Unmarshaller.unmarshal(FlightBean.class, reader);System.out.println(Flight + read.getCarrier() +read.getNumber() + departing at +read.getDepartureTime() + and arriving at + read.getArrivalTime(); catch (IOException ex) ex.printStackTrace(System.err); catch (MarshalException ex) ex.printStackTrace(System.err); catch (ValidationException ex) ex.printStackTrace(System.err);这段代码首先构造了一个FlightBeanbean,并使用一些固定的数据初始化它。然后用该 bean 默认的 Castor XML 映射将其写入一个输出文件。最后又读回生成的 XML, 同样使用默认映射重构 bean,然后打印重构的 bean 中的信息。结果如下:Flight AR426 departing at 6:23a and arriving at 8:42a这个输出结果表明您已经成功地来回转换了航班信息(不算太糟,只有两次方法调用)。现在我还不满足于简单控制台输出,准备再往深处挖一挖。幕后为了更清楚地了解这个例子中发生了什么,看一看Marshaller.marshal()调用生成的 XML。文档如下:8:42a6:23aARCastor 使用 Java 内部检查机制检查Marshaller.marshal()调用传递的对象。在本例中,它发现了定义的四个属性值。Castor 在输出的 XML 中创建一个元素(文档的根元素)表示整个对象。元素名从对象的类名中衍生出来,在这里是flight-bean。然后Castor 用以下两种方法中的一个,把该对象的属性值包括进来: 对于具有基本类型值的属性创建元素的一个属性(本例中只有number属性通过getNumber()方法公开为int值)。 对于每个具有对象类型值的属性创建根元素的一个子元素(本例中的所有其他属性,因为它们是字符串)。结果就是上面所示的 XML 文档。改变 XML 格式如果不喜欢 Castor 的默认映射格式,您可以方便地改变映射。在我们的航班信息例子中,比方说,假定我们需要更紧凑的数据表示。使用属性代替子元素有助于实现这个目标,我们也许还希望使用比默认的名字更短一些的名字。如下所示的文档就可以很好地满足我们的需要:定义映射为了让 Castor 使用这种格式而非默认的格式,首先需要定义描述这种格式的映射。映射描述本身(非常意外的)是一个 XML 文档。清单 3 给出了把 bean 编组成上述格式的映射。清单 3. 紧凑格式的映射Basic mapping exampleclass元素定义了一个命名类FlightBean的映射。通过在该元素中加入auto-complete属性并把值设为true,您可以告诉 Castor 对于该类的任何属性,只要没有在这个元素中专门列出,就使用默认映射。这样非常简便,因为number属性已经按照希望的方式处理了。子元素map-to告诉 Castor,要把FlightBean类的实例映射为 XML 文档中的flight元素。如果您继续使用默认的元素名flight-bean(参阅幕后小节中默认映射输出的例子),可以不使用该元素。最后,对于每个希望以非默认方式处理的属性,可以引入一个field子元素。这些子元素都按照相同的模式:name属性给出映射的属性名,bind-xml子元素告诉 Castor 如何映射那个属性。这里要求把每个属性映射成给定名称的属性。使用映射现在已经定义了一个映射,您需要告诉 Castor 框架在编组和解组数据时使用那个映射。清单 4 说明了要实现这一点,需要对前面的代码做哪些修改。清单 4. 使用映射编组和解组./ write it out as XML (if not already present)Mapping map = new Mapping();map.loadMapping(mapping.xml);File file = new File(test.xml);Writer writer = new FileWriter(file);Marshaller marshaller = new Marshaller(writer);marshaller.setMapping(map);marshaller.marshal(bean);/ now restore the value and list what we getReader reader = new FileReader(file);Unmarshaller unmarshaller = new Unmarshaller(map);FlightBean read = (FlightBean)unmarshaller.unmarshal(reader);. catch (MappingException ex) ex.printStackTrace(System.err);.与前面清单 2默认映射所用的代码相比,这段代码稍微复杂一点。在执行任何其他操作之前,首先要创建一个Mapping对象载入您的映射定义。真正的编组和解组也有区别。为了使用这个映射,您需要创建Marshaller和Unmarshaller对象,用定义的映射配置它们,调用这些对象的方法,而不是像第一个例子那样使用静态方法。最后,您必须提供对映射错误产生的另一个异常类型的处理。完成这些修改后,您可以尝试再次运行程序。控制台输出与第一个例子相同(如清单 2所示),但是现在的 XML 文档看起来符合我们的需要:处理集合现在单个航班数据已经有了我们喜欢的形式,您可以定义一个更高级的结构:航线数据。这个结构包括起降机场的标识符以及在该航线上飞行的一组航班。清单 5 给出了一个包含这些信息的 bean 类的例子。清单 5. 航线信息 beanimport java.util.ArrayList;public class RouteBeanprivate String m_from;private String m_to;private ArrayList m_flights;public RouteBean() m_flights = new ArrayList();public void setFrom(String from) m_from = from;public String getFrom() return m_from;public void setTo(String to) m_to = to;public String getTo() return m_to;public ArrayList getFlights() return m_flights;public void addFlight(FlightBean flight) m_flights.add(flight);在这段代码中,我定义了一个addFlight()方法,用于每次增加一个属于这条航线的航班。这是在测试程序中建立这种数据结构非常简便的办法,但是可能和您预料的相反, Castor 在解组时并不使用种方法向航线中增加航班。相反,它使用getFlights()方法访问一组航班,然后直接添加到集合中。在映射中处理航班集合只需要稍微改变上一个例子(如清单 3所示)中的field元素。清单 6 显示了修改后的映射文件。清单 6. 映射包含一组航班的航线Collection mapping example一切都和上一个映射(如清单 3所示)完全相同,只不过用field元素定义了一个RouteBean的flights属性。这个映射用到了两个原来不需要的属性。collection属性的值collection把该属性定义成一个 java.util.Collection (其他值分别定义数组,java.util.Vectors 等等)。type属性定义包含在集合中的对象类型,值是完整的限定类名。这里的值是FlightBean,因为对这些类我没有使用包。另一个区别在FlightBean类元素中,不再需要使用map-to子元素定义绑定的元素名。定义RouteBean的flights属性的field元素,通过它的bind-xml子元素定义了这一点。因为编组或解组FlightBean对象只能通过该属性,它们将永远使用这个bind-xml元素设定的名称。我不再详细列出这个例子的测试程序,因为数据绑定部分和上一个例子相同。以下是用一些示例数据生成的 XML 文档:回页首对象引用现在可以为处理完整的航班时刻表做最后的准备了。您还需要增加三个 bean: AirportBean用于用于机场信息 CarrierBean用于航线信息 TimeTableBean把一切组合起来为了保持趣味性,除了上一个例子(参阅处理集合)中用到的RouteBean和FlightBean之间的从属关系,您还要在 bean 之间增加一些联系。连接 bean要增加的第一个联系是修改FlightBean,让它直接引用班机信息,而不再仅仅用代码标识班机。以下是对FlightBean的修改:public class FlightBeanprivate CarrierBean m_carrier;.public void setCarrier(CarrierBean carrier) m_carrier = carrier;public CarrierBean getCarrier() return m_carrier;.然后对RouteBean做同样的修改,让它引用机场信息:public class RouteBeanprivate AirportBean m_from;private AirportBean m_to;.public void setFrom(AirportBean from) m_from = from;public AirportBean getFrom() return m_from;public void setTo(AirportBean to) m_to = to;public AirportBean getTo() return m_to;.我没有给出新增 bean 自身的代码,因为和前面的代码相比没有什么新鲜的东西。您可以从下载文件 code.jar 中找到完整的示例代码(请参阅参考资料)。映射引用您可能需要映射文档的其他一些特性,以支持编组和解组的对象之间的引用。清单 7 给出了一个完整的映射:清单 7. 完整的时刻表映射Reference mapping example除了新增的 bean 之外,这里有一个重要的变化,就是增加了identity和reference属性。class元素的identity属性,通知 Castor 这个命名属性是该类实例的唯一标识符。在这里,我把CarrierBean和AirportBean的ident属性定义成它们的标识符。bind-xml元素的reference属性,提供了对于该映射 Castor 所需要的另一部分链接信息。reference设为true的映射告诉 Castor 编组和解组引用对象的标识符,而不是对象本身的副本。从RouteBean链接AirportBean(表示航线的起止点)的引用,从FlightBean链接CarrierBean的引用,都使用了这种方法。当 Castor 使用这种类型的映射解组数据时,它自动把对象标识符转化为对实际对象的引用。您需要保证标识符的值确实是唯一的,甚至不同类型的对象之间也要保证这种唯一性。对于本例中的数据,这一点不成问题:飞机的标识符是两个字符,而机场的标识符是三个字符,永远不会冲突。如果确实有潜在冲突的可能性,只要在所代表的对象类型的每个标识符加上唯一的前缀,就可以很容易地避免这种问题。编组后的时刻表这个例子的测试代码没有新东西,只是增加了一些示例数据。清单 8 给出了编组形成的 XML 文档:清单 8. 编组的时刻表Arctic AirlinesCombined AirlinesSeattle, WASeattle-Tacoma International AirportLos Angeles, CALos Angeles International Airport使用数据现在,时刻表中的所有数据都最终完成了,简单地看一看如何在程序中处理它们。使用数据绑定,您已经建立了时刻表的数据结构,它由几种类型的 bean 组成。处理数据的应用程序代码可以直接使用这些 bean。比方说,假设您要查看在西雅图和洛杉矶之间有哪些航班可供选择,并且要求班机至少具备指定的最低品质评价级别。清单 9 给出了使用数据绑定 bean 结构获取这些信息的基本代码(完整的细节请参阅从参考资料下载的源文件)。清单 9. 航班查找程序代码private static void listFlights(TimeTableBean top, String from,String to, int rating) / find the routes for outbound and inbound flightsIterator r_iter = top.getRoutes().iterator();RouteBean in = null;RouteBean out = null;while (r_iter.hasNext() RouteBean route = (RouteBean)r_iter.next();if (route.getFrom().getIdent().equals(from) &route.getTo().getIdent().equals(to) out = route; else if (route.getFrom().getIdent().equals(to) &route.getTo().getIdent().equals(from) in = route;/ make sure we found the routesif (in != null & out != null) / find outbound flights meeting carrier rating requirementIterator o_iter = out.getFlights().iterator();while (o_iter.hasNext() FlightBean o_flight = (FlightBean)o_iter.next();if (o_flight.getCarrier().getRating() = rating) / find inbound flights meeting carrier rating/ requirement, and leaving after outbound arrivesint time = timeToMinute(o_flight.getArrivalTime();Iterator i_iter = in.getFlights().iterator();while (i_iter.hasNext() FlightBean i_flight = (FlightBean)i_iter.next();if (i_flight.getCarrier().getRating() = rating&timeToMinute(i_flight.getDepartureTime() time) / list the flight combinationprintFlights(o_flight, i_flight, from, to);您可以尝试使用前面清单 8中的数据。如果您询问从西雅图(SEA)到洛杉矶(LAX)、级别大于或等于 8 的班机,就会得到如下的结果:Leave SEA on Arctic Airlines 426 at 6:23areturn from LAX on Arctic Airlines 593 at 9:27aLeave SEA on Arctic Airlines 426 at 6:23ar
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025浙江缙云县保安服务有限公司招聘国有企业项目用工10人笔试参考题库附带答案详解
- 2025中国一冶集团建安公司春季校园招聘笔试参考题库附带答案详解
- 危险因素安全培训课件
- 地质灾害滑坡课件
- 地球的内部构造
- 回忆我的母亲课件介绍
- 地球与地球仪课件
- 地板厂安全培训课件
- 危化安全教育培训课件
- 嘉兴消防安全知识培训课件
- 主播岗位职业生涯规划与管理
- 老年综合评估各种表格
- 2025至2030中国牙科手机消耗行业项目调研及市场前景预测评估报告
- NBT 11551-2024 煤矿巷道TBM法施工及验收标准
- 口腔瓷贴面诊疗沟通指南
- 山东安全管理人员大考试题库
- 2025-2030冲牙器行业市场深度调研及发展趋势与投资前景预测研究报告
- 70华诞主题班会课件
- 建筑抗震设计规程(下)DB62T3055-2020
- 商品赠品协议书范本
- 工伤事故赔偿协议书范本
评论
0/150
提交评论