WebService对象可空的困惑,关于nillable和minOccurs

术语约定
文章中会反复出现[值类型]、[包装类型]、[普通引用类型]、[元素节点]和[元素取值]的表述
1> [值类型]指的是java和.NET中的基本数据类型,如:int;
2> [包装类型]指的是java中的包装类和.NET中的Nullable<T>类型,如:Integer、int?;
3> [普通引用类型]指的是除包装类型外的其它引用类型;
4> [元素节点]指的是xml文档中的一个标签节点,我们可以说[元素节点]是必输还是非必输,如:<Name>lubiao</Name>;
5> [元素取值]指的是xml标签节点的内容,我们可以说[元素取值]是空还是非空,如:<Name>lubiao</Name>表示元素取值不为空,<Name xsi:nil="true"/>表示元素取值为空;

1、首先来看minOccurs和Nillable的定义
minOccurs
:表示XML文档中某个[元素节点] 是否可以不出现,默认值为1,表示必须出现
nillable:表示XML文档中某个[元素取值] 是否可以为空(形如:<name xsi:nil="true" />),默认值为false,表示不能为空
那么,minOccurs和nillable的组合形式就会有4种,如下:
例一,值类型:


[html]      view plain      copy     
     
                   
1. <element name="id1" type="int" minOccurs="0" nillable="true"/>  
2. <element name="id2" type="int" minOccurs="0" nillable="false"/>  
3. <element name="id3" type="int" minOccurs="1" nillable="true"/>  
4. <element name="id4" type="int" minOccurs="1" nillable="false"/>

例二,引用类型:


[html]      view plain      copy     
     
                   
1. <element name="name1" type="string" minOccurs="0" nillable="true"/>  
2. <element name="name2" type="string" minOccurs="0" nillable="false"/>  
3. <element name="name3" type="string" minOccurs="1" nillable="true"/>  
4. <element name="name4" type="string" minOccurs="1" nillable="false"/>

2、Java和.NET自动生成WSDL的规则在实际开发时我们通常的做法是先编写Server端代码,然后利用工具自动生成wsdl描述,最后再利用工具读取wsdl进而生成客户端程序。那么用工具生成wsdl和用工具反向生成代理程序的规则是什么呢?下来对此进行实例分析:
2.1 先看.NET生成wsdl的规则实验时所用的开发工具为VS 2010,用vs开发WebService的资料,百度搜索一大推,这里不赘述。
2.1.1、定义数据实体:Person.cs


[csharp]      view plain      copy     
     
                   
1. public class Person   
2. {  
3. public int Id { get; set; } //值类型  
4. public string Name { get; set; }//普通引用类型  
5. public int? PhoneNbr { get; set; }//包装类型  
6. }

2.1.2、定义服务类:WebService.cs


[csharp]      view plain      copy     
     
                   
1. /// <summary>  
2. ///WebService 的摘要说明  
3. /// </summary>  
4. [WebService(Namespace = "http://tempuri.org/")]  
5. [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]  
6. public class WebService : System.Web.Services.WebService {  
7.   
8. public WebService () {  
9. //如果使用设计的组件,请取消注释以下行   
10. //InitializeComponent();   
11.     }  
12.   
13.     [WebMethod]  
14. public string HelloWorld(int id,int? phoneNbr,string name,Person person) {  
15. return "Hello World";  
16.     }  
17.       
18. }


helloWorld方法有4个参数,前三个参数和Person类的三个属性是一样的,加入这三个参数的目的是进行对比测试。
2.1.3、查看生成的wsdl


[html]      view plain      copy     
     
                   
1. <?xml version="1.0" encoding="utf-8"?>  
2. <wsdl:definitions xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://tempuri.org/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">  
3. <wsdl:types>  
4. <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">  
5. <s:element name="HelloWorld">  
6. <s:complexType>  
7. <s:sequence>  
8. <s:element minOccurs="1" maxOccurs="1" name="id" type="s:int" />  
9. <!--nillable默认为false,minOccurs等于1:即id元素为必输,且值不能为空-->  
10. <s:element minOccurs="1" maxOccurs="1" name="phoneNbr" nillable="true" type="s:int" />  
11. <!--nillable等于true,minOccurs等于1:即phoneNbr元素为必输,但值可以为空-->  
12. <s:element minOccurs="0" maxOccurs="1" name="name" type="s:string" />  
13. <!--nillable默认为false,minOccurs等于0:即name元素为非必输,但值不能为空-->  
14. <s:element minOccurs="0" maxOccurs="1" name="person" type="tns:Person" />  
15. <!--nillable默认为false,minOccurs等于0:即person元素为非必输,但值不能为空-->  
16. </s:sequence>  
17. </s:complexType>  
18. </s:element>  
19. <s:complexType name="Person">  
20. <s:sequence>  
21. <s:element minOccurs="1" maxOccurs="1" name="Id" type="s:int" />  
22. <!--nillable默认为false,minOccurs默认为1:即id元素为必输,且值不能为空-->  
23. <s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string" />  
24. <!--nillable默认为false,minOccurs等于0:即Name元素为非必输,但值不能为空-->  
25. <s:element minOccurs="1" maxOccurs="1" name="PhoneNbr" nillable="true" type="s:int" />  
26. <!--nillable默认为false,minOccurs等于1:即PhoneNbr元素为必输,但值可以为空-->  
27. </s:sequence>  
28. </s:complexType>  
29. <s:element name="HelloWorldResponse">  
30. <s:complexType>  
31. <s:sequence>  
32. <s:element minOccurs="0" maxOccurs="1" name="HelloWorldResult" type="s:string" />  
33. </s:sequence>  
34. </s:complexType>  
35. </s:element>  
36. </s:schema>  
37. </wsdl:types>  
38. </wsdl:definitions>


