四、使用JavaScriptVonverter
使用JavaScriptConverter的作用就是返回一个复杂类型,但可能会出现“循环引用”的错误。那我们该怎么解决呢,一种方式是我们可以采用自定义数据类型来封装复杂类型;通过使用web.config,在web.config里定义Converter.
我们还是分析示例吧,定义一个DataTableWebService.asmx,提供一个方法返回一个DataTable对象:
1
[WebMethod]
2
public DataTable GetDataTable()
3
{
4
DataTable dt = new DataTable();
5
dt.Columns.Add(new DataColumn("ID", typeof(int)));
6
dt.Columns.Add(new DataColumn("Text", typeof(string)));
7
8
Random random = new Random(DateTime.Now.Millisecond);
9
for (int i = 0; i < 10; i++)
10
{
11
dt.Rows.Add(i, random.Next().ToString());
12
}
13
14
return dt;
15
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

新建一个aspx页面来调用这个WebService所提供的方法:
1
<script type="text/javascript">
2
function getDataTable()
3
{
4
DataTableWebService.GetDataTable(onSucceeded,onFailed);
5
}
6
7
//成功时候的回调函数
8
function onSucceeded(result)
9
{
10
alert(result);
11
}
12
13
//失败时候的回调函数
14
function onFailed(error)
15
{
16
alert(error.get_message());
17
}
18
</script>

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

我们提供一个失败时候的回掉函数,当调用失败我们就把错误信息给提示出来。这样在页面调用就会发生一个错误。我们通过一个按扭来调用,当点击按扭的时候就调用getDataTable()方法,运行结果会alert出一个错误,如下图:

是什么原因造成这个错误的呢?这个错误提示信息的表示:“在序列化这个类型(DataTable)的时候发生了一个程循环的引用”。也就是说,因为有循环的引用,我们不能简单的将这个对象序列化。那么我们该怎么做呢。我们可以通过ASP.NET AJAX ctp组件提供的一个动态库("Microsoft.Web.Preview.dll”)来处理,这里我们需将其引入项目中。他指定了一些转换DataSet,DataTable,DataRow的类型。使用他我们需要在Web.config的WebService节点中添加相宜的配置。
1
<jsonSerialization>
2
<converters>
3
<add name="DataSetConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataSetConverter, Microsoft.Web.Preview" />
4
<add name="DataRowConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataRowConverter, Microsoft.Web.Preview" />
5
<add name="DataTableConverter" type="Microsoft.Web.Preview.Script.Serialization.Converters.DataTableConverter, Microsoft.Web.Preview" />
6
</converters>
7 </jsonSerialization>

2

3

4

5

6

7 </jsonSerialization>
通过这样的配置后,我们就可以正确的转换了。WebService返回了一个DataTable对象。客户端接收到result里。下面我们通过拼接字符串的形式来动态的构造一个表格在页面上呈现出来:
1
var sb = new Sys.StringBuilder("<table border='1'>");
2
sb.append("<tr><td>ID</td><td>Text</td></tr>");
3
for (var i = 0; i < result.rows.length; i++)
4
{
5
sb.append(
6
String.format( "<tr><td>{0}</td><td>{1}</td></tr>", result.rows[i]["ID"],
7
result.rows[i].Text));
8
}
9
sb.append("</table>");
10
$get("result").innerHTML = sb.toString();
这就是JavaScriptConverter的用法,下面是运行结果图:
2

3

4

5

6

7

8

9

10


五、自定义JavaScriptConverter
往往我们在使用JavaScriptConverter的时候,我们可能会自定义一个负责的类型,那么在序列化的时候也可能会出现循环引用的情况,处于这种情况我们就可以自己定义一个JavaScriptConverter。那么我们该如何定义这个JavaScriptConverter呢?JavaScriptConverter的作用就是处理循环引用,简化默认的复杂序列化和反序列化的行为。我们首先先定义两个负责的类型:
1
public class Boy
2
{
3
public string Name;
4
public Girl GirlFriend;
5
}
6
7
public class Girl
8
{
9
public string Name;
10
public Boy BoyFriend;
11
}

2

3

4

5

6

7

8

9

10

11

定义JavaScriptConverter我们需定义一个类继承JavaScriptConverter类,并实现SupportedTypes及Serialize方法用于序列化复杂数据;实现SupportenTypes:
1
public override IEnumerable<Type> SupportedTypes
2
{
3
get { yield return typeof(Boy); }
4
}

2

3

4

实现Serialize用户序列化复杂数据:
1
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
2
{
3
Boy boy = (Boy)obj;
4
IDictionary<string, object> result = new Dictionary<string, object>();
5
6
result["Name"] = boy.Name;
7
8
boy.GirlFriend.BoyFriend = null;
9
result["GirlFriend"] = boy.GirlFriend;
10
11
return result;
12
}

2

3

4

5

6

7

8

9

10

11

12

实现Deserialize方法用于反序列化复杂数据:
1
public override object Deserialize(IDictionary<string, object> dictionary,
2
Type type, JavaScriptSerializer serializer)
3
{
4
Boy boy = new Boy();
5
boy.Name = (string)dictionary["Name"];
6
boy.GirlFriend = serializer.ConvertToType<Girl>(dictionary["GirlFriend"]);
7
boy.GirlFriend.BoyFriend = boy;
8
9
return boy;
10
}

2

3

4

5

6

7

8

9

10

到这里我们自定义的JavaScriptConverter就算是万事具备,只欠东风了。这东风从何起呢?我们已经定义好了JavaScriptConverter,最后一步就是需要在web.config里注册该JavaScriptConverter。

接下来我们来看看我们怎么来使用自定义的这个JavaScriptConverter,客户端代码:
1
<script language="javascript" type="text/javascript">
2
function getBoy()
3
{
4
BoyGirlService.GetBoyWithGirlFriend(onGetBoySucceeded, onFailed);
5
}
6
function onGetBoySucceeded(result)
7
{
8
// result.GirlFriend.BoyFriend = result;
9
10
alert(String.format(
11
"他是 {0}, 他的女朋友是 {1}",
12
result.Name,
13
result.GirlFriend.Name));
14
}
15
16
function onFailed(error)
17
{
18
alert(error.get_message());
19
}
20
21
function setBoy()
22
{
23
var boy = new Object();
24
boy.Name = "李四";
25
var girl = new Object();
26
girl.Name = "王玫";
27
boy.GirlFriend = girl;
28
29
BoyGirlService.SetBoyWithGirlFriend(boy, onSetBoySucceeded, onFailed);
30
}
31
function onSetBoySucceeded(result)
32
{
33
alert(result);
34
}
35
</script>

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

同样我们还是通过按扭来实现调用,当点击按纽的时候就得到复杂数据或是设置数据。
1
<input type="button" value="Get Boy" onclick="getBoy()" />
2
<input type="button" value="Set Boy" onclick="setBoy()" />

2

下面是运行后的效果:
示例代码下载:ClientWebService.rar
------------------------------------------------------------------------------------------------------------