基于XML模式的数据绑定应用程序接口生成方法转让专利

申请号 : CN200810112277.8

文献号 : CN101339500B

文献日 :

基本信息:

PDF:

法律信息:

相似专利:

发明人 : 李涓子王峰张鹏李军陈德伟唐杰

申请人 : 清华大学

摘要 :

基于xml模式的数据绑定应用程序接口生成方法针对实际中存在的复杂xml schema规范,提出了一种自动生成用于操作符合xml Schema规范的xml文件的数据绑定API的方法。其特征为该方法以数据绑定技术为基础,首先生成一套与生成目标语言类型无关的中间层,然后通过独立的代码生成引擎生成所需类型目标代码。本文基于新华社颁布的CNML Schema(中文新闻置标语言)以及CNML稿件模板进行代码生成和测试,实验证明,这种方法在保证API的健壮性和正确性的基础上,同时具有了极大的灵活性,可以大幅降低schema规范变迁所带来的API维护成本,保持多种语言API接口的基本一致性以降低培训成本,同时也为基于API之上的应用系统提供稳定的支持。

权利要求 :

1.基于XML模式的数据绑定应用程序接口生成方法,其特征在于所述方法是在计算机中依次按以下步骤实现的:步骤1,使用JaxMeXS解析器,对输入到计算机中的可扩展宏语言XML Schema模式做语法解析和逻辑解析,生成SchemaElement和SchemaDataType类型的对象,在其中记录与Schema相关的所有信息,其中至少包括子元素列表,属性列表以及父类型名称:步骤1.1,语法解析:记录所有在Schema中定义的数据类型datatype,存入到syntax_map中,并单独记录abstract属性为true的datatype,存入到abstract_map中;

步骤1.2,逻辑解析:对Schema进行逻辑解析,对于其中定义的元素、数据类型分别生成SchemaElement和SchemaDataType类型的对象,并在其中记录与Schema相关的所有信息,各自形成一个信息详单,数据项中至少含有子结点名、类型以及约束信息;对于所有的element,在所述记录信息中,包含有根据Schema中定义来设置的SchemaElement对象成员变量或成员变量值,以及生成代码所需的所有Schema信息,并把生成的对象添加到_element列表中;

对于所有的数据类型datatype,在所述记录信息中,除了根据schema中的定义设置的成员变量或成员变量值以及生成代码所需的所有schema信息外,当该datatype是通过其他数据类型经过扩展或者约束修饰而来,则要在parent_map中记录两种数据类型之间的继承关系,即被扩展或约束的类型为本类型的父类型,并把生成的对象添加到_datatype列表;所属子元素列表至少包含子元素名称、类型、以及允许出现次数;   步骤2,接口映射,根据SchemaDataType和SchemaElement对象中记录的信息,按以下步骤,通过接口映射生成目标代码中每个类以及类中成员变量的接口信息生成DataTypeObject和NodeObject实体描述对象,以完成从结点的Schema到对该结点进行操作访问的应用程序接口API操作接口之间的映射:步骤2.1,建立一个Schema描述信息到API操作接口的对照表,所述API操作接口包括读、写、管理以及特有操作接口CNML,在所述对照表中,包括以下两种映射关系:基本映射关系,是对元素或属性内容的一些基本操作接口的映射规则,其中包括以下五种关系:对于元素属性数据,提供以下两种API操作接口:

setXXXAttr,设置元素某一属性值,

getXXXAttr,获得元素某一属性值,

对于包含有文本内容的元素,当出现次数maxOccur为1时,提供以下两种操作接口:setText,设置元素的文本内容,

getText,获取元素的文本内容,

对于包含子元素的元素或者数据类型,当出现minOccur和maxOccur都为1时,提供以下几种操作接口:addXXX,添加XXX子元素,

getXXX,获得XXX子元素,

removeXXX,删除XXX子元素,

当子元素包含文字结点时,提供以下操作接口:

setXXXText,设置当前元素下XXX子元素中包含的文本内容,getXXXText,获取当前元素下XXX子元素中包含的文本内容,对于包含的子元素中出现maxOccur大于1时,提供以下几种操作接口:addXXX,添加XXX子元素,

getXXXint index,获得由索引index所指示的XXX子元素,int是Java内置整型数据类型,removeXXXint index,删除由索引index所知是的XXX子元素,getXXXTextint index,获得由索引index所指示的XXX子元素文本内容,setXXXTextint index,设置由索引index所指示的XXX子元素文本内容,getXXXList,获得当前元素下所有XXX子元素的列表,

getXXXTextList,获得当前元素下所有XXX子元素文本内容的列表,特有映射针对所述XML特有操作接口,以一种附加的形式连同基本操作接口同时给出,包括以下四种:XPath数据访问接口:为所有的属性、元素和类型定义添加getNodeByXPath,getNodesByXPath以及getValueByXPath特有操作接口,其中:getNodeByXPath为根据指定的XPath获得相应结点对象,getNodesByXPath为根据指定的XPath获得相应结点对象列表,getValueByXPath为根据指定的XPath获得其对应的第一个结点对象的文本内容,特殊类型数据设置与访问接口,针对元素文本内容可能为HTML格式数据内容或CDATA格式数据内容,为所有元素添加以下接口:setCDATAText:为当前元素设置CDATA格式的文本内容,hasCDATAText:判断当前元素是否含有CDATA格式的文本内容,asXML:获取当前元素内部所有元素、属性等的XML文本,并包含当前元素的标签,getXML:获取当前元素内部所有元素、属性等的XML文本,不包含当前元素的标签,getXMLText:获取当前元素内部所有元素、属性等的XML文本,并去掉其间所有的XML标签,对于Schema中通过正则表达式对其文本内容进行格式约束的元素,添加validateType方法,用于检验所当前元素所包含的文本内容是否符合类型定义中正则表达式的限制;