2.1.4、得出结论  
* id参数和Person.Id属性都为值类型:[元素节点] 都必输,[元素取值] 都不能为空
* phoneNbr参数和Person.PhoneNbr属性都是包装类型:[元素节点] 都必输,但[元素取值] 都可以为空;
* name参数和Person.Name属性都是普通引用类型:[元素节点] 都可以不输,但[元素取值] 都不能为空;
* person参数是普通引用类型:[元素节点] 可以不输,但[元素取值] 不能为空。
2.2 再看Java的规则首先,先熟悉在eclipse中快速开发webservice程序的步骤,参考链接:http://www.blogjava.net/parable-myth/archive/2010/08/03/327802.html,此次实验采用的就是这种快速开发方式(注:不同的框架和工具会可能会有不同的生成规则),所用ecllipse版本为:
    Version: Juno Service Release 1
    Build id: 20121004-1855
2.2.1、定义数据实体:Person.java


[java]      view plain      copy     
     
                   
1. package com.lubiao.axis;  
2.   
3. public class Person  implements java.io.Serializable {  
4. private int id;//编号,值类型  
5. private java.lang.String name;//姓名,普通引用类型  
6. private Integer phoneNbr;//电话,包装类型  
7.       
8. public int getId() {return id;}  
9. public void setId(int id) {this.id = id;}  
10.       
11. public java.lang.String getName() {return name;}  
12. public void setName(java.lang.String name) {this.name = name;}  
13.   
14. public Integer getPhoneNbr() {return phoneNbr;}  
15. public void setPhoneNbr(Integer phoneNbr) {this.phoneNbr = phoneNbr;}  
16. }

2.2.2、定义服务类:WebServiceTest.java


[java]      view plain      copy     
     
                   
1. package com.lubiao.axis;  
2.   
3. public class WebServiceTest{  
4. public String helloWorld(int id,Integer phoneNbr,String name,Person person){  
5. return "Hello World";  
6.     }  
7. }

helloWorld方法有4个参数,前三个参数和Person类的三个属性是一样的,加入这三个参数的目的是进行对比测试。
2.2.3、查看生成的Wsdl


[html]      view plain      copy     
     
                   
1. <?xml version="1.0" encoding="UTF-8"?>  
2. <wsdl:definitions targetNamespace="http://axis.lubiao.com" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://axis.lubiao.com" xmlns:intf="http://axis.lubiao.com" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
3. <!--WSDL created by Apache Axis version: 1.4 Built on Apr 22, 2006 (06:55:48 PDT)-->  
4. <wsdl:types>  
5. <schema elementFormDefault="qualified" targetNamespace="http://axis.lubiao.com" xmlns="http://www.w3.org/2001/XMLSchema">  
6. <element name="helloWorld">  
7. <complexType>  
8. <sequence>  
9. <element name="id" type="xsd:int"/>  
10. <!--nillable默认为false,minOccurs默认为1:即id元素为必输,且值不能为空-->  
11. <element name="phoneNbr" type="xsd:int"/>  
12. <!--nillable默认为false,minOccurs默认为1:即phoneNbr元素为必输,且值不能为空-->  
13. <element name="name" type="xsd:string"/>  
14. <!--nillable默认为false,minOccurs默认为1:即name元素为必输,且值不能为空-->  
15. <element name="person" type="impl:Person"/>  
16. <!--nillable默认为false,minOccurs默认为1:即person元素为必输,且值不能为空-->  
17. </sequence>  
18. </complexType>  
19. </element>  
20. <complexType name="Person">  
21. <sequence>  
22. <element name="id" type="xsd:int"/>  
23. <!--nillable默认为false,minOccurs默认为1:即id元素为必输,且值不能为空-->  
24. <element name="name" nillable="true" type="xsd:string"/>  
25. <!--nillable默认为true,minOccurs默认为1:即name元素为必输,但值可以为空-->  
26. <element name="phoneNbr" nillable="true" type="xsd:int"/>  
27. <!--nillable默认为true,minOccurs默认为1:即phoneNbr元素为必输,但值可以为空-->  
28. </sequence>  
29. </complexType>  
30. <element name="helloWorldResponse">  
31. <complexType>  
32. <sequence>  
33. <element name="helloWorldReturn" type="xsd:string"/>  
34. </sequence>  
35. </complexType>  
36. </element>  
37. </schema>  
38. </wsdl:types>  
39. <!-- 其它元素省略 -->  
40. </wsdl:definitions>


