xml是描述与储蓄数据又不同于数据库;而HTML是显示技术,xml借助css/xsl/dso也可以显示
好有许多东西
来至:
http://hi.baidu.com/wang668/blog/item/6dd7deef99dd5530adafd531.html
和
http://www.baidu.com/s?wd=xml%D3%EBhtml%C7%F8%B1%F0%BC%B0Java%D6%D0%D3%C3%B5%C4%B6%E0%B2%BB
很不错
本文从Java EE系统中对XML应用的需求出发,详细论述了XML在Java企业级应用系统中的若干问题,内容涵盖XML的解析处理、XML的与Java的相互转换、XML的展示与抓取、XML数据的查询与持久化这几个方面,并重点论述了企业应用中XML与Java的转换的技术难题,并给出了一个实际可行的解决方案。
一、XML在Java中应用的概述
XML是一种可扩展标记语言(EXtensible Markup Language),于1998年2月10日被确立为 W3C 标准。由于是一种标记语言,这使得成为XML跨平台的、用于传输信息且独立于软件和硬件的工具。
XML设计的目的是为了描述数据,它使用使用文件类型声明(DTD)或者 XML Schema 来描述数据规范。带有 DTD 或者 XML Schema 的 XML 被设计为具有自我描述性。XML标签没有被预定义,您必须自行定义标签。
XML与HTML的最大的区别是两者目的不同,XML作为数据载体来描述数据,而HTML是为了格式化和展示数据。
在Java EE应用中,XML主要用来描述配置和数据的交换。配置信息描述是最常见不过的了,比如Java Web应用的描述符web.xml包括很多开源框架的配置信息。数据交换主要是XML的数据与其他格式的数据的转换,XML与Java、XML与数据库表记录、XML与HTML等转换。
二、XML在Java应用存在的若干问题
Java是一门面向对象的语言,在应用XML时,不可避免存在以下几个方面的问题:
XML数据解析
XML数据的存储
XML数据的转换
XML数据的查询
XML数据的形成
XML数据的展示
这些方面我们可以用一个模式图来表示,如下图:
上图是Java企业应中XML数据流图,这个图分左右两不部分,左边是XML数据的展示和抓取部分主要应用与客户端,右边是Java对象的持久化技术在服务端。不难看出,在客户端和服务器端之间,还应该有中间一个转换器工厂,将XML与Java对象联系起来,这个应用才完整。中间这个双向的转换器是企业应用中的难点,本文重点要论述的内容。
三、XML的数据的客户端展示与抓取
XML数据展示是Java EE 应用中客户端的首要职责,XML数据的展示技术很多,常见的有XSLT、XML数据岛技术、AJAX或者其他客户端脚本技术都可以完成数据的展示。这些技术都是目前比较流行的技术,具体选择应该根据技术复杂度、灵活性、软件需求等方面来考虑 。有关数据岛的原理如下:
在HTML中,通过使用非官方的 <xml> 标签可用来创建 XML 数据岛。这项技术首先在Internet Explorer中得以支持,其他浏览器的支持程度还需要进一步考证。
数据岛注入是在jsp或者HTML页面中,嵌入一个<xml id="[IslandId]",src="[Island.xml]"></xml>的标签来实现,其中id是表示数据岛标识,src是数据岛资源名称。
在客户端请求带有数据岛的页面时,首先通过JavaScript组件将数据岛中的数据展示到页面的表单域,当客户端填写完成数据之后,通过JavaScript组件将填写后的数据改写注入到XML数据岛中,然后将数据岛数据发送给服务器端响应请求。
另外,除了数据岛技术之外,还可以通过XSLT来展示XML数据,这个展示也很方便,但缺点是难以编写XSLT的文档,也难以实现复杂交互,因此实际中主要用于XML数据的格式化输出。
XML是数据的载体,通过相应的模板或组件技术,可以方便将XML转换PDF、Excel、HTML文档等等。这些一般都依赖一些组件工具来完成。
XML数据的抓取是客户端另一个重要职责,一般来说,用户通过浏览器将数据发送给服务器,在请求发出之前,应该通过一种策略将请求数据抓取为XML,然后将所有请求的数据一次性发送给服务器。服务器拿到这个XML数据,做一个XML到Java的转换,然后处理请求业务。
XML是客户端抓取是XML数据的来源之一,另外还可以将关系数据库表记录封装为XML数据。
从客户端抓XML常常通过JavaScript结合数据岛技术来实现,这需要为页面数据信息模型建立XML数据的模板,然后以数据岛的方法注入页面,页面在数据的展示修改保存都直接操作数据岛中的XML,结果将以XML格式的文本发送给服务器。这里面有两个问题需要注意:其一,如何定义合理的XML数据模型或者模板;其二,基于数据安全性考虑,XML模板应该遵循最小原则,尽可能暴露XML数据岛中最少的数据信息。
四、XML文档的解析
当客户端携带XML数据将请求发送给服务器端的处理程序,一般都需要进行XML数据的提取和转化,比如将XML通过Java XML API或者Dom4j、Jdom等解析。解析出来的数据既可以构建Java Bean对象,也可以直接将解析出来的数据参与计算或控制,这些都是根据实际需要来确定。
XML数据Java解析是Java应用XML的基础,也是后面要讲述的XML-Java数据转换器实现的基础。
目前Java XML解析器有很多,并且基本上都是开源的组件,解析原理有二:使用基于事件的XML简单API(Simple API for XML)称为SAX和基于树和节点的文档对象模型(Document Object Module)称为DOM。
Sun公司提供了Java API for XML Parsing(JAXP)接口来使用SAX和DOM,通过JAXP,可以使用任何与JAXP兼容的XML解析器来处理XML。但事实上并不是所有的Java XML解析工具都实现了JAXP所提供的接口。Dom4j就是其中的一个,但非常优秀,已成为Java XML解析的事实标准。
在使用Java组件或者API解析XML之前,应该对XML DOM的概念有个全面认识,这是解析XML基础。有关使用某个解析器解析XML的技术,都是解析器API的如何应用问题,在此不做具体介绍,因为相关的书籍文章比比皆是。
五、XML与Java对象的相互转换
为什么要在XML与Java之间做转换呢?
首先,先分析一下典型的客户端要完成一次注册会员请求过程,客户填写登记信息,JavaScript组件抓取数据写入数据岛,然后将携带XML数据岛信息将请求服务器端处理,服务器端接收到请求后,获取XML数据并封装为不同的实体对象,然后通过OR-Mapping组件(比如Hibernate)写入到数据库。当用户需要修改注册信息时,需要从数据库取出数据,通过OR-Mapping组件转换为Java实体对象,然后将Java实体对象转换为XML,在注入JSP或者HTML页面的数据岛,并展示到注册信息修改的页面上。将这两个过程用一个图形标识如下:
从上图可以看出,客户端请求会发送到服务层去做处理,而服务层暴露给外部只有接口方法,这些方法的参数大多都是和领域活动层密切相关的对象和参数。服务层也不接受XML数据作为参数。这时候,在请求到达服务层之间,将请求的XML数据转换为Java Object是非常有必要的。相反,当修改登记信息时,客户端会请求要修改登记信息,通过服务层查找到要修改的信息后,转换为XML,注入到XML数据岛,完成后,再做保存操作即可。
从上面的过程看来,存在一个迫切要解决的问题就是XML-Java Object之间的相互转换。这个转换相当于一个桥梁,将XML格式数据与页面表单域、Java对象、数据库表记录有机的联系起来,从而实现自由的交换处理。
至于XML数据的展示与抓取、Java对象通过OR-Mapping框架实现持久化,以及持久化数据转换为Java对象,这些技术已经相当的成熟,有很多的开源框架可以实现,大名鼎鼎的Hibernate就是一个优秀的OR-Mapping框架,并且得到的广泛的应用。因为本文将在下面的篇幅中不去深刻介绍这些技术,而将花费大篇幅去分析讲述XML与Java之间的相互转换和处理。
通常对于一个Java EE应用系统来说,领域层的实体数量都是相当多的,从几十个到上千都有可能,如果单纯用XML解析技术针对每个实体完成转换,将是相当麻烦和复杂的工作,不但会导致很多重复的工作,而且实体增加属性或删除属性的时候,解析转换也需要随之改变,维护起来无异于是异常噩梦,给软件项目带来巨大的风险。
因此,可以考虑设计一个转换器工厂,任何的实体对象都可以通过转换器工厂自动转换为XML,并且任何的XML通过转换器工厂也自动生成Java对象,从而将转换的过程简化,这个转换是由程序自动维护的。这样前台页面程序员仅仅关注自己页面的书写与展示,后台的设计人员只考虑领域模型的设计以及服务层的开发,节省了前后台人员相互交流的时间,也大大的增加的软件的健壮性和维护的代价,减少了项目的风险。
如何设计一个转换器工厂呢?下面将以登记业务为利,详细说明如何构建这样一个转换器工厂。
为了实现这个转换器工厂,首先要考虑的是如何实现一个转换器,将一个对象转换为XML,并且如何让这个过程可以逆转?因为这是实现一个转换工厂最基础的工作。
在此不得不引入一个开源的组件Xstream,XStream是一个Java和XML相互转换的工具。使用非常的简单,并且可以自定义转换的过程,但是这个组件仅能做的就是这些最基本的工作,有了这个组件,我们可以不用考虑具体一个Java对象和XML之间问题了。而是将注意力转移到一个转换器工厂的构建上去。
在构建Java Object与XML转换器工厂之前,应该对这个工厂的的功能加以描述:
工厂的目标是生产转换器,转换器的生成应该是自动的。传入一个Java类,就应该生成一个转换器对象。
转换器的应该是双向的。可以从Java对象生成XML,也可以反向从XML转向Java。
转换器的生成应该是可配置的,而不是手动编码去构建。并且转换类支持类的组合。
下面就以一个用户登录的例子来说明如何实现这个转换器工厂的构建。在此之前,我们先看看实体模型,以及目标的XML数据模板。
上面是模型的UML类图,一个User类中包含两个相关联的类UserProfile和Address类。一个用户对应一个UserProfile(用户的其他信息)和一组Address(用户地址信息)。我们就以这样一个模型为研究对象,实现一个转换器工厂。
在实现转换器之前,先看看XStream是如何做到让对象转换为XML的。
XStream的应用的核心就是要构建出实体模型,然后将实体按照所表达的信息组合成许多单元,然后每个单元确定作为完整信息模型。在本例中,User、Address、UserProfile三个实体类就是一个单元,User就是一个完整的信息模型,表述用户的信息。现在,假设我们已经拥有了一个User对象,如何将这个对象转换为XML,代码如下:
//创建转换器对象
XStream xStream = new XStream();
//为类所生成的节点添加别名
xStream.alias("User", User.class);
xStream.alias("Address", Address.class);
xStream.alias("UserProfile", UserProfile.class);
//为用户信息模型的成员加上别名
xStream.aliasField("UserProfile", User.class, "userProfile");
//将User对象转换为XML数据
String xml = xStream.toXML(user);
System.out.println(xml);
我们可以看到这段代码非常简单,甚至我们可以直接new一个转换器出来直接将对象转换为XML,但是一般来说别名是非常必要的。这段代码打印的结果如下:
<User>
<id>100</id>
<username>admin</username>
<password>123456</password>
<UserProfile>
<id>1000</id>
<sex>M</sex>
<birthday>1982-04-03 19:54:14.421 CST</birthday>
<telphone>66668888</telphone>
<email>admin@yahoo.com.cn</email>
<userId>100</userId>
</UserProfile>
<addresses class="set">
<Address>
<id>2000</id>
<addr>郑州市花园路</addr>
<postCode>450001</postCode>
</Address>
<Address>
<id>2001</id>
<addr>郑州市经三路</addr>
<postCode>450001</postCode>
</Address>
</addresses>
</User>
如果我们需要将这个XML再转换为Java Object,那么一样的容易,只要将这个XML传递给相对应的转换器即可。代码如下:
User u =(User)xStream.fromXML(xml);
以上代码还不足以给您带来惊喜,因为企业应用中的信息模型动辄好几百个,如果一个一个去写这些转换器是件相当耗时而且容易出错的工作。必须想办法构建一个转换器工厂,让工厂自动构建出所需要的转换器。
为此,可以考虑定义一个配置文件,将每个信息模型做为一个单元写入其中。并且在这些单元内部,详细定义各个域的别名等信息。这样,通过配置文件,就可以获取到转换构建信息,从而为工厂产品的生产提供了依据。构建的配置文件信息如下:
<?xml version="1.0" encoding="GBK"?>
<!--
Document : InfoUnits.xml.xml
Created on : 2008年6月9日, 下午8:10
Author : leizhimin
Description:
Purpose of the document follows.
-->
<InfoUnits>
<User class ="User.class">
<ID en="id" />
<MC en="username" />
<MM en="password"/>
<UserProfile class="UserProfile.class">
<ID en = "id"/>
<XB en = "sex"/>
<SR en ="birthday"/>
<DH en = "telphone"/>
<YJDZ en ="email"/>
<YHID en ="userId"/>
</UserProfile>
<Address-List>
<Address class = "Address.class">
<ID en = "id"/>
<DZ en = "addr"/>
<YB en = "postCode"/>
</Address>
</Address-List>
</User>
</InfoUnits>