对于有多种子类型的数据类型定义,提供addXXXofYYYType操作接口,用一个YYY的实例来添加为当前元素的XXX子元素;

步骤2.2,把步骤2.1中完成接口映射生成的接口对象保存成XML中间配置文件,供用户修改该中间配置文件,实现生成目标代码的个性化:步骤2.2.1,依次从_datatype列表中取出SchemaDataType对象,依据该对象中的成员变量值以及预先如上定义的接口映射规则为其生成一个DataTypeObject类对象,在该对象的列表中加入为每个操作接口生成的NodeMethod对象,把生成的DataTypeObject加入到data_cache_中,所述的NodeMethod对象是依据上述映射规则生成的一个用来对datatype所定义的XML元素进行操作的函数接口描述,其中会有以下数据项:name:用来记录所需生成函数的函数名称,

isAttributeMethod:用来记录是否是对某个元素属性进行操作的函数,MethodReturn:用来记录所需生成函数,

Arguments:记录生成函数的参数信息,

MethodAction:记录生成函数的函数体实际所需提供的操作信息,步骤2.2.2,依据步骤1.2中生成的parent_map中的继承关系,为相应的两个datatype所对应的DataTypeObject对象加入依赖关系;

步骤2.2.3,依次从_element列表中取出SchemaElement对象,依据该对象中的成员变量值为其生成一个NodeObject类,并在NodeObject类中相应列表中加入NodeMethod对象,用以记录需要生成的成员函数接口描述,并把生成的NodeObject加入到cache_中;

步骤2.2.4,把所得到的cache_和data_cache_中的对象保存成XML中间配置文件,作为中间代码备用,供用户修改,实现API操作接口的定制;

步骤3,从中间代码读入步骤2.2.4所述经过用户修改的XML中间配置文件,然后在内存中生成DataTypeObject和NodeObject的对象,分别相应的保存到data_cache_和cache_中;

步骤4,针对不同的程序设计语言,使用代码生成程序,生成API源代码:步骤4.1,对于cache_中的每一个对象,利用相应的程序设计语言代码生成程序,生成用于操作XML文件中element的interface以及实现类,所述的实现类是指用于实现该类的对应的接口中定义的接口interface中定义的接口函数,在每个具体的实现类中要把该类对应的XML元素在dom树上的dom结点对象作为私有变量封装在该类中,类中对于该元素的操作要调用该dom节点中相应的函数来实现;

步骤4.2,对于data_cache_中的每一个对象,用所述步骤4.1的方法来生成用于操作XML文件中datatype所定义的元素的接口以及实现类;但是,若该datatype在schema中定义为abstract,则生成的接口也设置为abstract,若该datatype是从其他数据类型约束或扩展而来,则为该接口添加与父接口的继承关系;

步骤4.3,把步骤4.1或步骤4.2生成的代码写出到文件,生成源代码文件;

步骤5,添加顶层工厂类、编译并打包所生成的代码文件,以封装成一个二进制包作为发行包以供用户使用:步骤5.1,在所述API顶层添加APIFactory类,为用户提供统一的操作入口;

步骤5.2,使用开源工具Ant提供的API,调用API完成包括代码文件的编译,打包以及说明文档的生成。

说明书 :

技术领域

本发明属于xml数据绑定或代码自动生成领域。

背景技术

数据绑定技术提供了一种简单而直接的方法,使得可以在Java、C++等平台应用程序中高效的使用XML、Database等各种非结构化的数据源。通过数据绑定,应用程序可以在一定程度上忽略XML文件的实际结构和数据库中存储表的结构等因素的制约,而直接使用通过绑定的代码来访问数据内容。在读写文件时,一些特殊类型的数据(譬如数字和日期)可以被转换成程序语言中存在的类型形式,而不是保留为文本形式,这使应用程序可以更方便、有效地使用数据本身,而不用过多的关心其所采取的存储和表示方式。
目前市场上有很多技术产品都实现了对于XML数据绑定的支持。但是它们各自所能支持的Schema特性以及生成的目标代码种类上有十分巨大的差别。当需要进行数据绑定的Schema足够的简单和短小时,很多开源技术就可以满足要求。但是当需要进行数据绑定的Schema文档包含复杂特性(如extensions、restrictions、substitution groups等)的时候,现有的大多数开源技术就无法进行完全的支持。由Apache基金所支持的XMLBean项目而言,虽然它可以支持大多数复杂的Schema结构,但是在利用XMLBean进行数据绑定时,目前还没有有效的方法来达到对生成API方法的增加和修改等操作,这导致当用户需要生成高度可定制的API时,XMLBean也无法胜任;另外XMLBean只支持生成Java平台下的API,这就使得很多基于其它平台的应用程序无法直接使用。在C++平台下这些问题表现更为突出。
针对这些问题以及实际存在的复杂XMLschema规范,本发明提出了一种自动生成用于操作符合XML Schema规范的XML文件的数据绑定API的方法。该方法以数据绑定技术为基础,首先生成一套与生成目标语言类型无关的中间配置层,然后通过附加独立的目标代码生成引擎生成所需类型目标代码。在进行数据绑定时,通过如下方式进行:采用以Dom结构存储数据,在生成代码的类中封装Dom结点的私有成员变量指针(或引用),并在类的方法中通过Dom指针(或引用)完成对数据的操作;在最顶层添加API顶层类,并实现save和load方法用以对XML文件的进行存储操作,完成对象与XML文本的转换。

