SOAP 技术与 B2B 应用集成( 3 )
SOAP 的型系统和数据编码规则
本文最初由 IBM developerWorks 中国网站 发表,其网址是
http://www.ibm.com/developerWorks/cn/
SOAP 的类型和数据的编码是基于一个简单类型系统的,这个简单类型系统是基于程序语言、数据库和半结构数据中的类型系统的,是程序语言、数据库和半结构数据中类型系统的公共特性的一个泛化。在该简单类型系统中,一个类型要么是一个简单 ( 可量化的 ) 类型或是一个复合类型,这个复合类型由多个部分组成,每个部分是一个类型 ( 包括简单类型或复合类型 ) 。在本文的后面会有类型的更为详细的描述。 SOAP 定义了一个编序规则,用于可类型化对象的编序。它在两个级别上操作,首先,给出一个符号上一致化的由该类型系统描述的模式,构造一个 XML 语法层的模式 ( 也就是型的描述 ) ,其次,提供一个类型系统的模式以及一个与该模式相一致的值的表示方法,构造一个 XML 实例层的模式 ( 也就是值的描述 ) 。
在 SOAP 中定义的元素和属性所用到的命名空间标识是 http://schemas.xmlsoap.org/soap/encoding/ 。
SOAP 规范中描述的数据模型和编码风格的使用方式是被鼓励的而不是必备的,其他的数据模型和编码也是可以与 SOAP 联合使用的,但在使用的时候必须使用完整的命名空间修饰以唯一标识该编码风格。
XML 提供了一种灵活性很高,具备良好可扩展性的数据编码方式。 SOAP 规范只定义了非常有限的编码规则,用户可以按需要扩展该基本定义。本文在一个高层次上介绍了编码规则的定义,而以后的文章则描述明确类型的编码规则。
术语定义
为了描述编码,首先来介绍和定义一下以下需要使用的术语:
1. “value” 值,是一个字符串 (string) 、一个可量度对象 ( 数字、日期、玫举 ) 的名字、或是数个简单值的组合。所有的值都有明确的类型。
2. “simple value” 简单值,是一个不可分的值,它不包含任意可以命名的部分。简单值的例子可以是特定的字符串、证书或玫举值等。
3. “compound value” 复合值,是一个值的关系的聚集。复合值的例子可以是特定的采购定单、存货报表、街道地址等。
4. 在一个复合值中,每一个相关的值都可以用一个角色名来区分,也可以用一个序数来区分,当然也可以同时使用两者。这被称为 “accessor” 存取标识。复合值的例子包括特定的采购定单、存货报表等。数组也是复合值。它可以被看成是具有多个相同名字的存取标识 (accessor) 的复合值。
5. “array” 数组,是一个复合值,在其成员值之间仅有顺序位置不同。
6. “struct” 结构,是一个复合值,在其成员值之间的区分是依靠存取标识 (accessor) 。同时所有存取标识的名应各不相同。
7. “simple type” 简单类型,是简单值的类型。简单类型的例子包括那些类 “string”, “integer” ,玫举类等。
8. “compound type” 复合类型,是复合值的类型。复合类型的例子包括采购定单的抽象类型,这些由该类型派生的采购定单具备相同的存取标识 (shipTo, totalCost 等 ) ,当然他们有不同的值 ( 也许对某些值还有约束 ) 。
9. 在一个复合类型中,一个存取标识在本复合类型中是唯一的,如果它和其他复合类型中的某个存取标识无法相区别,则该存取标识名加上复合类型的名字才能成为唯一标识,这个名称为局部名。无论该名是直接或间接基于一个 URI ,如果该存取标识不用加类型名约束就已经是唯一的,那该名就称为全局名。
10. 对于值表的编序模式中所给出的信息,是有可能能决定一些值只能关联一个存取标识的简单实例。对于其他可能的情况,则无法下这个断言。一个值被称为 ”single-reference” 单引用,如果只有一个存取标识能够引用它。如果能被多个引用,无论是事实上还是潜在可能,那就是 ”multi-reference” 多引用。注意有可能在一模式中有一个确定的值是单引用而其他则是多引用。关于单引用和多引用的简单区别,在具体应用时带 ID 等属性使之有可被重复引用的就是多引用,反之就是单引用。
11. 在句法上,一个元素可以是独立的或嵌入的。一个独立的元素是作为编序中一个顶级元素出现。而其他则是嵌入元素。
编序规则
尽管使用 usi:type 属性可以令值的表示可以是自描述的,也就是说即包含值的结构也包含值的类型,但编序规则允许值的类型 可以 仅仅引用模式 (Schema) 中的类型定义。而这些模式 可以 使用 “XML Schema Part 1: Structures” 和 “XML Schema Part 2: Datatypes” 中定义的规范来描述,当然也可以使用其他的模式定义来定义。注意尽管这样,但许多模式定义只支持结构 (struct) 和数组 (array) 类型,而编序规则则有可能要尝试使用结构 (struct) 和数组 (array) 类型之外的复合类型。
编序规则如下:
1. 所有的值都应当表现为 XML 元素的内容 (content) 。一个多引用的值 必须 被表示为一个独立元素的内容。
2. 对每个包含一个值的元素,值的类型表示 必须 满足以下至少一个条件: (a) 包含该值的元素实例包含一个 xsi:type 属性 ( 用于即时申明类型 ) , (b) 包含该值的元素实例包含在一个具备 SOAP-ENC: arrayType 属性的元素中 ( 可能是 default 的 ) , (c) 该元素的名带有一个类型的明确关联,而该类型由一个模式来定义并决定。
3. 一个简单值应被表示为字符数据 (character data) ,也就是说,它没有任何子元素。每一个简单值必须有一个类型,该类型要么是在 XML Schema 规范的 DataTypes 部分中被罗列,要么它的元类型应当在该部分中被罗列。
4. 一个复合值应当被编码为一个元素序列,其中每一个存取标识由一个嵌入元素来表示,他们的名是一一对应的。若存取标识的名在包含它的型中是局部的,则它有一个未修饰的元素名,而带有全局名的存取标识则应有完全修饰的名。
5. 一个多引用的简单或复合值应当被编码为一个独立元素,该独立元素应包含一个局部的带有非限制名的 “id” 属性,该属性的类型为 XML 规范中定义的 ID 类型。对其他对该同一值的所有存取标识应当是一个空元素,该空元素有一个局部的未修饰的属性 “href” ,该属性类型是 XML Schema 规范中定义的 “uri-reference” 类型, “href” 属性的值是一个引用该对应独立元素的 URI 片段标识。
6. 字串和字节数组被表示为多引用简单类型,不过特别的规则也允许它们在通常情况下更有效地表示。一个字串或字节数组的存取标识可以有一个名为 ”id” 的 ID 类型的属性。如果这样的话,所有其他对该同一值的存取标识可以被编码为一个空元素,该空元素应包含一个局部的带有非限制名的 “href” 属性,该属性的类型为 XML Schema 规范中定义的 “uri-reference” 类型, “href” 属性的值是一个引用该对应独立元素的 URI 片段标识。
7. 对一个值编码多个引用是允许的,看上去这些引用好象是引用了多个不同的值,但事实上这些引用句柄引用的是同一值实体。
8. 数组是复合值。 SOAP 数组被定义为类型是 “SOAP-ENC:Array” 或类型是源于 “SOAP-ENC:Array” 。
SOAP 数组有一或多维,而它的成员由顺序位置区分。一个数组的值被表示为一个能表示该数组的元素的序列,这些成员按序数从小到大顺序出现。对于多维数组,则元素维按从右到左顺序变化。每一个成员元素都被命名为一个独立元素 [ 参见编码规则 2] 。
SOAP 数组可以是单引用值,也可以是多引用值,从而可以被表示为一个嵌入元素或一个独立元素。
SOAP 数组 必须 包含一个 “SOAP-ENC:arrayType” 属性,其中定义的包含元素的值的类型与维数一起描述了该数组。 ”SOAP-ENC:arrayType” 属性的值定义如下:
arrayTypeValue = atype asize
atype = QName *( rank )
rank = "[" ( "," ) "]"
asize = "[" #length "]"
length = 1DIGIT
“atype” 结构是数组所包含的元素的类型的名,首先它包含一个 QName 表示, QName 应在 XML Schema 元素声明中的 “type” 属性中出现,是一个预先定义好的类型名, QName 是一个型约束 ( 意味着所有其包含的元素都应宣称与该指明的类型相一致,也就是说,在 SOAP-ENC:arrayType 中引用的类型必须是所有数组元素的类型或元类型 ) 。对于那些数组的数组或是 “jagged arrays” ,使用 rank 结构来表示数组的元素是一个数组,同时该数组的具体类型将在下层具体成员数组的定义时进行实例化, rank 中出现零个、一个到多个逗号,表明该成员变量是一维、二维或多维数组。对于多维数组,维数定义为一个由 “,” 分隔的维数序列,每个维数的计数基数为 1( 也就是说声明为 6 ,就是有 6 个元素,但从后面的定义我们会知道每个成员的引用则是 0..5) 。
“asize” 结构包含一个由逗号分隔的由零个、一个或多个整数组成的序列指明的数组的每个维的长度。一个由零个整数组成的序列表明对数组打下并没有特别限制,不过具体的大小将由下层具体的成员来决定 ( 例如 “,,,” 、 “6,,6” “2,” “3,3”) 。
例如,一个有 5 个成员的数组,每个成员的类型都为 integer 数组,但各个成员的数组长度可待定,那么它的 arrayTypeValue 的值就应当是 “int[][5]” 。其中, atype 的值是 “int[]” , asize 的值是 “[5]” 。类似地,一个有 3 个成员的数组,成员类型为 integer 二维数组,它的 arrayTypeValue 的值就应当是 “int[,][3]” 。其中, atype 的值是 “int[,]” , asize 的值是 “[3]” 。
SOAP 数组成员 可以 包含一个 “SOAP-ENC:offset” 属性来指明该成员在其装载的数组中的偏移量。这也可以用于指明在一个 部分描述的数组 中成员的偏移。类似的, SOAP 数组成员 可以 包含一个 “SOAP-ENC:position” 属性来指明该成员在其装载的数组中的位置。这也可以用于指明在一个 稀疏描述的数组 中成员的位置。 “SOAP-ENC:offset” 和 “SOAP-ENC:position” 属性的值定义为:
arrayPoint = "[" #length "]"
他们的基数都是 0 。
9. NULL 值和默认值 可以 在存取标识元素中省略 ( 依赖于模式 Schema 中的定义,或者 ) 。 NULL 值可以在一个存取标识元素中使用一个值为 1 的属性 xsi:null 来指明,或者可以是其他依赖于应用程序的属性和值。
注意规则 2 允许独立元素和数组中成员元素可以自由地使用元素名,而不需要和其包含的类型的名相对应。