This blog post starts to cover a number of interesting Windows platform development challenges that I ran into as I built out my Brick Manager app. While the code was initially written for Windows Phone, it also applies to building Windows Store apps. Given that I started the app out as a learning experiment a bit over a year ago, it’s funny that it’s taken me a year to get the first one of these out. Feel free to share your feedback below. – CRS
At the heart of my Brick Manager app is communication with the BrickSet web services. As I started using the service, I wanted to take advantage of HttpClient and the async keyword, but I ran into two challenges up front: (1) Huw uses ASMX for his web service (good luck finding a blog or StackOverflow article discussing how to wire up against one of those) and (2) the site only returns XML (while most Windows/HttpClient blogs only talk about wiring up JSON).
The logic of the XML communications eventually settled on the following pattern:
- Instantiate HttpClient to use for the web service communication
- Instantiate a CancellationToken to allow me to set a time-out
- Parse the result into an XElement data structure, and pass that back to the caller
- From the caller, pass the returned XElement into the appropriate parser
I eventually compartmentalized my service calls into a static class, which centralized all of the web service call logic into one place (which not only deduped the code, but also improved stability and perf).
For the web service communication (steps 1-3 above), this is a cleaned up version of my GetBrickSetData method:
private static async Task<XElement> GetBrickSetData(string url) {
using (<strong>HttpClient _client = new HttpClient()</strong>) {
using (var <strong>_cts = new CancellationTokenSource(TIMEOUT_DEFAULT)</strong>) {
try {
var _<strong>result = await _client.GetAsync(url, _cts.Token);</strong>
var _data = XElement.Parse(<strong>await _result.Content.ReadAsStringAsync()</strong>);
return _data;
} catch (OperationCanceledException _e) {
<span style="color: #008040;"> // Code to handle timed out exception
</span> } catch (System.Net.Http.HttpRequestException _e) {
if (_e.Message.ToLower().Contains("Response status code") && _e.Message.ToLower().Contains("404")) {
<span style="color: #008040;"> // Code to handle timed out exception</span>
}
}
}
return null;
}
}
One item that is worth calling out is the CancellationToken. I mentioned this as step 2 above, but this was a later addition to my code logic, which I added when BrickSet was undergoing a DoS attack. During that time, the app experience looked to users like it was in a perpetual unresponsive state. Adding the CancellationToken enables me to take the default 60 second timeout to something closer to 3 seconds, but the addition did require me to tease apart the typical HttpClient.GetStringAsync() call you see in code samples everywhere into the two calls above.
Once I get the data back from the above, I then have to parse apart the huge clump of XML For me, I found it easiest to use LINQ to XML to parse the data apart. In the code below, which is pulled from my method that parses the BrickSet set themes, I grab the namespace from the XML and then iterate through each elements in the “themes” node.
public static async Task<IEnumerable<Theme>> GetThemes() {
string _url = String.Format(BRICKSET_ASMX + "/getThemes?apiKey={0}", BRICKSET_API_KEY);
XElement _themeData = await GetBrickSetData(_url);
//parse the results
<strong>XNamespace _ns = _themeData.Attribute("xmlns").Value;</strong>
var _themes = (<strong>from themes in _themeData.Elements(_ns + "themes")</strong>
<strong>select new Theme {</strong>
ThemeName = <strong>(string)themes.Element(_ns + "theme").Value</strong>,
Sets = <strong>int.Parse(themes.Element(_ns + "setCount").Value)</strong>
});
return _themes;
}
In the end, [I believe that] I landed on a pretty elegant solution, but it can be a real pain to get it right. Not only do you need to really know the XML structure coming off the server, but you need to be able to handle the parsing in a bullet-proof fashion – one parsing error and the code goes bananas.
Parsing errors to keep your eye out for:
- XML elements that need more robust parsing (e.g., turning 0 | 1 into a Boolean)
- XML elements that are occasionally null
- XML elements that are child nodes (and aren’t always present)
- Optional XML elements that may or may not be there (e.g., when you’re not passing a user ID)
My code (above) gives some decent starting pointers for basic XML parsing, and I’ll dig into my travels through LINQ-to-XML-land in a future blog post.
I hope this helps!
Cliff