发明内容

本发明的目的在于提供一种依据复杂XML schema模式,生成操作符合该schema模式的XML文件的数据绑定应用程序接口方法。
本发明的特征在于:
步骤1,使用JaxMeXS解析器,对输入到计算机中的可扩展宏语言XML Schema模式做语法解析和逻辑解析,生成SchemaElement和SchemaDataType类型的对象,在其中记录与Schema相关的所有信息,其中至少包括子元素列表,属性列表以及父类型名称:
步骤1.1,语法解析:记录所有在Schema中定义的数据类型datatype,存入到syntax_map中,并单独记录abstract属性为true的datatype,存入到abstract_map中;
步骤1.2,逻辑解析:对Schema进行逻辑解析,对于其中定义的元素、数据类型分别生成SchemaElement和SchemaDataType类型的对象,并在其中记录与Schema相关的所有信息,各自形成一个信息详单,数据项中含有子结点名、类型以及约束信息;
对于所有的element,在所述记录信息中,包含有根据Schema中定义来设置的SchemaElement对象成员变量或成员变量值,以及生成代码所需的所有Schema信息,并把生成的对象添加到_element列表中;
对于所有的数据类型datatype,在所述记录信息中,除了根据schema中的定义设置的成员变量或成员变量值以及生成代码所需的所有schema信息外,当该datatype是通过其他数据类型经过扩展或者约束修饰而来,则要在parent_map中记录两种数据类型之间的继承关系,即被扩展或约束的类型为本类型的父类型,并把生成的对象添加到_datatype列表;所属子元素列表至少包含子元素名称、类型、以及允许出现次数;
步骤2,接口映射,根据SchemaDataType和SchemaElement对象中记录的信息,按以下步骤,通过接口映射生成目标代码中每个类以及类中成员变量的接口信息生成DataTypeObject和NodeObject实体描述对象,以完成从结点的Schema到对该结点进行操作访问的应用程序接口API操作接口之间的映射:
步骤2.1,建立一个Schema描述信息到API操作接口的对照表,所述API操作接口包括读、写、管理以及特有操作接口CNML,在所述对照表中,包括以下两种映射关系:
基本映射关系,是对元素或属性内容的一些基本操作接口的映射规则,其中包括以下五种关系:
对于元素属性数据,提供以下两种API操作接口:
setXXXAttr,设置元素某一属性值,
getXXXAttr,获得元素某一属性值,
对于包含有文本内容的元素,当出现次数maxOccur为1时,提供以下两种操作接口:
setText,设置元素的文本内容,
getText,获取元素的文本内容,
对于包含子元素的元素或者数据类型,当出现minOccur和maxOccur都为1时,提供以下几种操作接口:
addXXX,添加XXX子元素,
getXXX,获得XXX子元素,
removeXXX,删除XXX子元素,
当子元素包含文字结点时,提供以下操作接口:
setXXXText,设置当前元素下XXX子元素中包含的文本内容,
getXXXText,获取当前元素下XXX子元素中包含的文本内容,
对于包含的子元素中出现maxOccur大于1时,提供以下几种操作接口:
addXXX,添加XXX子元素,
getXXXint index,获得由索引index所指示的XXX子元素,int是Java内置整型数据类型,
removeXXXint index,删除由索引index所知是的XXX子元素,
getXXXTextint index,获得由索引index所指示的XXX子元素文本内容,
setXXXTextint index,设置由索引index所指示的XXX子元素文本内容,
getXXXList,获得当前元素下所有XXX子元素的列表,
getXXXTextList,获得当前元素下所有XXX子元素文本内容的列表,
特有映射针对所述XML特有操作接口,以一种附加的形式连同基本操作接口同时给出,包括以下四种:
XPath数据访问接口:为所有的属性、元素和类型定义添加getNodeByXPath,getNodesByXPath以及getValueByXPath特有操作接口,其中:
getNodeByXPath为根据指定的XPath获得相应结点对象,
getNodesByXPath为根据指定的XPath获得相应结点对象列表,
getValueByXPath为根据指定的XPath获得其对应的第一个结点对象的文本内容,
特殊类型数据设置与访问接口,针对元素文本内容可能为HTML格式数据内容或CDATA格式数据内容,为所有元素添加以下接口:
setCDATAText:为当前元素设置CDATA格式的文本内容,
hasCDATAText:判断当前元素是否含有CDATA格式的文本内容,
asXML:获取当前元素内部所有元素、属性等的XML文本,并包含当前元素的标签,
getXML:获取当前元素内部所有元素、属性等的XML文本,不包含当前元素的标签,
getXMLText:获取当前元素内部所有元素、属性等的XML文本,并去掉其间所有的XML标签,
对于Schema中通过正则表达式对其文本内容进行格式约束的元素,添加validateType方法,用于检验所当前元素所包含的文本内容是否符合类型定义中正则表达式的限制;
对于有多种子类型的数据类型定义,提供addXXXofYYYType操作接口,用一个YYY的实例来添加为当前元素的XXX子元素;
步骤2.2,把步骤2.1中完成接口映射生成的接口对象保存成XML中间配置文件,供用户修改该中间配置文件,实现生成目标代码的个性化:
步骤2.2.1,依次从_datatype列表中取出SchemaDataType对象,依据该对象中的成员变量值以及预先如上定义的接口映射规则为其生成一个DataTypeObject类对象,在该对象的列表中加入为每个操作接口生成的NodeMethod对象,把生成的DataTypeObject加入到data_cache_中,所述的NodeMethod对象是依据上述映射规则生成的一个用来对datatype所定义的XML元素进行操作的函数接口描述,其中会有以下数据项:
name:用来记录所需生成函数的函数名称,
isAttributeMethod:用来记录是否是对某个元素属性进行操作的函数,
MethodReturn:用来记录所需生成函数,
Arguments:记录生成函数的参数信息,
MethodAction:记录生成函数的函数体实际所需提供的操作信息,
步骤2.2.2,依据步骤1.2中生成的parent_map中的继承关系,为相应的两个datatype所对应的DataTypeObject对象加入依赖关系;
步骤2.2.3,依次从_element列表中取出SchemaElement对象,依据该对象中的成员变量值为其生成一个NodeObject类,并在NodeObject类中相应列表中加入NodeMethod对象,用以记录需要生成的成员函数接口描述,并把生成的NodeObject加入到cache_中;
步骤2.2.4,把所得到的cache_和data_cache_中的对象保存成XML中间配置文件,作为中间代码备用,供用户修改,实现API操作接口的定制;
步骤3,从中间代码读入步骤2.2.4所述经过用户修改的XML中间配置文件,然后在内存中生成DataTypeObject和NodeObject的对象,分别相应的保存到data_cache_和cache_中;
步骤4,针对不同的程序设计语言,使用代码生成程序,生成API源代码:
步骤4.1,对于cache_中的每一个对象,利用相应的程序设计语言代码生成程序,生成用于操作XML文件中element的interface以及实现类,所述的实现类是指用于实现该类的对应的接口中定义的接口interface中定义的接口函数,在每个具体的实现类中要把该类对应的XML元素在dom树上的dom结点对象作为私有变量封装在该类中,类中对于该元素的操作要调用该dom节点中相应的函数来实现;
步骤4.2,对于data_cache_中的每一个对象,用所述步骤4.1的方法来生成用于操作XML文件中datatype所定义的元素的接口以及实现类;但是,若该datatype在schema中定义为abstract,则生成的接口也设置为abstract,若该datatype是从其他数据类型约束或扩展而来,则为该接口添加与父接口的继承关系;
步骤4.3,把步骤4.1或步骤4.2生成的代码写出到文件,生成源代码文件;
步骤5,添加顶层工厂类、编译并打包所生成的代码文件,以封装成一个二进制包作为发行包以供用户使用:
步骤5.1,在所述API顶层添加APIFactory类,为用户提供统一的操作入口;
步骤5.2,使用开源工具Ant提供的API,调用API完成包括代码文件的编译,打包以及说明文档的生成。
申请所采用的自动生成XML数据绑定API的方法已经应用于CNML中文新闻置标语言标准管理系统中。在实际的测试中,自动生成的API具有健壮、稳定等特点,同时其执行效率也满足要求。CNML标准几次简单改动,使用这种方法实现的系统几乎可以不用更改任何代码而重新生成符合要求的API,具有很大的灵活性。

