Introduction
My last post (Consuming a WebService from Silverlight) was all about accessing a Web Service/API from Silverlight. In the article, I described how to consume an external API, with a sample from the GeoName Web Services. This post is a continuation of that article. Here, I am going to demonstrate how to convert the result output, which is in XML, to a CLR object, and of course use it as a datasource in a Silverlight app.
Source Code and Links
GeoSearch, the Example Project
GeoName is a provider of various Web Services/API, exposing information about geographic details of places. You can visit the website here. Using their service, I am going to design a search page where the user can get the geo details based on the name.
On accessing the service URL (http://api.geonames.org/search?q=orissa&username=demo), it returns a set of data in XML format. Let's take a look at the XML file. You can try the same service URL in your browser and check the XML file.
In my last post, I had mentioned this result XML can be parsed in a Silverlight project using any of the methods mentioned below:
- Using
XmlReader
- Using
XLINQ
- Using
XmlSerializer
As XmlReader
and XLINQ
are quite straightforward, I am going to skip them for the time being, but you can take a look into those topics from these links: parsing XML with Xlinq, XmlReader. This post describes how to deserialize the XML file to a collection of CLR objects using the XMLSerializer
class and attributes.
A Little Bit About System.Xml.Serialization
This namespace
contains classes and attributes that allow us to convert an object to XML and vice versa. Attributes can be defined against CLR object members for serializing and deserializing. This MSDN resource will give you a detailed insight about the namespace
. As this post is concerned about converting XML to CLR objects, let's focus on that.
Project GeoSearch
Converting XML to objects basically involves three steps:
- Create the schema/class from XML
- Apply attributes to the members of the generated class
- Use the XMLSerializer class to deserialize it
1. Create the Schema /Class from XML
Using VS2010, create an XML schema over the XML result file.
To generate the class structure from the schema, I am going to use the XSD2CODE
tool available at CodePlex (http://xsd2code.codeplex.com/). Once installed, select your generated schema file in Solution Explorer and run Code Generator.
This way, you will be able to generate the skeleton of classes and members based on the supplied XML schema. But as we don't want our class/property name to be the same as that generated by the tool, let's change that. By modifying some member names of 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
{
public partial class Location
{
public ushort totalResultsCount { get; set; }
public List<GeoDetail> GeoDetails { get; set; }
public string style { get; set; }
}
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 will notice, I have changed the class name of both the generated classes. By doing so, I have to add some attributes 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 elements to the class.
The XMLRoot
attribute directs the deserialization process saying that Location
targets the geonames
root of the XML. Similarly, for the GeoDetail
class. Now we will have a look at the Location
class. Structuring the class as per the XML, Location
is going to have a collection of GeoDetail
s (geoname
in the XML), and an attribute of XMLElement
specifying the name of the element in XML. One point should be kept in mind: if the class and its members carry the same name as the XML root and element, then there is no need for adding extra attributes.
More details about attribute uses on a case based scenario can be found here: MSDN.
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)
{
if (String.IsNullOrWhiteSpace(txtPlace.Text))
{
MessageBox.Show("Provide a location name for result");
return;
}
WebClient client = new WebClient();
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 straightforward, and supports event based callbacks. However, HttpRequest
is more suitable for advanced REST service calls allowing more control over an HTTP request.
The XmlSerializer
class in the System.Xml.Serialization
namespace
has methods responsible for converting plain XML to objects. In this example, we are creating an instance of XmlSerlizer
with a Location
type as the constructor says the type of object it is going to operate it Location
.
Once the OpenReadAsync
operation completes, the results are taken into a stream and passed to the Deserialize
methods for conversion.
Assign the Data Source and Let's Run …
The code above is ready to return a collection of GeoDetail
s which we have assigned to the list box with a defined DataTemplate
. The ItemTemplate
for the list box is quite simple with a XAML based declarative binding expression.
Hooo.. Now it's ready to go live. Take a look at a working demo with the link below.
Conclusion
The strongly typed XML with objects and serialization gives the application the power of transportability although it comes with a bit of extra effort in the initial stage of development. Still, there is lot more to be rediscovered and more complex XML format can be handled. That’s all for this time. Keep learning. I will BRB.
Source Code and Links