2.2.4、得出结论* id参数和Person.id属性都是值类型:[元素节点] 都必输,[元素取值] 都不能为空;
* phoneNbr参数和Person.phoneNbr属性都是包装类型:[元素节点] 都必输,但前者[元素取值]不能为空,后者[元素取值]可以为空;
* name参数和Person.name属性都是普通的引用类型:[元素节点] 都必输,但前者[元素取值]不能为空,后者[元素取值]可以为空;
* person参数属于普通引用类型:[元素节点]必输,且[元素取值]不能为空;
*总结:此框架生成wsdl时认为:
        1、作为方法参数时,不管是值类型、包装类型还是普通引用类型,[元素节点]都为必输项,且[元素取值]都不能为空;
        2、作为对象属性时,不管是值类型、包装类型还是普通引用类型,[元素节点]都为必输项,其中值类型的[元素取值]不能为空,但包装类型和普通引用类型的[元素取值]可以为空。
2.3、Java和.NET的异同
其一,对于值类型:

            Java和.NET都认为值类型不论是作为 [方法参数时] 还是 [对象属性时] ,[元素节点]都为必输项,且[元素取值]都不能为空。
             (都合理:这符合值类型的特点,总有一个值)
其二,对于包装类型:
            作为方法参数时:java和.NET都认为[元素节点]是必输的,但java认为[元素取值]不能为空,而.NET认为[元素取值]可以为空;
               (.NET合理,Java不合理。java既要求必输又要求值不能为空,那空对象怎么传入?可以通过生成客户端代理进行验证(这里不再演示),对于phoneNbr参数客户端代码变成了int类型,而服务端可是Integer类型)
            作为对象属性时:java和.NET都认为[元素是节点]必输的,而[元素取值]是可以为空的;
                (都合理。java和.net都要求节点必须出现,然后以nillable=true标识可以传入空值)
其三,对于普通引用类型:
            作为方法参数时:java和.NET都认为[元素取值]是不能为空的,但Java同时认为[元素节点]是必输的,而.NET认为[元素节点]是非必输的;
                (.NET合理,Java不合理。java既要求节点必输还要求值不能为空,那空对象怎么传入?)
            作为对象属性时:java认为[元素节点]是必输的、[元素取值]是可空的;.NET认为[元素节点]是非必输的、[元素取值]是不可空的。
                (都合理。java始终要求必输,但以nillable=true标识可以传入空对象;.NET始终要求元素取值不能为空,但以minOccurs=0来标识可以传入空对象)
总结:作为参数时,java出现了很多不合理的情况,可能是因为选的这个框架有bug,这里不再关心,主要看作为对象属性时的情况。文章最开始的时候说到minOccurs和nillable会有4种组合方式,在上面的实验中java用到了两种,而.NET用到了3种,分别为:
Java:[ minOccurs=1,nillable=false ] 和 [ minOccurs=1,nillable=true ],前者对应值类型,后者对应引用类型(包装类型和普通引用类型),java压根没用minOccrs=0
.NET:[ minOccurs=1,nillable=false ]、[ minOccurs=1,nillable=true ] 和[ minOccurs=0,nillable=false],前者对应值类型,再者对应包装类型,最后对应普通引用类型。
3、Java和.NET由WSDL自动生成代码的规则
接着上面的实验结果,先来做最简单的测试:1、用java的client对接java的service生成代码;2、用.NET的client对接.NET的Service生成代码,然后分别观察Client端自动生成的Person类和Server段的Person是否完全一致。这里不再演示,只说结论,结论是:生成的Person分别和各自Server端的完全相同。
那如果用[ java对接.NET ] 或者[ 用.NET对接java ],会是什么结果呢?等一等,这么问好像有问题,哪有什么谁对接谁,大家对接的都是wsdl,哪里知道wsdl后面是什么平台!!嗯,那就把minOccurs和nillable的四种组合都列出来,看java和.NET的生成规则分别是什么样的,继续。
3.1 先看.NET的规则
3.1.1 定义WSDL,限于篇幅只列出类型定义部分


[html]      view plain      copy     
     
                   
1. <?xml version="1.0" encoding="UTF-8"?>  
2. <schema xmlns="<a target="_blank" href="http://www.w3.org/2001/XMLSchema">http://www.w3.org/2001/XMLSchema</a>" xmlns:sys="<a target="_blank" href="http://com.lubiao.service/system">http://com.lubiao.service/system</a>" targetNamespace="<a target="_blank" href="http://com.lubiao.service/system">http://com.lubiao.service/system</a>" elementFormDefault="qualified">  
3. <complexType name="Person">  
4. <sequence>  
5. <element name="id1" type="long" minOccurs="0" nillable="true"/>  
6. <element name="id2" type="long" minOccurs="0" nillable="false"/>  
7. <element name="id3" type="long" minOccurs="1" nillable="true"/>  
8. <element name="id4" type="long" minOccurs="1" nillable="false"/>  
9. <element name="name1" type="string" minOccurs="0" nillable="true"/>  
10. <element name="name2" type="string" minOccurs="0" nillable="false"/>  
11. <element name="name3" type="string" minOccurs="1" nillable="true"/>  
12. <element name="name4" type="string" minOccurs="1" nillable="false"/>  
13. </sequence>  
14. </complexType>  
15.     
16. <complexType name="PersonResponse">  
17. <sequence>  
18. <element name="id" type="string" minOccurs="0"/>  
19. </sequence>  
20. </complexType>  
21.     
22. <element name="GetPerson" type="sys:Person"></element>  
23. <element name="GetPersonResponse" type="sys:PersonResponse"></element>  
24. </schema>


