-- 作者:jomper
-- 发布时间:3/22/2006 7:54:00 PM
-- [原创]对XML文档操作的通用CRUD(JDOM版)
/** *1.操作XML文档分析 *2.设计过程 *3.适用范围 */ 关键词:元组元素 元组元素相当于关系数据库里元组的概念。 1.0 操作XML文档分析 1.1 为什么要写XML文档操作的通用CRUD(Create Read/Retrieve Update Delete)。 做过几次读写改删XML文档的工作,总是被大量的element构建弄的头晕眼花。XML文档简单点还好,一旦元素属性变得复杂,整个XMLDAO模块就会变的相当臃肿。导致程序的可读性下降,bug增多。而且每个XMLDAO的CRUD都是做的同一个流程,完全可以独立出来。 1.2 XMLDAO的CRUD分析 在XML的CRUD操作中有几个功能是会反复重复的。 (1)Input:读XML文档,并用SAX或者DOM解析成Document对象。 (2)Find:用XPath在文档中查询元素,并返回查询结果。其实在操作XML数据的时候Find跟Read基本是一个概念,查询数据后当然是要返回一个查询结果的,不然好浪费资源。这里结果就是一个list。 (3)Construts:构建需要操作的Element。包括构建新元素,更新元素或属性的值,删除元素。为什么不是Document对象?其实没什么区别。 (4)Output:写Document对象到XML文档。 唯一有区别的地方是Construts Element。对于不同的XML文档存在着不同Element上下文。我要做的就是把除Construts Element之外的工作独立出来。 Create操作 需要Construts Element和输出Document。 Read操作 需要读取XML文档,并返回一个读取结果。 Retrieve操作 需要读取XML文档并给特定的Element添加一个子元素。一般这个特定的元素就是元组元素的父亲。元组元素相当于关系数据库里元组的概念。 Update操作 需要读取XML文档,然后通过XPath获得查询结果并重新构建这个结果。最后输出到文档。 Delete操作 需要读取XML文档,然后通过XPath获得查询获得需要删除元素。最后在上下文中删除它。 下面是每个操作的工作流程: Create Read Retrieve Update Delete Input √ √ √ √ Find √ √ √ Construts √ √ √ Output √ √ √ 2.0 设计过程 2.1 XML文档设计(XML文档设计的一点建议) 2.1.1 建议一 尽量少用elements来装载数据,无论什么时候都尽可能用attributes来代替 根据《Java Enterprise Best Practices》- 5.1.3 Use Elements Sparingly, Attributes Excessively的内容,“ use elements infrequently and, instead, use attributes whenever possible. ”尽量少用elements来装载数据,无论什么时候都尽可能用attributes来代替。这样做的好处是针对SAX的处理机制做出了优化。可能你要说你从不使用SAX,你有其他的解决方案例如:DOM,JAXP等等。事实上这些高层的API例如:DOM,JAXP,JDOM,SOAP在底层还是使用的SAX,如果你不相信你可以直接去看它们的源码。 一个XML文档的例子: 例2.1: <?xml version="1.0" encoding="UTF-8"?> <JomperWorld> <userInfo userCode="-1426920836"> <accountInfo userId="jomper1" password="888888"></accountInfo> <basicInfo nickName="jo" gender="男" placeOfBirth="hubei" email="1@1.com" registerDate="3-17-2006" picture="null"></basicInfo> <detailInfo> <experienceOfTeaches><![CDATA[test12]]></experienceOfTeaches> <individualHonor><![CDATA[test22]]></individualHonor> </detailInfo> </userInfo> </JomperWorld> 2.1.2 建议二 给你的元组元素确定一个KEY 这样设计应当还注意一个问题,特别是你可能会使用XPath。 就看例2.1,如果元组元素<userInfo/>没有一个userCode属性。那么已知accountInfo.userId想查询basicInfo.nickName 的XPath就必须这样写 "/JomperWorld/userInfo[accountInfo/@userId='jomper1']",但是得到的是<userInfo/>而不是期望的nickName属性。必须userInfo.getChild("basicInfo").getAttribute("nickName");才能获得nickName属性。 当然这不会给设计带来致命的打击,但多少会有些不便,这个不便具体有多少只有你自己知道。 2.2 Construts element类的设计 2.2.1 Builder接口设计 由于将给不同的XML文档构建元组元素,所以这个接口是通用XMLDAO的关键。 需要Construts Element的操作包括create,retrieve,update。 例2.2: public interface Builder { /** builderType */ public static final int USER_INFO_BUILDER= 0; //更多的builderType...... /** * create&retrieve构建元素 * @param toggle create&add判断开关 * @param facade elements外观 * @return 元组元素 */ public Element addBuilder(boolean toggle,Object facade); /** * update构建元素 * @param facade elements外观 * @param mainElement 元组元素 */ public void updateBuilder(Object facade,Element mainElement); } builderType 是用来在XMLDAO里判断facade的对象类型。本来可以用instanceof做迭代判断最后确定其对象类型,但这样效率太低。于是传一个参数来确定。不知道还有没有更好的办法。 facade 可以是一个元素的DTO(元组元素很简单的情况),也可以是几个元素DTO的外观。可以直接通过它获得element的数据,也可以通过它间接获得其他元素DTO的数据。 2.2.2 ConcreteBuilder类的设计 例2.3: public class UserInfoBuilder implements Builder{ private Element rootElement,userInfoElement; UserInfoFacade userInfo; public UserInfoBuilder(){ //初始化rooElement和元组Element(userInfoElement) elementDecorator = new ElementDecorator(); rootElement = new Element(UserInfo.ROOT_ELEMENT); userInfoElement = new Element(UserInfo.USER_INFO_ELEMENT); } /** * 创建,添加元素构建器 * @param toggle 开关 [true 添加][false 创建] * @param userInfo 用户信息 * @return element [toggle:true 返回主元素][toggle:false 返回根元素] */ public Element addBuilder(boolean toggle,Object userInfo){ if(userInfo instanceof UserInfoFacade){ this.userInfo = (UserInfoFacade)userInfo; }else{ if (logger.isDebugEnabled()) {logger.debug("addBuilder() - UserInfoFacade 对象匹配失败");} } //具体Construts动作省略,详情请看源码 return toggle ? userInfoElement : rootElement.addContent(userInfoElement); } /** * 更新文档元素 * @param userInfo 用户信息 * @param mainElement 主元素 */ public void updateBuilder(Object userInfo,Element mainElement){ if(userInfo instanceof UserInfoFacade){ this.userInfo = (UserInfoFacade)userInfo; }else{ if (logger.isDebugEnabled()) {logger.debug("updateBuilder() - UserInfoFacade 对象匹配失败");} } // 具体更新动作省略,详情请看源码 } } 2.2.3 BuilderFactory public class BuilderFactory { private static final Logger logger = Logger.getLogger(BuilderFactory.class); public static Builder getBuilder(int builderType){ switch(builderType){ case Builder.USER_INFO_BUILDER : if (logger.isDebugEnabled()) {logger.debug("getBuilder() - 创建UserInfoBuilder实例完毕");} return new UserInfoBuilder(); default: if (logger.isDebugEnabled()) {logger.debug("getBuilder() - 创建Builder实例失败");} return null; } } } 这里用工厂来创建不同的Builder实例,起到ConcreteBuilder与XMLDAO解耦的效果。 2.3 通用XMLDAO类的设计 在这个类里一共有5个方法,它们将完成CRUD。(create、read、add、update、delete) 2.3.1 create方法 /** * 创建单个文档 * @param file 目标输出文档路径 * @param facade elements外观 * @param builderType 获得构建器的类型 */ public void create(String file,Object facade,int builderType){ doc = new Document(); // Construts Element doc.addContent(BuilderFactory.getBuilder(builderType).addBuilder(false,facade)); // Output OutputFactory.newJDOMOutput().outputXML(file,doc); } 在这个方法里用BuilderFactory创建具体的UserInfoBuilder实例。 该模块类图: 此主题相关图片如下:
待续...(3-22-2006) [此贴子已经被作者于2006-3-22 21:05:35编辑过]
|