处理JSON数据(转)
《Silverlight 2 & ASP.NET高级编程》第9章创建用户界面,本章将介绍如何使用Silverlight的网络和通信功能来访问分布式数据。本章将涵盖如何创建Silverlight可以调用的服务,讨论处理跨域问题的不同方法,并介绍Silverlight内置的、用于处理数据的类。本节为大家介绍处理JSON数据。
AD:
9.5.3 处理JSON数据
Silverlight为解析从Web服务、REST API或者其他类型所返回的XML数据提供了非常优秀的支持。由于许多服务均返回XML数据,因此在Silverlight应用程序中,诸如XmlReader、XmlSerializer或XDocument之类的类会频繁被使用。但是,XML数据并不是可以从服务返回的唯一数据类型。服务也可能支持JavaScript对象表示(Java Script Object Notation,JSON)。该格式为客户端和服务之间交换数据提供了一种压缩的方法。JSON最初设计是和JavaScript语言一起使用,但是很多情况下,需要在Silverlight中利用诸如C#或者VB.NET之类的语言对它进行处理。在这些情况下,可以使用Silverlight的DataContractJsonSerializer类,该类支持将CLR对象序列化为JSON以及将JSON消息反序列化为CLR对象。
DataContractJsonSerializer类位于System.Runtime.Serialization.Json名称空间中。为了使用该类,需要在Silverlight项目中引用System.ServiceModel.Web程序集。DataContractJson Serializer的结构和功能均和前面所讨论的XmlSerializer类非常类似。它提供了诸如ReadObject的方法以实施反序列化,而提供了WriteObject方法以实施序列化。ReadObject方法接受一个包含需要反序列化的JSON数据的流作为参数,而WriteObject方法则以源对象以及序列化后的JSON数据流作为参数。两个方法的签名如下所示:
- public Object ReadObject(
- Stream stream
- )
- public void WriteObject(
- Stream stream,
- Object graph
- )
1. JSON基础
包括由Flickr所提供的服务的很多REST API,均允许数据以JSON格式返回。JSON利用括弧来区分不同的对象,并利用冒号将属性名和属性值实施分离。下面的例子利用JSON将来源于Flickr的图像数据传递给某个客户端:
- {
- "id":"555242564", "owner":"555371@N00", "secret":"555afd69d",
- "server":"2226", "farm":3, "title":"Camp Chihuahua: Class In Session",
- "ispublic":1, "isfriend":0, "isfamily":0
- }
该JSON片段利用{和}字符指定了照片数据在哪开始到哪结束,利用冒号将属性名和属性值分离,并利用逗号分隔不同的属性。如下所示,利用方括号可以定义一个photo对象的数组:
- {
- "photo":
- [
- {
- "id":"2386242564", "owner":"3911171@N00", "secret":"555afd69d",
- "server":"2226","farm":3,"title":"Camp Chihuahua: Class In Session",
- "ispublic":1, "isfriend":0, "isfamily":0
- },
- {
- "id":"2386239664", "owner":"25111971@N02", "secret":"555cced92",
- "server":"3229", "farm":4, "title":"kid and dog",
- "ispublic":1, "isfriend":0, "isfamily":0
- }
- ]
- }
如下面的JSON消息所示,通过添加嵌套的括号,对象也可以定义子对象:
- {
- "photos":
- {
- "page":1, "pages":32047, "perpage":100, "total":"3204603",
- "photo":
- {
- [
- {
- "id":"2386242564", "owner":"3911171@N00", "secret":"555afd69d",
- "server":"2226", "farm":3, "title":"Camp Chihuahua: Class In Session",
- "ispublic":1, "isfriend":0, "isfamily":0
- },
- {
- "id":"2386239664", "owner":"25111971@N02", "secret":"555cced92",
- "server":"3229", "farm":4, "title":"kid and dog", "ispublic":1,
- "isfriend":0, "isfamily":0
- }
- ]
- }
- },
- "stat":"ok"}
- }
2. 使用DataContractJsonSerializer类
为了使用DataContractJsonSerializer类,并不需要完整地理解JSON消息格式,因为它处理了数据的序列化和反序列化过程。但是,需要足够了解相关知识,以在Silverlight应用程序中创建JSON数据可以被反序列化成的匹配CLR类。映射为前面所示的JSON消息例子的类如下所示:
- public class FlickrResponse
- {
- public string stat { get; set; }
- public photos photos { get; set; }
- }
- public class photos
- {
- public int page { get; set; }
- public int pages { get; set; }
- public int perpage { get; set; }
- public int total { get; set; }
- public photo[] photo { get; set; }
- }
- public class photo
- {
- public string id { get; set; }
- public string owner { get; set; }
- public string secret { get; set; }
- public string title { get; set; }
- public int ispublic { get; set; }
- public int isfriend { get; set; }
- public int isfamily { get; set; }
- public string server { get; set; }
- public string farm { get; set; }
- //Custome properties used for data binding
- public string Url { get; set; }
- public ImageBrush ImageBrush { get; set; }
- }
注意,在每个类中定义的属性均和包含在该JSON消息中的属性大小写匹配。这种大小写的匹配甚至应用到了子类的名称上,如photos和photo。这正是JSON消息正确反序列化的精华所在!JSON消息和CLR对象之间大小写的不匹配将导致数据丢失。
为了反序列化JSON消息,可以调用DataContractJsonSerializer类的ReadObject方法,并为该方法传递一个包含JSON数据的流作为参数。下面所示的例子检索了来自Flickr REST服务的数据,并将其进行了反序列化。数据是利用HttpWebRequest和HttpWebResponse对象进行检索的。而反序列化JSON数据的代码则位于GetJSONResponse CallBack方法中。注意,JSON数据将被反序列化到CLR对象的类型,传递给了DataContractJsonSerializer类的构造函数。
- private Uri CreateJSONUri()
- {
- return new Uri(String.Format("{0}?method={1}&api_key={2}&text={3}" +
- "&per_page={4}&format=json&nojsoncallback=1",
- _BaseUri, "flickr.photos.search", _APIKey, this.txtSearchText.Text,
- 50));
- }
- private void StartJsonRequest()
- {
- //Add reference to System.Net.dll if it's not already referenced
- HttpWebRequest request = (HttpWebRequest)WebRequest.Create
- (CreateJSONUri());
- //Start async REST request
- request.BeginGetResponse(new AsyncCallback(GetJSONResponseCallBack),
- request);
- }
- private void GetJSONResponseCallBack(IAsyncResult asyncResult)
- {
- JSON.FlickrResponse res = null;
- HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
- using (HttpWebResponse response =
- (HttpWebResponse)request.EndGetResponse(asyncResult))
- {
- Stream dataStream = response.GetResponseStream();
- //Deserialize JSON message into custom objects
- DataContractJsonSerializer jsonSerializer =
- new DataContractJsonSerializer(typeof(JSON.FlickrResponse));
- jsonObj = (JSON.FlickrResponse)jsonSerializer.ReadObject
- (dataStream);
- Dispatcher.BeginInvoke(() => ProcessJsonObject(jsonObj));
- }
- }
- private void ProcessJsonObject(JSON.FlickrResponse jsonObj)
- {
- //Assign Url to each photo object as well as ImageBrush to paint photo
- if (jsonObj != null && jsonObj.photos.photo != null)
- {
- foreach (JSON.photo photo in jsonObj.photos.photo)
- {
- photo.Url = this.CreateJSONPhotoUrl(photo);
- ImageBrush brush = new ImageBrush();
- brush.ImageSource = new BitmapImage(new Uri(photo.Url));
- brush.Stretch = Stretch.Uniform;
- photo.ImageBrush = brush;
- }
- DisplayJSONPhotos(jsonObj.photos.photo);
- }
- else
- {
- ShowAlert("Unable to proces photo data.");
- }
- }
- private string CreateJSONPhotoUrl(JSON.photo photo)
- {
- //http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_
- {size}.jpg
- //farm-id: 1
- //server-id: 2
- //photo-id: 1418878
- //secret: 1e92283336
- //size: m, s, t, b
- return String.Format(_BasePhotoUrl, photo.farm, photo.server,
- photo.id,photo.secret, "s");
- }
- private void DisplayJSONPhotos(JSON.photo[] photos)
- {
- if (photos != null && photos.Length > 0)
- {
- this.icPhotos.ItemsSource = photos;
- }
- else
- {
- ShowAlert("No photos found.");
- }
- }