附图说明

图1.生成配置文件流程图。
图2.生成目标代码流程图。
图3.XML API生成系统设计图。
图4.Schema源信息与操作接口映射关系图。

具体实施方式

本发明的目的在于提供一种用于自动生成依据复杂XML schema生成操作符合该schema的XML文件的数据绑定API方法。
本发明所提出的方法的思路在于:首先通过对复杂XML schema的解析,取得其中关于XML元素(element)和数据类型(datatype)的定义,以及数据类型之间诸如约束、扩充等复杂关系;然后依据所得到的schema信息,生成API接口的中间配置文件;接着,对于不同的API目标代码类型,附加上不同的代码生成引擎,依据API接口配置文件生成最终的API源代码。最后,通过API封装引擎对生成原始代码进行封装和发布的处理,主要包括增加基于Factory设计模式的API生成接口,增加测试用例和一些说明文档等内容。最终编译生成各种形式的标准API发布二进制文件包。
所述方法是基于一个已经存在的XML schema文档次按以下步骤具体实现的,所采用的方法流程图请见图1和图2。
一种依据XML schema生成操作符合该schema的XML文件的数据绑定API方法,其特征在于所述方法依次会有以下步骤:
步骤(1)使用JaxMeXS解析器提供的语法解析和逻辑解析功能,共同完成对复杂XML schema的解析,通过解析对于Schema中定义的元素、数据类型分别生成SchemaElement和SchemaDataType类型的对象,并在其中记录与Schema相关的所有信息,如包含子元素名称、类型和允许出现次数等信息的子元素列表,属性列表,父类型名称:
步骤(1.1)调用语法解析功能,对输入的schema进行解析,并把解析所得的所有类型存入syntax_map,同时把抽象类型放到abstract_map中,留为后续步骤备用。
步骤(1.2)对输入的schema进行逻辑解析,得到schema中所有的datatype,为每个datatype建立SchemaDataType类的对象,并依据schema中的定义添加和设置对象的成员变量或成员变量值,在该对象中,把生成代码所需的所有schema信息记录下来。包括子元素的名称,子元素的类型和允许出现次数等信息的子元素列表和属性列表。如果该datatype是通过其他数据类型经过扩展(extension)或者约束(restriction)修饰而来,则需要在parent_map中记录两种数据类型之间的继承关系,即被扩展或约束的类型为本类型的父类型。最后把生成的对象添加到_datatype列表中备用。
步骤(1.3)从schema逻辑解析结果中得到schema中所有的element,为每个element建立SchemaElement类的对象,与(1.2)类似,依据schema中的定义添加和设置对象的成员变量或成员变量值,在该对象中,把生成代码所需的所有schema信息记录下来,最后把生成的对象添加到_element列表中备用。
步骤(2)接口映射步骤(见图4):对从步骤(1)中得到的SchemaDataType和SchemaElement对象,通过进行接口映射,分别生成DataTypeObject和NodeObject实体描述对象。按照SchemaDataType和SchemaElement对象中包含的Schema信息的特点以及需要对其进行的操作,添加与生成代码相对应的记录。在接口映射步骤中将完成从基本的Schema结点描述信息到对其进行操作访问的API接口之间的映射。
步骤(2.1)依次从_datatype列表中取出SchemaDataType对象,依据该对象中的成员变量值以及预先定义的接口映射规则为其生成一个DataTypeObject类对象,并该对象中相应列表中加入NodeMethod对象,每个NodeMethod对象都是一个用来对该datatype定义的XML element进行操作的函数(方法)的接口的描述。把生成的DataTypeObject加入到data_cache_中。
步骤(2.2)依据(1.2)中生成的parent_map中的继承关系,为相应的两个datatype对应的DataTypeObject对象加入依赖关系,从而在生成代码时可以为这些类正确加入继承关系。
步骤(2.3)依次从_element列表中取出SchemaElement对象,依据该对象中的成员变量值为其生成一个NodeObject类,并在NodeObject类中相应列表中加入NodeMethod对象,用以记录需要生成的成员函数接口描述。把生成的NodeObject加入到cache_中。
步骤(2.4)把cache_和data_cache_中对象保存成XML形式的配置文件作为中间代码备用,用户可以根据自己需求对中间代码进行修改,从而达到定制API的目的。
步骤(3)从XML中间代码读入中间层配置文件,并在内存中生成DataTypeObject和NodeObject的对象分别保存到cache_和data_cache_中。这一步可以看作是步骤(2.4)的逆步骤。
步骤(4)对于cache_和data_cache_中的所有对象,依照其私有成员变量规定的程序接口生成目标源代码。以java代码生成为例,其步骤如下:
步骤(4.1)对于cache_中的每一个对象,利用JaxME Java Source提供的API生成用于操作XML文件中element的interface以及实现类。
步骤(4.1.1)设置interface的访问级别为public并在其中加入方法的声明;添加对于该interface的注释信息用于将来生成javadoc文件;对于取值定义为枚举类型具有枚举类型属性(attribute)的element,在interface中定义与枚举类型相对应的公有静态成员变量以及相关注释信息;在interface中声明用于操作element的所有方法原型,并添加关于方法的注释。
步骤(4.1.2)生成类实现步骤(4.1.1)中的interface,在该类中,封装私有element对应在dom树上的dom节点对象,并利用该对象实现interface中声明的所有函数功能。
步骤(4.1.3)把生成的代码写出到文件,生成源代码文件
步骤(4.2)对于data_cache_中的每一个对象,利用JaxME Java Source提供的API生成与XML中datatype对应的interface以及实现类。
步骤(4.2.1)设置interface的访问级别为public并在其中加入方法的声明;添加对于该interface得注释信息用于将来生成javadoc文件;对于取值定义为枚举类型具有枚举类型属性(attribute)的datatype,在interface中定义与枚举类型相对应的公有静态成员变量以及相关注释信息;在interface中声明用于操作类型为该datatype的element所有方法原型,并添加关于方法的注释。如果该datatype在schema中定义为abstract,生成的interface也设置为abstract。如果该datatype是从其他数据类型约束(restriction)或扩展(extension)而来,则为该interface添加与父interface的继承关系。
步骤(4.2.2)生成类实现步骤(4.1.1)中的interface,在该类中,封装私有element对应在dom树上的dom节点对象,并利用该对象实现interface中声明的所有函数功能。
步骤(4.2.3)把生成的代码写出到文件,生成源代码文件
步骤(5)添加顶层工厂类、编译、打包在Java代码生成完成后,原始代码并不能作为最终的代码为开发人员所用,必须添加一部分手写的顶层工厂类等手工代码后进行编译,最终封装成一个JAR二进制包作为发行包提供给开发人员使用。
步骤(5.1)在API顶层添加APIFactory类中,实现new,parse和save等方法,以提供新建稿件,从指定XML稿件读入稿件并解析为API中提供的java对象,以及把修改后的java对象保存为XML稿件等功能。在进行解析时,通过调用第三方(dom4j)的XML解析器,并把处在XML最顶层的元素所对应的dom结点作为私有变量封装在API中为顶层元素生成的对象中。
步骤(5.2)使用开源工具Ant提供的API,来调用Ant完成源代码的编译,打包及其javadoc等文档的生成。最终得到提供给开发人员使用的是二进制的API Jar包以及javadoc说明文档。
这样的技术方案,其最大的好处有以下四点:
(1).使用中间配置文件弱化了生成的API目标代码与原始XML Schema规范之间的紧密耦合,从而使得标准维护人员在原始设计允许的范围之内,通过手工修改中间配置文件来修改最终生成代码的业务接口和业务逻辑。
(2).中间配置文件为多语种的标准API生成提供了条件,中间配置文件中规定了生成代码的接口和功能,从而使得多语种的API有了公共的基础,从而可以统一多语种API的函数接口,降低API的学习成本。
(3).在类中封装Dom结点的方法进行数据操作,可以直接使用目前现成的成熟的XMLDOM解析器,而无需自己实现对XML稿件的Dom解析,既减少了工作量,而且对于成熟代码的复用也会降低系统出错的概率。
(4).使用在java类中封装Dom结点的方法,可以把对结点的操作与数据在物理上分开,又保持了其在逻辑上的依附性。同时,也解决了许多Schema结构在java中没有对应数据结构的问题。
本文所采用的自动生成XML数据绑定API的方法已经应用于CNML中文新闻置标语言标准管理系统中。在实际的测试中,自动生成的API具有健壮、稳定等特点,同时其执行效率也满足要求。CNML标准几次简单改动,使用这种方法实现的系统几乎可以不用更改任何代码而重新生成符合要求的API,具有很大的灵活性。
该方法包括如下步骤:
(1)对XML schema进行语法解析和逻辑解析
(1.1)语法解析,记录所有在schema中定义的datatype,并单独记录abstract属性为true的datatype。
(1.2)对schema进行逻辑解析,通过解析对于Schema中定义的元素、数据类型分别生成SchemaElement和SchemaDataType类型的对象,并在其中记录与Schema相关的所有信息。
具体记录的信息见表1和表2
表1.SchemaElement中记录Schema信息详单
  私有变量名   类型   记录信息   _attributes   List   记录element中所有属性信息,包括属性名、类型、  是否可选、注释,允许出现次数、是否内置类型等。   私有变量名   类型  记录信息   _elements   List  记录element中所有子element的信息,记录的具 体信息和本表格一致。   _enumerations   List  记录element中枚举类型的信息。包括名称、取值、 注释等信息。   _root   ElementParticle  记录element下小品词(Particle)的信息,包括类 型(root、all、choice、sequence等)和所有子 element的Particle信息。   _elementTypeInfos   Map  记录子元素出现次数信息,包括子元素名 (String),以及ElementTypeInfo中的子元素最多 和最少出现次数。   hasText   boolean  元素中是否包含text文本信息   isAnyType   boolean  元素是否被定义为可以是任何类型   isBasicType   boolean  元素是否是基本类型   hasEnumeration   boolean  元素是否含有枚举信息   minOccur   int  元素允许重复出现最少次数   maxOccur   int  元素允许重复出现最多次数   TypeNameList   List  元素允许类型的名称