id对应的是值类型,name对应的是引用类型
3.1.2 生成数据类

[csharp]      view plain      copy     
     
                   
1. [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18058")]  
2. [System.SerializableAttribute()]  
3. [System.Diagnostics.DebuggerStepThroughAttribute()]  
4. [System.ComponentModel.DesignerCategoryAttribute("code")]  
5. [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://com.csii.bank.core/system")]  
6. public partial class Person{  
7.               
8. private System.Nullable<long> id1Field;  
9.       
10. private bool id1FieldSpecified;  
11.       
12. private long id2Field;  
13.       
14. private bool id2FieldSpecified;  
15.       
16. private System.Nullable<long> id3Field;  
17.       
18. private long id4Field;  
19.       
20. private string name1Field;  
21.       
22. private string name2Field;  
23.       
24. private string name3Field;  
25.       
26. private string name4Field;  
27.               
28. /// <remarks/>  
29. true)]  
30. public System.Nullable<long> id1 {  
31. get {  
32. return this.id1Field;  
33.         }  
34. set {  
35. this.id1Field = value;  
36.         }  
37.     }  
38.       
39. /// <remarks/>  
40.     [System.Xml.Serialization.XmlIgnoreAttribute()]  
41. public bool id1Specified {  
42. get {  
43. return this.id1FieldSpecified;  
44.         }  
45. set {  
46. this.id1FieldSpecified = value;  
47.         }  
48.     }  
49.       
50. /// <remarks/>  
51. public long id2 {  
52. get {  
53. return this.id2Field;  
54.         }  
55. set {  
56. this.id2Field = value;  
57.         }  
58.     }  
59.       
60. /// <remarks/>  
61.     [System.Xml.Serialization.XmlIgnoreAttribute()]  
62. public bool id2Specified {  
63. get {  
64. return this.id2FieldSpecified;  
65.         }  
66. set {  
67. this.id2FieldSpecified = value;  
68.         }  
69.     }  
70.       
71. /// <remarks/>  
72. true)]  
73. public System.Nullable<long> id3 {  
74. get {  
75. return this.id3Field;  
76.         }  
77. set {  
78. this.id3Field = value;  
79.         }  
80.     }  
81.       
82. /// <remarks/>  
83. public long id4 {  
84. get {  
85. return this.id4Field;  
86.         }  
87. set {  
88. this.id4Field = value;  
89.         }  
90.     }  
91.       
92. /// <remarks/>  
93. true)]  
94. public string name1 {  
95. get {  
96. return this.name1Field;  
97.         }  
98. set {  
99. this.name1Field = value;  
100.         }  
101.     }  
102.       
103. /// <remarks/>  
104. public string name2 {  
105. get {  
106. return this.name2Field;  
107.         }  
108. set {  
109. this.name2Field = value;  
110.         }  
111.     }  
112.       
113. /// <remarks/>  
114. true)]  
115. public string name3 {  
116. get {  
117. return this.name3Field;  
118.         }  
119. set {  
120. this.name3Field = value;  
121.         }  
122.     }  
123.       
124. /// <remarks/>  
125. public string name4 {  
126. get {  
127. return this.name4Field;  
128.         }  
129. set {  
130. this.name4Field = value;  
131.         }  
132.     }  
133. }


分析:
id1:可空类型、特性:IsNullable=true、生成了对应的id1Specified
id2:值类型、特性:IsNullable=false、生成了对应的id2Specified
id3:可空类型、特性:IsNullable=true
id4:值类型、特性:IsNullable=false
name1:引用类型、特性:IsNullable=true
name2:引用类型、特性:IsNullable=false
name3:引用类型、特性:IsNullable=true
name4:引用类型、特性:IsNullable=false
3.1.3 查看SOAP报文
将可为null的属性都置位null,然后查看SOAP报文是什么样子的
C#代码1:

[csharp]      view plain      copy     
     
                   
1. Person p=new Person();  
2. p.id1 = null;  
3. p.id2 = 123;  
4. p.id3 = null;  
5. p.id4 = 456;  
6. p.name1 = null;  
7. p.name2 = null;  
8. p.name3 = null;  
9. p.name4 = null;


C#代码2:

[csharp]      view plain      copy     
     
                   
1. Person p=new Person();  
2. p.id1 = null;  
3. p.id1Specified = true;  
4. request.id2 = 123;  
5. p.id1Specified = true;  
6. p.id3 = null;  
7. p.id4 = 456;  
8. p.name1 = null;  
9. p.name2 = null;  
10. p.name3 = null;  
11. p.name4 = null;


SOAP报文1:对应C#代码1,只贴出Person部分:


[html]      view plain      copy     
     
                   
1. <Person>  
2. <id3 xsi:nil="true" />  
3. <id4>456</id4>  
4. <name1 xsi:nil="true" />  
5. <name3 xsi:nil="true" />  
6. </Person>


SOAP报文2:对应C#代码2,只贴出Person部分:


[html]      view plain      copy     
     
                   
1. <Person>  
2. <id1 xsi:nil="true" />  
3. <id2>123</id2>  
4. <id3 xsi:nil="true" />  
5. <id4>456</id4>  
6. <name1 xsi:nil="true" />  
7. <name3 xsi:nil="true" />  
8. </Person>


