深入学习XML和XSD、定制XML数据交互规范
2013年04月19日


最近制定了报表引擎数据交互的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标准”是基于W3CXML规范制定的,除了W3CXML规范外,业界还有比如微软的OOXML规范、ISORELAX NG规范等。

2)“TRE标准”主要参考了“OTA标准”、SpringXML配置文件标准、MavenXML配置文件标准以及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 XercesDOM4JJDOMMSVMulti Schema ValidatorMSV),JARVJava API for RELAX Verifiers)等。经过很多的比较和测试,我们选择了DOM4J作为解析XML的工具,而XML的验证,是基于Java自带的API——JaXP 1.1 TraXjavax.xml.*),相比之下,其效率是最好的

 

3、定义便于扩展的XML数据结构

直接编写XML,考虑到各种情况,如果不好统一,也可以弄出几个版本。

 

4、定义描述和验证XMLSchema文档

XML的需求上,定义它的Schema文档,主要考虑到格式的验证数据结构的合理性。


5、编写验证XML的程序 

使用定义好的xsd文件去验证xml文档。这里写了很多个程序去验证xml,包括dom4jJARVMSV等等选择效果最好的那个

 

6、定义XML的体系和版本

类似于Maven等定义的版本方式,但是没有采用“OTA”那种复杂的版本定义方式,主要是我认为“OTA”的版本控制过于复杂和冗余。

目前我只制定了ReportAddRQ.xml, Version 1.0.0。我认为报表引擎的XML版本更新不会太频繁,因为每一个版本都对应于不同的解析和验证程序,所以为了简便起见,类似于Version 1.0.0(完整的定义为aa.bb.cc,可以从0.0.099.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是功能性描述,RQRequest,代表这是一个“请求”,同理,如果是RS(Response),则代表“响应、返回”。其他元素、属性都是以大写开头,“驼峰式”命名法。

第四,xml内容的开始部分,定义了xml的属性(XmlAttr),包含了VersionTokenTimeStamp等基本信息。这一部分的内容针对不同的XML Version,是可以任意改变的。

 

2元素Type的定义

我采用了元素和元素的Type分开定义的方式。以XmlAttr为例,如下图:

3.3  XmlAttr元素的定义

 

 

3.4  XmlAttr元素Type的定义(片段)

这种定义方式的好处在于:使元素的定义更加清晰,而且使得某些类型可以重用。

 

3XML组装、验证和传递

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参数。

3XML的验证,入口示例如下:

// 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里面的验证方法,我写了一整套,下面仅列举其中的核心方法:

 

 

 

 

4XML的解析和封装

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;
    }
}