表2.SchemaDataType中记录Schema信息详单
  私有变量名   类型   记录信息   TypeName   String   datatype类型名称   Comment   String   datatype的注释信息   hasEnumeration   boolean   是否含有枚举信息   enumerations   List   记录datatype中枚举类型的信息。包括名称、取值、  注释等信息。
  私有变量名   类型   记录信息   isSimpleType   boolean   datatype是否是简单类型   isExtended   boolean   datatype是否是从其他类型扩展(extension)而来   extendedType   String   所扩展类型的类型名   isChoice   boolean   datatype子元素是否是可选的   私有变量名   类型   记录信息   isRestricted   boolean   datatype是否是从其他类型约束(restriction)而来   restrictedType   String   所约束类型的类型名   isAbstract   boolean   datatype是否是抽象类型   isParentAbstract   boolean   datatype的父类型是否是抽象类型   isBuiltinType   boolean   是否是W3C定义的内置类型   typeList   List   对于简单类型,如果是联合(Union)类型的话,记录所  有允许类型的名称   attributeList   List   记录datatype中所有属性信息,包括属性名、类型、  是否可选、注释,允许出现次数、是否内置类型等   elementList   List   记录datatype中所有子元素的信息,其具体信息与表  1.1一致   restricted_pattern   String   如果是受pattern(模式)约束的,记录约束的模式   _elementTypeInfos   Map   记录子元素出现次数信息,包括子元素名(String),以  及ElementTypeInfo中的子元素最多和最少出现次数。
