最近制定了报表引擎数据交互的XML规范,
没时间写总结,先列出下面的参考资料:
http://www.w3school.com.cn/schema/schema_example.asp
http://www.w3.org/TR/xmlschema-0/
http://maven.apache.org/xsd/(很好的规范参考)
http://www.opentravel.org/Specifications/OnlineXmlSchema.aspx(很好的规范参考)
http://blog.csdn.net/zhengyeqing520/article/details/6091656
报表引擎XML规范“TRE标准”的制定和实施
一、“TRE标准”的来源和背景
我们可以看到,很多项目的XML结构采用了“OTA标准”,报表引擎在这一方面借鉴了“OTA标准”,制定了自己的XML标准,我们称之为“TRE标准”。
1、为什么要制定这样一个标准?它有什么作用?
因为报表引擎的一些参数传递采用了XML的结构,XML是一种较好的组装数据的方式,内容丰富,易于扩展,也得到了广泛的认可。
报表引擎支持多个不同的项目,报表引擎是提供服务的一方,应用端需要通过一套规范的流程来调用报表引擎的服务,无论是应用端提交的指令还是数据,都需要遵循一定的格式。我们通过XML来定义这种格式,一是可以验证,二是方便解析。(最原始的方式是自己组装字符串,用%、&等字符来分割,用正则表达式去验证,用split等方法去解析……这种方式很麻烦,不太适合复杂类型的数据)。
标准化的东西,一个是容易使用(比如封装、解析),二是容易判定(是否符合标准),三是便于版本控制,四是体系容易扩展。
2、“TRE标准”是基于什么规范?参考了哪些标准?
1)“TRE标准”是基于W3C的XML规范制定的,除了W3C的XML规范外,业界还有比如微软的OOXML规范、ISO的RELAX NG规范等。
2)“TRE标准”主要参考了“OTA标准”、Spring的XML配置文件标准、Maven的XML配置文件标准以及Apache的一些知名项目的XML配置文件标准,包括它们的数据格式、类型、验证方式、版本定义、命名方式等。下面列举一部分示例:
图1.1 Maven标准XSD的头部信息
图1.2 OTA标准的XSD头部信息
二、制定“TRE标准”的过程
1、选择定义方式
规范XML格式的方法最常见的有两种:一是通过DTD去定义和验证,二是通过XML Schema去定义和验证。DTD的方式较古老,现在用的不是很多(Unfortunately the DTD Syntax was not that powerful. Written in SGML, DTDs are also not as easy to handle as XML.),而Schema要更灵活,功能更强大,使用更广泛。鉴于报表引擎XML数据结构还是比较复杂,需要较好的扩展性,且考虑到Schema使用的广泛性,我们选择了用XML Schema这种方式去定义。
2、选择XML规范(和编程工具)
首先我们想到的就是基于W3C的规范。它是最老的,也是用得最多的规范,基于这个规范,可以很好的定义和验证XML文档。
不过,我们也没有用到十分特殊的语法,所以,对于其他规范,比如RELAX NG,应该也是兼容的。
XML相关的编程工具,有Apaches Xerces,DOM4J,JDOM,MSV(Multi Schema ValidatorMSV),JARV(Java API for RELAX Verifiers)等。经过很多的比较和测试,我们选择了DOM4J作为解析XML的工具,而XML的验证,是基于Java自带的API——JaXP 1.1 TraX(javax.xml.*),相比之下,其效率是最好的。
3、定义便于扩展的XML数据结构
直接编写XML,考虑到各种情况,如果不好统一,也可以弄出几个版本。
4、定义描述和验证XML的Schema文档
在XML的需求上,定义它的Schema文档,主要考虑到格式的验证,数据结构的合理性。
5、编写验证XML的程序
使用定义好的xsd文件去验证xml文档。我这里写了很多个程序去验证xml,包括dom4j、JARV、MSV等等,选择效果最好的那个。
6、定义XML的体系和版本
类似于Maven等定义的版本方式,但是没有采用“OTA”那种复杂的版本定义方式,主要是我认为“OTA”的版本控制过于复杂和冗余。
目前我只制定了ReportAddRQ.xml, Version 1.0.0。我认为报表引擎的XML版本更新不会太频繁,因为每一个版本都对应于不同的解析和验证程序,所以为了简便起见,类似于Version 1.0.0(完整的定义为aa.bb.cc,可以从0.0.0到99.99.99)这种版本号,aa表示XML的体系,例如1就代表ReportAddRQ这个体系(即新增报表请求的xml);中间那位数字bb代表大版本号,用于区分一些不同的应用版本(包括一些个性化的要求);后面的那位数字cc代表小的版本号,即,在原来版本的基础上进行的一些里程碑式的升级更新。
程序在验证和解析XML的时候,只需要判断这个Version便可以立即从API方法里面寻找到合适的解析、验证方法。
7、编写整体XML的传递、验证、解析、封装流程
如题。需要提到的一点是,XML的传送最好采用加密传输的方式,关于加密解密算法,不再本文讨论的访问内。
另外,传递的时候,版本号要单独传递(独立于XML文档之外),以便接收方拿到版本号之后,对xml文档进行验证和解析。
三、“TRE标准”解密
1、头部定义
我们定义了默认的名称命名空间(name space),并在名称命名空间中融入了版本号。
xmlns="http://report.zollty.com/TRE/1.0.0"
第二,我们定义了schema文件的网络Location,以便可以直接通过网络下载此xsd文件,对xml进行验证。
xsi:schemaLocation="http://report.zollty.com/TRE/1.0.0 http://report.zollty.com/schema/ReportAddRQ-1.0.0.xsd"
第三,看看它的命名规范,root元素名叫“RepoortAddRQ”,ReportAdd是功能性描述,RQ即Request,代表这是一个“请求”,同理,如果是RS(即Response),则代表“响应、返回”。其他元素、属性都是以大写开头,“驼峰式”命名法。
第四,xml内容的开始部分,定义了xml的属性(XmlAttr),包含了Version,Token,TimeStamp等基本信息。这一部分的内容针对不同的XML Version,是可以任意改变的。
2、元素Type的定义
我采用了元素和元素的Type分开定义的方式。以XmlAttr为例,如下图:
图3.3 XmlAttr元素的定义
图3.4 XmlAttr元素Type的定义(片段)
这种定义方式的好处在于:使元素的定义更加清晰,而且使得某些类型可以重用。
3、XML的组装、验证和传递
1)组装入口,示例如下:
if(VERSION_1_0_0.equals(version)){
xmlStr = createXmlVer1_0_0(xmlParams,version);
}
就是根据不同的版本,调用不同的方法去组装XML。
2)传递方式,示例如下:
xmlStr = EncryptTools.getEncryptedStr(attrsXml);
originalStr.append("&").append(version);
originalStr.append("&").append(xmlStr);
其中xmlStr加密过,类似于d94b3c5a7f646d8e....这种形式,同时注意到传递了版本号version参数。
3)XML的验证,入口示例如下:
// xml格式校验
InputStream xsdIn = RequestTools.class
.getResourceAsStream("ReportAddRQ-"+version+".xsd");
StreamSource xsdSource = new StreamSource(xsdIn);
XmlSaxErrorHandler errorHandler = XMLUtils.xmlValidator(xmlStr, xsdSource);
if(!errorHandler.isSuccess()){
throw new RuntimeException(errorHandler.getErrorMsg());
}
根据不同的版本号,调用不同的验证规则xsd文件去验证它。
关于这个XMLUtils里面的验证方法,我写了一整套,下面仅列举其中的核心方法:
4、XML的解析和封装
将XML解析出来,封装成JavaBean,我用的是dom4j,仅举一例来说明吧:
/** * 解析任务请求的特定格式的XML * @param xml 待解析的XML字符串,其格式应严格为: * @return 封装好的参数Bean * @author zollty <br>2012-12-17 */ public static ParamsXMLBean xmlParser(String xml){ try{ StringReader sr = new StringReader(xml); SAXReader reader = new SAXReader(); reader.setEncoding("UTF-8"); Document document = reader.read(sr); Element root = document.getRootElement(); List<Element> formAttrsElement = root.element("FormAttrs").elements(); Map<String,FormVO> formAttrs = new LinkedHashMap<String, FormVO>(); FormVO fvo = null; String name = null; for (Element element : formAttrsElement) { name = element.element("Name").getText().trim(); fvo = new FormVO(); fvo.setName(name); fvo.setValue(element.element("Value").getText().trim()); if( null!=element.element("Title") ){ fvo.setTitle(element.element("Title").getText().trim()); }else{ fvo.setTitle(""); } if( null!=element.element("DisName") ){ fvo.setDisName(element.element("DisName").getText().trim()); }else{ fvo.setDisName(""); } formAttrs.put(name, fvo); } List<Element> otherAttrsElement = root.element("OtherAttrs").elements(); Map<String,OthersVO> otherAttrs = new LinkedHashMap<String, OthersVO>(); OthersVO ovo = null; for (Element element : otherAttrsElement) { name = element.element("Name").getText().trim(); ovo = new OthersVO(); ovo.setName(name); ovo.setValue(element.element("Value").getText().trim()); if( null!=element.element("Extra") ){ ovo.setValue(element.element("Extra").getText().trim()); } otherAttrs.put(name,ovo); } ParamsXMLBean paramsXMLBean = new ParamsXMLBean(); paramsXMLBean.setFormAttrs(formAttrs); paramsXMLBean.setOtherAttrs(otherAttrs); return paramsXMLBean; }catch (Exception e) { return null; } }