3.1.4 得出结论
其一:对于值类型和包装类型

minOccurs="0" 组合nillable="true"时
会生成包装类型(id1),同时会生成对应的id1Specified属性,为什么会生成带Specified的属性呢?这是因为minOccurs="0"的缘故,minOccurs=0意味着元素节点可以不出现,那到底是出现呢?还是不出现呢?工具自己没了主意,所以生成了一个xxxSpecified属性,该属性默认值为false,只有给它指定true的时候[元素节点]才会出现到soap报文中。
minOccurs="0"组合nillable="false"时
会生成值类型(id2),同时会生成对应的id2Specified属性(原因同上)
minOccurs="1" 组合nillable="true"时
会生成包装(id3),不会生成Specified属性(因为元素节点必输)。
minOccurs=“1”组合nillable=“false”时
会生成值类型(id4),不会生成Specified属性(因为元素节点必输)。
其二:对于普通引用类型
name1、name3:
只要nillable=true不管minOccurs等于什么,[null对象]序列化的时候都以[元素取值]为空方式来体现这是一个空对象。
name2、name4:   
只要nillable=false不管minOccurs等于什么,[null对象]序列化时都以[元素节点]不出现的方式来体现这是一个空对象。(上面实验时,Server端对soap报文进行了schema验证,所以name4传null会报错的)

总结:
* .NET在wsdl_2_C# 和C#_2_wsdl时的思路其实是一致的,1、生成值类型还是包装类型先看nillable属性、然后再看minOccurs属性来控制是否生成xxxSpecified属性;2、而生成普通引用类型时只关心nillable属性,nillable=true就采用nil=true的方式发送null对象,nillabe=false则采用[元素节点]不出现的方式发送null对象,压根就不关心minOccurs是神马。
* 前面说到name4=null时,Server端会报错,所以 对于引用类型:minOccurs=1和nillable=false的组合是没有意义的,这种组合无法让空对象传输过来
3.2 再看Java的规则

3.2.1 定义WSDL,限于篇幅只列出类型定义部分

[html]      view plain      copy     
     
                   
1. <span style="color:#000000;"><?xml version="1.0" encoding="UTF-8"?>  
2. <schema xmlns="<a target="_blank" href="http://www.w3.org/2001/XMLSchema">http://www.w3.org/2001/XMLSchema</a>" xmlns:sys="<a target="_blank" href="http://com.lubiao.service/system">http://com.lubiao.service/system</a>" targetNamespace="<a target="_blank" href="http://com.lubiao.service/system">http://com.lubiao.service/system</a>" elementFormDefault="qualified">  
3. <complexType name="Person">  
4. <sequence>  
5. <element name="id1" type="long" minOccurs="0" nillable="true"/>  
6. <element name="id2" type="long" minOccurs="0" nillable="false"/>  
7. <element name="id3" type="long" minOccurs="1" nillable="true"/>  
8. <element name="id4" type="long" minOccurs="1" nillable="false"/>  
9. <element name="name1" type="string" minOccurs="0" nillable="true"/>  
10. <element name="name2" type="string" minOccurs="0" nillable="false"/>  
11. <element name="name3" type="string" minOccurs="1" nillable="true"/>  
12. <element name="name4" type="string" minOccurs="1" nillable="false"/><!--前面说的这个节点这么定义没有意义,为了实验的严谨性,依然保留-->  
13. </sequence>  
14. </complexType>  
15.     
16. <complexType name="PersonRequest">  
17. <sequence>  
18. <element name="person" type="sys:Person"/>  
19. </sequence>  
20. </complexType>  
21. <complexType name="PersonResponse">  
22. <sequence>  
23. <element name="id" type="string" minOccurs="0"/>  
24. </sequence>  
25. </complexType>  
26.     
27. <element name="GetPerson" type="sys:PersonRequest"></element>  
28. <element name="GetPersonResponse" type="sys:PersonResponse"></element>  
29. </schema>  
30. </span>


和.net相比,此处多了一个类型PersonRequest,之所以改成这样是因为:如果按照net那种方式,生成的java代码中不会有Person类,因为工具认为id1,id2....是GetPerson的参数。

3.1.2 生成数据类