(2)接口映射。根据SchemaDataType和SchemaElement对象中记录的信息,通过接口映射,生成目标代码中每个类以及类中成员函数的接口信息,从而生成DataTypeObject和NodeObject实体描述对象。
这一步是生成默认中间代码层的关键步骤,如果不对中间配置文件进行修改的话,这一步将决定生成的目标代码中各个element和datatype所对应的类以及类中静态成员变量的变量名、取值,以及各个成员函数的函数名,函数原型以及函数所进行的功能等。总而言之,在接口映射步骤中将完成从基本的Schema节点描述信息到对其进行操作访问的API接口之间的映射。
(2.1)映射规则
考虑到这两种映射对象之间的相似性,由于SchemaDataType会涉及更为复杂的接口映射关系。对于SchemaElement的映射处理可以看作是其一个子集。所以表3给出了Schema源信息和函数接口之间的对照。
表3.Schema源信息到映射目标对照


在给出如上的映射元和映射目标之后,还需要定义一套结点描述与接口之间的映射关系。
(2.1.1)基本映射
这里以对于元素或属性内容的一些基本操作接口的映射规则为例,其具体描述如下:
(a).对于元素属性数据,提供setXXXAttr和getXXXAttr方法。
(b).对于包含文字结点的元素,如果maxOccur都为1,提供setText、getText方法。
(c).对于包含子元素的元素或者数据类型,如果子元素的minOccur和maxOccur都为1,则提供addXXX、getXXX、removeXXX等方法,如果子元素包含文字结点,则还要提供setXXXText、getXXXText等方法。如果子元素的maxOccur为大于1,则除了添加上述方法外,还要依情况添加getXXXList、getXXXTextList等方法。
(2.2.2)特有映射
在映射过程中,针对XML特有操作,处理过程中包含一些对于特殊操作接口的使用,所以需要为标准API系统添加一些额外的操作接口映射关系。它们一般都是以一种附加的形式连同其他操作接口一同给出的:
(a).XPath数据访问接口:为所有的属性、元素和类型定义添加getNodeByXPath和getValueByXPath方法。以实现在具体业务需求中对于数据信息方便快速的访问要求。
(b).特殊类型数据设置与访问接口:为所有的元素添加setCDATAText、hasCDATA、asXML、setXML、getXML、getXMLText方法。这是考虑到具体业务需求中可能包含很多对于HTML格式数据内容和CDATA格式数据内容的操作,通过增加这些直接数据访问接口可以简化在实际使用过程中的处理过程。
(c).为包含有通过正规表达式对其格式进行约束的元素,添加validateType方法,用于检查该元素是否符合Schema中有正则表达式所约束的类型。
(d).对于有多种子类型的数据类型定义,由于在添加过程中需要指定以某种子类型作为添加数据。提供addXXXOfYYYType方法
(2.2)把完成接口映射生成的接口对象保存成XML中间配置文件,用户可以按照自己的需求相应修改中间配置文件,从而达到对生成目标代码的个性化。
在配置文件中,记录NodeObject和DataTypeObject对象中的所有私有成员变量及其值。配置文件是以XML文件的形式记录的。在中间配置XML文件中,除了对应于schema中元素和类型的定义中所定义的属性、子元素列表以及必要的描述信息的记录外,最重要的是对生成函数接口的记录。表4给出了对于每个函数进行记录的XML结点定义。
表4.NodeMethod元素中子元素与属性介绍
  类别   名称   父结点名  说明   属性   name   NodeMethod  记录函数的函数名称   属性   isAttributeMethod   NodeMethod  是否是对元素属性操作的函数   元素   MethodReturn   NodeMethod  用来记录函数的返回类型   元素   Arguments   NodeMethod  在其内包含可重复出现多次的Arg子元素,Arg子元素包括 name、type属性以及Comment子元素,在其中记录函数参数 的名称,类型以及对于函数参数的注释   元素   MethodAction   NodeMethod   记录函数体生成时需要所有信息   属性   pattern   MethodAction   生成函数的模式的模式名,在我们方法中把所有支持的函数  分成一些不同的生成模式。   属性   whenError   MethodAction   当函数体中出错时,处理方案,如return(返回)或  throwException(抛出错误)等   元素   ElementName   MethodAction   子元素名称   元素   XPath   MethodAction   该子节点相对于父节点的相对XPath路径   元素   RestrictedPattern   MethodAction   对节点文本值进行约束的模式   元素   ElementTypeName   MethodAction   节点类型名称
