My last Post (Consuming Webservice from Silverlight) was all about accessing a web service/API from Silverlight; there I described how to consume an external API, with a sample from GeoName web services. As I said in that article, this article is the continuation. Here I am going to demonstrate how to convert the result output, which is in XML, to a CLR object and of course using it as a datasource in a Silverlight application.
Source Code and Links
GeoSearch Live Link - : GeoSearch
Download SourceCode -: GeoSearch
GeoSearch, the example Project
GeoName is a provider of various web service/API exposing information about geographic details of places.You can visit their website here. So using their service I am going to design a search page where the user can retrieve geographical details based on name.
On accessing the serviceurl (http://api.geonames.org/search?q=orissa&username=demo) it returns set of data as XML format. Lets have a look into the XML file. You can try the same service URL at your browser and can check the xml file.
As in my last post I had mentioned this result XML can be parsed by a Silverlight application using either of the methods mentioned below:
- Using XmlReader
- Using XLINQ
- Using XmlSerializer
Since XMlReader and XLINQ are quite straight forward, I will skip them for the time being, but still you can have a look into those topics with these links (Parsing XML with Xlinq,XmlReader). This article describes how to deserialize the XML file to a collection of CLR objects using XMLSerializer class and attributes.
Little bit about System.Xml.Serialization
This namespace contains classes and attributes that allows us to convert an object to XML and vise versa. Attributes can be defined against CLR object members for serialization and deserialization. This MSDN resource will give you a detailed insight into the namespace. Since this article is concerned about converting the XML to CLR objects, let's focus on it.
Project GeoSearch
Converting XML to objects basically involves 3 steps
- Create the Schema /class from XML
- Apply Attributes to the members of the generated class
- Using XMLSerializer class deserialize it
1. Create the Schema /class from XML
Use VS2010 to create a XML schema over the XML result file.
To generate the class structure from a schema I am going to use XSD2CODE tool available at codeplex (http://xsd2code.codeplex.com/). Once installed, select your generated schema file in solution explorer and then run the code generator.
This way you will be able to generate the skeleton of classes and members based on the supplied XML schema. But since we don't want our class/property name to be the same as the code generated by the tool, let's change it. By modifying some member names in the generated classes, the output (by default the names will be based on the Tag and element names of XML) will be as below.
- namespace GeoSearch
- {
- /// <summary>
- /// Class For geonames
- /// class containing the collection of location
- /// </summary>
- public partial class Location
- {
- public ushort totalResultsCount { get; set; }
- public List<GeoDetail> GeoDetails { get; set; }
- public string style { get; set; }
- }
- /// <summary>
- /// Class For geoname
- /// Details about each location
- /// </summary>
- public class GeoDetail
- {
- public string toponymName { get; set; }
- public string name { get; set; }
- public decimal lat { get; set; }
- public decimal lng { get; set; }
- public uint geonameId { get; set; }
- public string countryCode { get; set; }
- public string countryName { get; set; }
- public string fcl { get; set; }
- public string fcode { get; set; }
- }
- }
2. Apply Attributes to the members of the generated class
As you can see, I have changed the class names of both generated classes. By doing so I have to add an attribute to the class so that it can relate itself to the XML. Let's compare the XML and the class side by side and map the element to the class.
The XMLRoot attribute directs the deserialization process saying that "Location" targets the "geonames" root of the XML. Similarly for GeoDetail class. Now have a look at Location class. Structuring the class as per the XML the Location is going to have a collection of GeoDetail (geoname in the XML), and having an attribute of XMLElement specifying the name of the element name in XML. One point should be kept in mind; if the class and it's members carry the same name as the XML root and the element then there is no need to add an extra attribute.
More detailed attribute uses on casebased scenario can be found here with MSDN Link.
3. Using the XMLserlizer class to deserialize it
- string baseServiceURL = @"http://api.geonames.org/search?q={0}&username=demo";
- public MainPage()
- {
- InitializeComponent();
- }
- private void btnCallService_Click(object sender, RoutedEventArgs e)
- {
- //Check For Empty Text value
- if (String.IsNullOrWhiteSpace(txtPlace.Text))
- {
- MessageBox.Show("Provide a location name for result");
- return;
- }
- WebClient client = new WebClient();
- //Apply Callback
- client.OpenReadCompleted += (s, ev) =>
- {
- XmlSerializer serializer = new XmlSerializer(typeof(Location));
- try
- {
- Stream stream = ev.Result;
- Location Loc = (Location)serializer.Deserialize(stream);
- lstSearchResult.ItemsSource = Loc.GeoDetails;
- lblNoOfRows.Content=string.Format("About {0} results ",Loc.totalResultsCount.ToString());
- }
- catch (Exception exp)
- {
- MessageBox.Show(exp.InnerException.Message);
- }
- finally
- {
- biSearch.IsBusy = false;
- }
- };
- string serUrl = String.Format(baseServiceURL, txtPlace.Text);
- biSearch.IsBusy = true;
- client.OpenReadAsync(new Uri(serUrl));
- }
- }
For sending and receiving data on the web, Silverlight exposes the classes WebClient and HTTpWebrequest. Both use basic GET and POST based verbs for retrieval and invocation. As compared to HTTPWebRequest, webclient is quite simple and straight forward and supports event based call-back. However HttpRequest is more suitable for advanced REST service call allowing more control over HTTP requests.
The methods of the XmlSerializer class (in the System.Xml.Serialization namespace) are responsible for converting a plain XML to objects. In this example we are creating an instance of XmlSerlizer with Location type as the constructor saying that the type of object it's going to operate is of Location.
Once the OpenReadAsync operation completes, the results are taken into a stream and passed to the Deserialize methods for conversion.
Assign Datasource and Let's run …
The code above is ready to return a collection of GeoDetails which we have assigned to the list box with a defined DataTemplete. The itemtemplete for list box is quite simple with XAML based declarative binding expression.
Hooo.. ,Now its ready to go Live , Check a working demo with link bellow.
Conclusion
The strongly typed XML with objects and serialization provides applications the power of transportability although it comes with a bit of extra effort during the initial stage of development. Still there is a lot more to discover and more complex XML formats can be handled. That's all for this time, keep learning, I will brb.
Source Code and Links
GeoSearch Live Link - : GeoSearch
Download SourceCode -: GeoSearch