MULTIMEDIA

Silverlight Recipes : Networking and Web Service Integration - Using JSON Serialization over HTTP

5/5/2011 4:07:15 PM

1. Problem

Your Silverlight application needs to exchange JavaScript Object Notation (JSON) messages with an HTTP endpoint.

2. Solution

Use the HttpWebRequest/HttpWebResponse pair of types to exchange JSON messages with the HTTP endpoint. Use DataContractJsonSerializer to serialize/deserialize JSON data.

3. How It Works

The techniques used in this recipe are largely .

3.1. JSON

JSON is a very lightweight format that can be applied to data exchanged on the wire between computers. JSON is textual, like XML, and is based on a subset of the JavaScript programming language, borrowing those portions of the JavaScript syntax that are needed to represent data structures and collections. JSON has gained a lot of popularity of late as a serialization format of choice, especially for Ajax web applications, where objects and collections need to be serialized to and from JavaScript code. For more on the format specification, and a more detailed introduction, visit www.json.org.

Listing 1 shows the JSON serialized representation of an instance of the ProductDetail class (which we use in the past recipes and continue to use here), for ProductId of value 680.

Listing 1. JSON representation of a ProductDetail instance
{"Class":"H",
"Color":"Black",
"DaysToManufacture":1,
"DiscontinuedDate":"",
"FinishedGoodsFlag":"True",
"MakeFlag":"True",
"ProductId":680,
"ProductLine":"R ",
"ProductNumber":"FR-R92B-58",
"ReorderPoint":375,
"SafetyStockLevel":500,
"Size":null,
"StandardCost":1059.31,
"Style":"U ",
"Weight":null}

It is easy to note that the serialized format does not contain any details about the actual CLR type or even the data types of the properties being serialized—it is a collection of named properties and their values. It is the job of an appropriate JSON serializer on both ends of the wire to take this textual format and convert it into an instance of a class.

Part of JSON's popularity is based on the fact that it is much more compact than XML in most cases—although both are textual, JSON is less verbose. However, JSON has some disadvantages as well. It was designed to be a serialization format and therefore is not meant to be used in a stand-alone way. In other words, the serialized textual format shown earlier is not much use until you turn it back into an object. XML, on the other hand, enjoys facilities that can be used to operate on the XML itself, such as XPath, XQuery, XSL transformations, and LINQ to XML, whereby the serialized XML can be useful to you without having to be deserialized into an object structure.

If you must choose formats, and you have control on both ends of the wire, JSON is preferable if you never intend to operate directly on the serialized form; XML is preferable otherwise. If you do not have control on both ends, the choice may already be made for you.

3.2. Using the DataContractJsonSerializer Type

Silverlight provides the DataContractJsonSerializer type in System.Runtime.Serialization.Json. It lets you serialize or deserialize JSON data to and from CLR types decorated with the DataContract attribute.

To use DataContractSerializer, create a new instance of it, and initialize it with the type of the CLR object you want to serialize:

DataContractJsonSerializer jsonSer = new
DataContractJsonSerializer(typeof(List<ProductHeader>));

To deserialize some JSON data, pass in a reference to the stream containing the JSON data to the ReadObject() method, and cast the returned object to the desired type:

List<ProductHeader> productList =
jsonSer.ReadObject(jsonStream ) as List<ProductHeader>;

DataContractJsonSerializer supports object trees with nested objects, and ReadObject() returns to you the object at the root of the tree.

To serialize objects to JSON, use the WriteObject() method, passing in a destination stream, and the root object in the object tree that you want serialized:

jsonSer.WriteObject(jsonStream,rootObject);

3.3. Configuring WCF to Use JSON

We continue to use the WCF service from the previous recipes, but let's configure it this time to use JSON formatting on the messages exchanged.

WebGetAttribute and WebInvokeAttribute expose two properties that let you control this formatting: RequestFormat and ResponseFormat. Both properties are of type WebMessageFormat, which is an enum. You need to set RequestFormat to WebMessageFormat.Json to enable the service to accept JSON-formatted requests; set ResponseFormat identically to send JSON-formatted responses from the service.

You must also configure your WCF service endpoint to specify the use of a JSON serializer. To do this, you apply a custom behavior to the endpoint. Define a behavior named ScriptBehavior; the webHttp element in it enforces the use of JSON:

<endpointBehaviors>
<behavior name="ScriptBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>

You can apply the behavior to an endpoint as shown here:

<endpoint address="" behaviorConfiguration="ScriptBehavior" binding="webHttpBinding"
contract="IProductManager" />


4. The Code

Listing 2 shows the service contract modified to use JSON.

Listing 2. Service contract modified for JSON in ServiceContract.cs
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace Recipe7_3.ProductsDataJSONService
{
[ServiceContract]
public interface IProductManager
{
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
List<ProductHeader> GetProductHeaders();

[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json)]
void UpdateProductHeaders(List<ProductHeader> Updates);

[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
ProductDetail GetProductDetail(ushort ProductId);

[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json)]
void UpdateProductDetail(ProductDetail Update);

}
}

You specify the RequestFormat and the ResponseFormat properties of the WebGet and WebInvoke attributes to use JSON. In this case, in the methods GetProductHeaders() and GetProductDetail(), you only need to specify ResponseFormat, because the query is performed using a GET. In case of the update methods, you do not expect a response back from the POST, so only the RequestFormat is set to use JSON; thus the data sent to the service is formatted appropriately. However, when using POST, you may encounter scenarios where you are both sending and receiving data, in which case you need to specify both properties in WebInvokeAttribute.

Listing 3. Codebehind for JSON serialization and deserialization in MainPage.xaml.cs
{
//create and initialize a new DataContractJsonSerializer
DataContractJsonSerializer jsonSer =
new DataContractJsonSerializer(typeof(List<ProductHeader>));
//Deserialize - root object returned and cast
List<ProductHeader> ProductList =
jsonSer.ReadObject(HeaderJson) as List<ProductHeader>;
return ProductList;
}
private void OnProdHdrUpdReqStreamAcquired(IAsyncResult target)
{
HttpWebRequest webReq = target.AsyncState as HttpWebRequest;
Stream stmUpdates = webReq.EndGetRequestStream(target);
Dispatcher.BeginInvoke(new Action(delegate
{
List<ProductHeader> AllItems =
ProductHeaderDataGrid.ItemsSource as List<ProductHeader>;

List<ProductHeader> UpdateList =
new List<ProductHeader>
(
from Prod in AllItems
where Prod.Dirty == true
select Prod
);
//create and initialize a DataContractJsonSerializer
DataContractJsonSerializer jsonSer =
new DataContractJsonSerializer(typeof(List<ProductHeader>));
//write object tree out to the stream
jsonSer.WriteObject(stmUpdates, UpdateList);


stmUpdates.Close();

webReq.BeginGetResponse(
new AsyncCallback(OnProductHeadersUpdateCompleted), webReq);
}));
}
private ProductDetail DeserializeProductDetails(Stream DetailJson)
{

DataContractJsonSerializer jsonSer =
new DataContractJsonSerializer(typeof(ProductDetail));
ProductDetail Detail =
jsonSer.ReadObject(DetailJson) as ProductDetail;
return Detail;
}
private void OnProductDetailUpdateRequestStreamAcquired(IAsyncResult target)
{
HttpWebRequest webReq =
(target.AsyncState as object[])[0] as HttpWebRequest;
Stream stmUpdates = webReq.EndGetRequestStream(target);


ProductDetail Detail =
(target.AsyncState as object[])[1] as ProductDetail;

DataContractJsonSerializer jsonSer =
new DataContractJsonSerializer(typeof(ProductDetail));
jsonSer.WriteObject(stmUpdates, Detail);
stmUpdates.Close();
webReq.BeginGetResponse(
new AsyncCallback(OnProductDetailsUpdateCompleted), webReq);
}


The DeserializeProductHeaders() method uses a DataContractJsonSerializer to deserialize JSON data from a stream to a List<ProductHeader>. You create a new instance of DataContractJsonSerializer, passing in the targeted CLR type. You then call the ReadObject() method, passing in the stream containing the serialized object tree. This deserializes the object and returns it to you as an Object, which you must cast appropriately. Note that if an object tree is serialized into the stream, on deserialization the entire tree is reconstructed and the root object of the tree is returned to you.