(3)读入修改后的配置文件
这一步骤中,利用dom4j提供的XML解析器,读入配置文件,并根据配置文件在内存中生成分别为datatype和element生成DataTypeObject和NodeObject对象
在用户不需要修改默认配置文件的情况下,这一步骤可以省略,因为内存中存在的DataTypeObject和NodeObject对象其实和从配置文件中生成的对象是等同的。这一步骤的好处不但为用户定制API接口提供了基础,而且也是实现多语言API生成的前提条件。对于多语言API的生成,不同语言在此前的步骤是完全一样的;从这一步骤开始起,针对不同的程序设计语言,添加不同的代码生成程序(引擎),就可以生成接口一致的多种语言版本的API。
(4)使用代码生成引擎,生成API源代码。
依照配置文件,为操作element和datatype生成相应的接口及其实现类。在生成的接口中,主要定义用于操作该节点对象的所有方法声明(方法原型)以及类、方法、方法参数等的注释,注释用于生成用户手册。
实现类用于实现该类对应的接口,主要用来实现接口中定义的函数,封装必要的数据结构等。在每个具体的实现类中,不封装具体的数据,而只是把该类对应的XML元素在dom树上的dom节点对象作为私有变量封装在类中。类中对于该元素的所有操作都是调用 该dom节点中相应的函数来实现的。以下给出了几种典型的函数具体的实现方法。
(4.1)get、set文本信息的函数
这一类函数包括获得属性值,元素文本或者子元素文本值等。在这一类方法中,首先,以该方法对象中NodeMethod记录的XPath为输入,通过调用封装在类中的dom节点的selectSingleNode函数得到相应的节点dom对象。然后,通过调用该节点对象的getText方法得到需要获得的文本值。最后,把获得的文本值作为函数返回值返回。
(4.2)get元素对象的函数
这一类的函数包括获得子元素对象,所有子元素列表等方法。在这类方法中,首先,同样通过XPath值得到相应的节点dom对象。然后,调用在API中为该元素定义的类的构造函数,构建一个新的对象,并把该dom对象赋值给新构建对象的domNode私有变量域。最后,把该对象以接口对象的形式作为函数返回值返回。如果是返回多个对象的列表,则新建一个列表对象,把新生成的元素对象加入到列表中,最终把列表对象返回。
(4.3)remove函数
这一类函数主要包括删除属性、子元素或者指定的重复出现的子元素中的某个等。同样通过XPath值得到相应的节点dom对象。如果该dom对象不为空,则通过该dom对象调用detach方法将该对象从dom树上摘下。
其他的方法基本都是在这三类方法的基础上,在源代码中附加具体的处理逻辑进行处理,然后返回相应的结果。例如,setContentCDATA等方法中,需要加入CDATA节点,直接使用添加text的方法就会出现问题。就需要使用到dom4j提供的DocumentHelper类中提供的createCDATA方法先创建一个CDATA类型节点对象,然后通过调用domNode中的addContent方法把生成的CDATA节点添加进去。
(5)添加顶层工厂类、编译、打包生成的代码
(5.1)添加顶层工厂类
通过如上步骤所生成的API目标代码,并不能直接提供给终端用户所使用。其原因在于生成的API代码中在创建最顶层元素对象时会涉及对于dom4j中domNode节点信息的获取和设置等工作,这对于一般的应用程序员而言将是十分麻烦的事情,也容易产生由于程序员个人失误而导致API不能正常工作情况的发生。另外通过所生成的API目标代码并不能完成存储当前XML文档内容或新建一个XML文档等操作要求。所以需要额外的添加操作代码来实现这些功能。考虑到设计模式中Factory应用模式的相关特性,将通过为用户提供一个简介的Factory类,并为用户提供如下统一的API操作接口的解决方法:
parse:利用dom4j中的SAXReader解析一个由文件路径字符串所指定外部XML稿件。并返回一个表示这个XML外部稿件的顶层数据对象。这主要通过完成对domNode节点信息的设置。
save:将当前代表XML文件内容的数据对象中的所有信息,保存到一个指定的外部XML文件。完成对于一个XML文件的存储工作。
new:返回一个新的XML文件对象,并只为其设置单一的顶层元素标签内容。用于用户作后期的扩展和对内容的添加。
这样终端用户在使用过程中只要通过Factory类所提供的getInstance方法就可以得到如上所叙述的一个标准Java API操作接口对象。
(5.2)编译、打包
作为发布的API代码,不能直接以源码形式提供给最终用户,而需要提供给用户编译完成后的二进制代码包,以方便用户使用。编译和打包的工作,这里使用Apache基金开源项目Ant所提供的API,来调用Ant完成代码的编译,打包以及javadoc等说明文档的生成。最终提供给开发人员使用的是二进制的API Jar包以及javadoc说明文档。
表5.符号类型及其用途一览表
  名称   类型   用途   SchemaElement   自定义Java类   用于保存Schema中关于元素定义的相关信息   SchemaDataType   自定义Java类   用于保存Schema中关于数据类型的相关信息   syntax_map   HashMap   用于保存语义解析结果,键为类型名,值为类型语义解  析所得对象   abstract_map   HashSet   用于保存语义解析过程中所得的abstract属性为真的  数据类型名   _datatype   List   用于保存逻辑解析过程中生成的所有SchemaDataType  对象   _element   List   用于保存逻辑解析过程中生成的所有SchemaElement对  象   parent_map   HashMap   用于保存逻辑解析过程中得到的类型之间继承关系,键  为子类型名,值为父类姓名   DataTypeObject   自定义Java类   用于保存为DataType生成的操作接口对象   NodeObject   自定义Java类   用于保存为Element生成的操作接口对象   NodeMethod   自定义Java类   用于保存接口对象生成代码所需要的相关信息   cache_   List   用于保存接口映射后得到的DataElement对象   data_cache_   List   用于保存接口映射后得到的DataTypeObject对象