[java]      view plain      copy     
     
                   
1. <span style="color:#000000;">/**
2.  * Person.java
3.  *
4.  * This file was auto-generated from WSDL
5.  * by the Apache Axis 1.4 Apr 22, 2006 (06:55:48 PDT) WSDL2Java emitter.
6.  */  
7.   
8. package core.bank.csii.com.system;  
9.   
10. public class Person  implements java.io.Serializable {  
11. private java.lang.Long id1;  
12.   
13. private java.lang.Long id2;  
14.   
15. private java.lang.Long id3;  
16.   
17. private long id4;  
18.   
19. private java.lang.String name1;  
20.   
21. private java.lang.String name2;  
22.   
23. private java.lang.String name3;  
24.   
25. private java.lang.String name4;  
26.   
27. public Person() {  
28.     }  
29.   
30. public Person(  
31.            java.lang.Long id1,  
32.            java.lang.Long id2,  
33.            java.lang.Long id3,  
34. long id4,  
35.            java.lang.String name1,  
36.            java.lang.String name2,  
37.            java.lang.String name3,  
38.            java.lang.String name4) {  
39. this.id1 = id1;  
40. this.id2 = id2;  
41. this.id3 = id3;  
42. this.id4 = id4;  
43. this.name1 = name1;  
44. this.name2 = name2;  
45. this.name3 = name3;  
46. this.name4 = name4;  
47.     }  
48.   
49.   
50. /**
51.      * Gets the id1 value for this Person.
52.      * 
53.      * @return id1
54.      */  
55. public java.lang.Long getId1() {  
56. return id1;  
57.     }  
58.   
59.   
60. /**
61.      * Sets the id1 value for this Person.
62.      * 
63.      * @param id1
64.      */  
65. public void setId1(java.lang.Long id1) {  
66. this.id1 = id1;  
67.     }  
68.   
69.   
70. /**
71.      * Gets the id2 value for this Person.
72.      * 
73.      * @return id2
74.      */  
75. public java.lang.Long getId2() {  
76. return id2;  
77.     }  
78.   
79.   
80. /**
81.      * Sets the id2 value for this Person.
82.      * 
83.      * @param id2
84.      */  
85. public void setId2(java.lang.Long id2) {  
86. this.id2 = id2;  
87.     }  
88.   
89.   
90. /**
91.      * Gets the id3 value for this Person.
92.      * 
93.      * @return id3
94.      */  
95. public java.lang.Long getId3() {  
96. return id3;  
97.     }  
98.   
99.   
100. /**
101.      * Sets the id3 value for this Person.
102.      * 
103.      * @param id3
104.      */  
105. public void setId3(java.lang.Long id3) {  
106. this.id3 = id3;  
107.     }  
108.   
109.   
110. /**
111.      * Gets the id4 value for this Person.
112.      * 
113.      * @return id4
114.      */  
115. public long getId4() {  
116. return id4;  
117.     }  
118.   
119.   
120. /**
121.      * Sets the id4 value for this Person.
122.      * 
123.      * @param id4
124.      */  
125. public void setId4(long id4) {  
126. this.id4 = id4;  
127.     }  
128.   
129.   
130. /**
131.      * Gets the name1 value for this Person.
132.      * 
133.      * @return name1
134.      */  
135. public java.lang.String getName1() {  
136. return name1;  
137.     }  
138.   
139.   
140. /**
141.      * Sets the name1 value for this Person.
142.      * 
143.      * @param name1
144.      */  
145. public void setName1(java.lang.String name1) {  
146. this.name1 = name1;  
147.     }  
148.   
149.   
150. /**
151.      * Gets the name2 value for this Person.
152.      * 
153.      * @return name2
154.      */  
155. public java.lang.String getName2() {  
156. return name2;  
157.     }  
158.   
159.   
160. /**
161.      * Sets the name2 value for this Person.
162.      * 
163.      * @param name2
164.      */  
165. public void setName2(java.lang.String name2) {  
166. this.name2 = name2;  
167.     }  
168.   
169.   
170. /**
171.      * Gets the name3 value for this Person.
172.      * 
173.      * @return name3
174.      */  
175. public java.lang.String getName3() {  
176. return name3;  
177.     }  
178.   
179.   
180. /**
181.      * Sets the name3 value for this Person.
182.      * 
183.      * @param name3
184.      */  
185. public void setName3(java.lang.String name3) {  
186. this.name3 = name3;  
187.     }  
188.   
189.   
190. /**
191.      * Gets the name4 value for this Person.
192.      * 
193.      * @return name4
194.      */  
195. public java.lang.String getName4() {  
196. return name4;  
197.     }  
198.   
199.   
200. /**
201.      * Sets the name4 value for this Person.
202.      * 
203.      * @param name4
204.      */  
205. public void setName4(java.lang.String name4) {  
206. this.name4 = name4;  
207.     }  
208.   
209. private java.lang.Object __equalsCalc = null;  
210. public synchronized boolean equals(java.lang.Object obj) {  
211. if (!(obj instanceof Person)) return false;  
212.         Person other = (Person) obj;  
213. if (obj == null) return false;  
214. if (this == obj) return true;  
215. if (__equalsCalc != null) {  
216. return (__equalsCalc == obj);  
217.         }  
218.         __equalsCalc = obj;  
219. boolean _equals;  
220. true &&   
221. this.id1==null && other.getId1()==null) ||   
222. this.id1!=null &&  
223. this.id1.equals(other.getId1()))) &&  
224. this.id2==null && other.getId2()==null) ||   
225. this.id2!=null &&  
226. this.id2.equals(other.getId2()))) &&  
227. this.id3==null && other.getId3()==null) ||   
228. this.id3!=null &&  
229. this.id3.equals(other.getId3()))) &&  
230. this.id4 == other.getId4() &&  
231. this.name1==null && other.getName1()==null) ||   
232. this.name1!=null &&  
233. this.name1.equals(other.getName1()))) &&  
234. this.name2==null && other.getName2()==null) ||   
235. this.name2!=null &&  
236. this.name2.equals(other.getName2()))) &&  
237. this.name3==null && other.getName3()==null) ||   
238. this.name3!=null &&  
239. this.name3.equals(other.getName3()))) &&  
240. this.name4==null && other.getName4()==null) ||   
241. this.name4!=null &&  
242. this.name4.equals(other.getName4())));  
243. null;  
244. return _equals;  
245.     }  
246.   
247. private boolean __hashCodeCalc = false;  
248. public synchronized int hashCode() {  
249. if (__hashCodeCalc) {  
250. return 0;  
251.         }  
252. true;  
253. int _hashCode = 1;  
254. if (getId1() != null) {  
255.             _hashCode += getId1().hashCode();  
256.         }  
257. if (getId2() != null) {  
258.             _hashCode += getId2().hashCode();  
259.         }  
260. if (getId3() != null) {  
261.             _hashCode += getId3().hashCode();  
262.         }  
263. new Long(getId4()).hashCode();  
264. if (getName1() != null) {  
265.             _hashCode += getName1().hashCode();  
266.         }  
267. if (getName2() != null) {  
268.             _hashCode += getName2().hashCode();  
269.         }  
270. if (getName3() != null) {  
271.             _hashCode += getName3().hashCode();  
272.         }  
273. if (getName4() != null) {  
274.             _hashCode += getName4().hashCode();  
275.         }  
276. false;  
277. return _hashCode;  
278.     }  
279.   
280. // Type metadata  
281. private static org.apache.axis.description.TypeDesc typeDesc =  
282. new org.apache.axis.description.TypeDesc(Person.class, true);  
283.   
284. static {  
285. new javax.xml.namespace.QName("http://com.csii.bank.core/system", "Person"));  
286. new org.apache.axis.description.ElementDesc();  
287. "id1");  
288. new javax.xml.namespace.QName("http://com.csii.bank.core/system", "id1"));  
289. new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema", "long"));  
290. 0);  
291. true);  
292.         typeDesc.addFieldDesc(elemField);  
293. new org.apache.axis.description.ElementDesc();  
294. "id2");  
295. new javax.xml.namespace.QName("http://com.csii.bank.core/system", "id2"));  
296. new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema", "long"));  
297. 0);  
298. false);  
299.         typeDesc.addFieldDesc(elemField);  
300. new org.apache.axis.description.ElementDesc();  
301. "id3");  
302. new javax.xml.namespace.QName("http://com.csii.bank.core/system", "id3"));  
303. new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema", "long"));  
304. true);  
305.         typeDesc.addFieldDesc(elemField);  
306. new org.apache.axis.description.ElementDesc();  
307. "id4");  
308. new javax.xml.namespace.QName("http://com.csii.bank.core/system", "id4"));  
309. new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema", "long"));  
310. false);  
311.         typeDesc.addFieldDesc(elemField);  
312. new org.apache.axis.description.ElementDesc();  
313. "name1");  
314. new javax.xml.namespace.QName("http://com.csii.bank.core/system", "name1"));  
315. new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema", "string"));  
316. 0);  
317. true);  
318.         typeDesc.addFieldDesc(elemField);  
319. new org.apache.axis.description.ElementDesc();  
320. "name2");  
321. new javax.xml.namespace.QName("http://com.csii.bank.core/system", "name2"));  
322. new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema", "string"));  
323. 0);  
324. false);  
325.         typeDesc.addFieldDesc(elemField);  
326. new org.apache.axis.description.ElementDesc();  
327. "name3");  
328. new javax.xml.namespace.QName("http://com.csii.bank.core/system", "name3"));  
329. new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema", "string"));  
330. true);  
331.         typeDesc.addFieldDesc(elemField);  
332. new org.apache.axis.description.ElementDesc();  
333. "name4");  
334. new javax.xml.namespace.QName("http://com.csii.bank.core/system", "name4"));  
335. new javax.xml.namespace.QName("http://www.w3.org/2001/XMLSchema", "string"));  
336. false);  
337.         typeDesc.addFieldDesc(elemField);  
338.     }  
339.   
340. /**
341.      * Return type metadata object
342.      */  
343. public static org.apache.axis.description.TypeDesc getTypeDesc() {  
344. return typeDesc;  
345.     }  
346.   
347. /**
348.      * Get Custom Serializer
349.      */  
350. public static org.apache.axis.encoding.Serializer getSerializer(  
351.            java.lang.String mechType,   
352.            java.lang.Class _javaType,    
353.            javax.xml.namespace.QName _xmlType) {  
354. return   
355. new  org.apache.axis.encoding.ser.BeanSerializer(  
356.             _javaType, _xmlType, typeDesc);  
357.     }  
358.   
359. /**
360.      * Get Custom Deserializer
361.      */  
362. public static org.apache.axis.encoding.Deserializer getDeserializer(  
363.            java.lang.String mechType,   
364.            java.lang.Class _javaType,    
365.            javax.xml.namespace.QName _xmlType) {  
366. return   
367. new  org.apache.axis.encoding.ser.BeanDeserializer(  
368.             _javaType, _xmlType, typeDesc);  
369.     }  
370.   
371. }</span>