In OnProdHdrUpdReqStreamAcquired(), you switch to the main thread using Dispatcher.Invoke(). Prior to sending an update to a ProductHeader, you serialize a List<ProductHeader> containing the updates to a stream as JSON. After you filter out the collection of ProductHeaders containing the updates using LINQ, you again use a newly constructed DataContractJsonSerializer instance, this time initializing it with the type of List<ProductHeader>. You then call the WriteObject()List<ProductHeader> instance containing the updates that you want to serialize. method on it, passing in the target stream and the

DeserializeProductDetails() and OnProductDetailUpdateRequestStreamAcquired() are implemented following the same pattern and should be self-explanatory.

Also note that while sending POST requests that contain JSON-formatted data, you must set the MIME type appropriately by setting the ContentType property on the request to the string "application/json" like so:

WebRequest webReq = HttpWebRequest.Create
(new Uri(string.Format("{0}/UpdateProductHeaders",ServiceUri)));
webReq.Method = "POST";
webReq.ContentType = "application/json";
Other  
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Using Resources in a Game (part 4) - Filling the Screen
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Using Resources in a Game (part 3) - Sprite Drawing with SpriteBatch
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Using Resources in a Game (part 2) - Positioning Your Game Sprite on the Screen
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Using Resources in a Game (part 1) - Loading XNA Textures
  •  iPhone 3D Programming : Holodeck Sample (part 5) - Overlaying with a Live Camera Image
  •  iPhone 3D Programming : Holodeck Sample (part 4) - Replacing Buttons with Orientation Sensors
  •  iPhone 3D Programming : Holodeck Sample (part 3) - Handling the Heads-Up Display
  •  iPhone 3D Programming : Holodeck Sample (part 2) - Rendering the Dome, Clouds, and Text
  •  iPhone 3D Programming : Holodeck Sample (part 1) - Application Skeleton
  •  Building LOB Applications : Printing in a Silverlight LOB Application
  •  Building LOB Applications : Data Validation through Data Annotation
  •  Building LOB Applications : Implementing CRUD Operations in RIA Services
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Resources and Content (part 2) - Adding Resources to a Project
  •  Microsoft XNA Game Studio 3.0 : Displaying Images - Resources and Content (part 1)
  •  iPhone 3D Programming : Blending and Augmented Reality - Rendering Anti-Aliased Lines with Textures
  •  Programming with DirectX : Game Math - Bounding Geometry (part 2) - Bounding Spheres & Bounding Hierarchies
  •  Programming with DirectX : Game Math - Bounding Geometry (part 1) - Bounding Boxes
  •  Programming with DirectX : Game Math - Matrices
  •  iPhone 3D Programming : Anti-Aliasing Tricks with Offscreen FBOs (part 2) - Jittering
  •  iPhone 3D Programming : Anti-Aliasing Tricks with Offscreen FBOs (part 1) - A Super Simple Sample App for Supersampling
  •  
    Most View
    Golden Media Spark One - Plenty To Offer Out Of The Box (Part 1)
    Introducing UEFI BIOS (Part 2)
    How To Automate Your Web With ifttt (Part 1)
    Corsair Carbide 200r - Joy To Build
    Panasonic Lumix DMC-SZ9 - Lots Of Smart Features In A Very Small Camera
    HTC One - A Huge Leap For Android Smartphones
    Windows Server 2003 : Advanced Backup and Restore (part 1) - Backup Options, The Ntbackup Command
    Linux vs Windows 8 (Part 5)
    Gigabyte Osmium Aivia Mechanical Keyboard
    The Complete Guide To Photography On Your Mac! (Part 2)
    Top 10
    Does Microsoft Have An Image Problem? (Part 2)
    Does Microsoft Have An Image Problem? (Part 1)
    Time For A Bigger iPhone?
    99 Mac Secrets (Part 5) - Top ten third-party apps
    99 Mac Secrets (Part 4) - iMovie secrets, GarageBand secrets, iWork secrets
    99 Mac Secrets (Part 3) : Safari secrets, Mail secrets, Safari shortcuts, Mail shortcuts, iPhoto secrets
    99 Mac Secrets (Part 2) : Customizing, Best menu bar add-ons, Quick Look secrets
    99 Mac Secrets (Part 1) : General OS X tips, Security tips, System shortcuts
    iMovie Trailers And Audio Premastered
    PowerTraveller Powerchimp 4A Battery Charger