利用上述步骤(1)-(5),创建了一个从XML schema文件生成用于操作符和该schema描述的XML文件的数据绑定API生成系统。本发明所有实验用java编程实现,并在在CPU为P4 1.7GHz,内存为512MB,硬盘转速为5400转的个人计算机平台上运行并进行测试。
(1)XMLschema及其XML文件
使用本系统,由CNML标准(中文新闻置标语言)描述的Schema文档(3480多行)生成用于对CNML稿件进行操作的API代码,并使用新华社标准稿件模板(1570多行)对生成的API进行测试,测试内容除了正确性之外,还对API的效率等进行测试。
从读入Schema文件到配置文件的生成耗时15秒,从配置文件生成java代码耗时20秒,编译、打包和生成javadoc文档耗时125秒。一共解析生成369个类,其中最大的类的实现类有2700多行,357个方法;最小的类的实现类有100行,6个方法。生成的API性能测试如表2所示:
(2)生成方法实施
1)生成配置文件
根据本发明的方法,使用CNML Schema作为输入,共生成373个配置文件,其中为element生成的文件263个,datatype生成的文件110个。
2)代码生成
以生成的373个配置文件作为输入,共生成746个类和接口,在生成的类中,其中最大的实现类有2700多行代码,357个方法;最小的实现类中只有100行代码,6个方法。
3)代码编译和打包
对于生成的源代码,使用Ant调用编译器进行编译,最终生成XML-api.jar二进制包。同时通用javadoc提取源代码中的注释信息生成API的说明文档,其格式为html格式。
(3)实验衡量的准则
我们主要以完成某项测试的时间作为效率测试的标准。对于耗时比较少的操作,我们采用循环进行测试10000次的方法进行测量,最终把单次平均时间作为操作的测试时间记录下来。
(4)实验结果
我们首先对生成API整个过程进行效率测试,其结果如下:从读入Schema文件到配置文件的生成耗时15秒,从配置文件生成java代码耗时20秒,编译、打包和生成javadoc文档耗时125秒。
其次,对于所生成的API对于XML稿件进行操作的效率进行测试。我们在这里测试了几种常用的函数的运行时间,并把其记录在表6中。
表6  CNML标准API性能测试      单位:毫秒(ms)
  操作   时间   parse方法   52.63158   save方法   14.92537   get方法   0.30003   set方法   0.30003   add方法   0.006   XPath方法   0.70028
其中,parse速度是指从一个XML稿件解析成API中所定义的对象所需要的时间。save速度是指把XML稿件的内存对象保存成XML文件所花费的时间。get方法主要是测试通过API获取XML某个element结点值所花费的时间。Set方法是测试通过API所提供的方法设置某个element节点值所花费的时间。add方法是指在指定的节点下,添加一个给定值的节点所花费的时间。XPath方法是指通过XPath获取节点和节点值等操作所耗费的时间。