java生成的Person类没有Specified属性,看来它只支持一种形式,下面进行验证

3.2.3查看SOAP报文
java代码:


[java]      view plain      copy     
     
                   
1. Person p=new Person();  
2. p.setId1(null);  
3. p.setId2(null);  
4. p.setId3(null);  
5. p.setId4(0);  
6. p.setName1(null);  
7. p.setName2(null);  
8. p.setName3(null);  
9. p.setName4("123");//.NET实验时,name4传的是null;但如果此处赋null,报文还未发出去,客户端框架就报错:提示不能为null了。

Soap报文:

[html]      view plain      copy     
     
                   
1. <?xml version="1.0" encoding="UTF-8"?>  
2. <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  
3. <soapenv:Body>  
4. <GetSystemStatus xmlns="http://com.csii.bank.core/system">  
5. <person>  
6. <id3 xsi:nil="true"/>  
7. <id4>0</id4>  
8. <name3 xsi:nil="true"/>  
9. <name4>123</name4>  
10. </person>  
11. </GetSystemStatus>  
12. </soapenv:Body>  
13. </soapenv:Envelope>

3.2.4 得出结论:
其一:对于值类型和包装:
  1、除 [ minOccurs=1,nillable=false ] 这一组合除外,生成的都是包装类型。
  2、当对象属性为null时,java根据minOccurs构造soap报文,如果minOccurs=0则[元素节点]不出现代表null,如果minOccurs=1用nil=true表示null。
其二:对于普通引用类型
  生成的肯定是引用类型(这是废话),当对象属性为null是,构造soap报文的规则和上面的2一样
4、深入浅出,最后的总结
不管是由代码生成wsdl,还是由wsdl生成代码,此实验中java都比.NET要简洁,个人比较喜欢java的方式,.NET显得有点儿啰嗦(它把包装类型和普通引用类型做了区分),下面进行最后的总结。
先来总结由代码生成wsdl
java用到了两种组合:
   [minOccurs=1、
nillable=false]:只有值类型用这一组合
   [minOccurs=1、
nillable=true]:包装类型和普通引用类型用这一组合注:其实Java用[minOccurs=0 和nillable=false]代表包装类型和普通引用类型也完全可以,如果是我的话我会用这一组合代替上面的组合,这样可以节省网络流量
而.NET有3种组合:
   [ minOccurs=1、nillable=false]:只有值类型用这一组合
   [ minOccurs=1、nillable=true ] :包装类型用这一组合
   [ minOccurs=0、nillable=false]:普通引用类型用这一组合
现在如果要我们自己写一个生成wsdl的框架,那应该有如下的原则:
   1>  值类型只能用
[minOccurs=1、nillable=false],而包装类型和普通引用类型不能用这一组合(否则空对象无法表示) 
   
2>  包装类型可以用[minOccurs=1、nillable=true]和[ minOccurs=0、nillable=false]
  3> 普通引用类型可以用
[minOccurs=1、nillable=true]和[ minOccurs=0、nillable=false]
  
4> 对于包装类型和普通引用类型不建议用[minOccurs=0、nillable=true],这一组合意义不够明确,究竟是空报文节点表示null呢?还是nill=true表示null呢?当然如果真这么用肯定也没问题,只要序列化工具支持就行。

再来看由wsdl生成代码
我是很难理解微软为什么要把[包装类型]和[普通引用类型]区别对待,在C#_2_WSDL的时候这么区别开到还没什么,但是当WSDL_2_C#的时候就带来很大的麻烦。
1、首先来看<element name="long" type="dateTime" minOccurs="0" nillable="false"/>,对这一组合微软生成的是值类型而不是包装类型,它认为只有nillable=true才代表包装类型,可包装类型和普通引用类型本质上有太大的区别吗?大家都是引用类型,[minOccurs=1、nillable=true]和[ minOccurs=0、nillable=false]应该都可以被解析为包装类型才合适。你微软如果认为nillable=true和包装类型(微软的可空类型)联系那么紧密,那这样的节点<element name="name3" type="string" minOccurs="1" nillable="true"/>最好还是别支持了,但现在普通应用类型你既支持minOccurs=0又支持nillable=true,那包装类型和普通引用类型分开的必要性究竟是神马!!!!!!2、再来看xxxSpecified,前面说到,微软认为对于像type=int这样的节点,如果element定义中出现了minOccurs=0,则要生成一个xxxSpecified属性。这么搞使用起来简直太麻烦了——让程序员指定xxxSepcified为true或false来控制元素节点要不要显示,这种麻烦还是由于“区别对待”带来的。微软只通过nillable判断是否是包装类型,对于minOccurs=0的情况它不得不生成一个xxxSpecified。而如果不区别对待的话就简单多了,只要minOccurs=0和nillable=true随便哪个出现或同时出现,直接生成包装类型。

最后来总结序列化
序列化的时候,java和.NET在处理上也不尽相同:
1、java优先考虑minOccurs,如果minOccurs=0,那不管nillable等于神马,程序中的null以[元素节点]不出现的方式来体现;如果minOccurs=1,则程序中的null以[元素取值]为空的方式来体现;这样的规则同时适用于值类型、包装类型和普通引用类型。
2、而.NET则优先考虑nillable,如果nillable=true,那不管minOccurs等于神马,程序中的null以[元素取值]为空的方式来体现;如果nillable=false,则程序中的null以[元素节点]不出现的方式来体现;这样的规则同时适用于值类型、包装类型和普通引用类型。(注:Specified=true的前提下)