3
\$\begingroup\$

I've run into the need to deserialize XML that returns from an http request. I've done some light work with XML deserialization in the past but that was against files on disk.

While attempting to write the code I noticed that many of the methods in HttpWeb* and XmlReader had both an async and a regular version. I'd glossed over await/async material in the past but have never done anything with it. This seemed like a good opportunity to kill two birds.

Below is my current attempt at both types of processing. You should be able to copy/paste the code into a C# Console App, add references to System.Runtime.Serialization and System.Xml.Linq, and then run.

I think I'm using everything properly and I'm actually pretty pleased with the code. However, I don't pretend to know enough about what I'm doing here yet to be confident that there aren't pitfalls that I'm missing.

using System;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;

class Program
{
    static void Main() {
        string url = @"http://www.holidaywebservice.com/HolidayService_v2/HolidayService2.asmx/GetHolidaysForDateRange?countryCode=UnitedStates&startDate=2000-01-01T00:00:00&endDate=2014-01-01T00:00:00";

        // Run Synchronous
        var sw1 = new System.Diagnostics.Stopwatch();
        sw1.Start();
        DoStuff(url);
        sw1.Stop();

        // Run Asynchronous
        var sw2 = new System.Diagnostics.Stopwatch();
        sw2.Start();
        DoStuffAsync(url).Wait();
        sw2.Stop();         

        Console.WriteLine("Sync Elapsed:{0}", sw1.Elapsed.TotalMilliseconds);
        Console.WriteLine("Async Elapsed:{0}", sw2.Elapsed.TotalMilliseconds);

        Console.ReadKey();
    }             

    private static void DoStuff(string url) {
        var stream = GetHttpResponse(url, @"application/xml");

        // Xml Processing
        ProcessXmlStream(stream);
    }
    private static async Task DoStuffAsync(string url) {
        var response = await GetHttpResponseAsync(url, @"application/xml");

        // Xml Processing
        await ProcessXmlStreamAsync(response);
    }

    private static WebResponse GetHttpResponse(string url, string contentType) {
        var request = HttpWebRequest.CreateHttp(url);
        request.ContentType = contentType;
        //string creds = userName + ":" + password;
        //creds = Convert.ToBase64String(Encoding.Default.GetBytes(creds));
        //request.Headers["Authorization"] = "Basic " + creds;

        return request.GetResponse();
    }
    private static async Task<WebResponse> GetHttpResponseAsync(string url, string contentType) {
        var request = HttpWebRequest.CreateHttp(url);
        request.ContentType = contentType;
        //string creds = userName + ":" + password;
        //creds = Convert.ToBase64String(Encoding.Default.GetBytes(creds));
        //request.Headers["Authorization"] = "Basic " + creds;

        return await request.GetResponseAsync();
    }

    private static void ProcessXmlStream(WebResponse response) {
        using (var xReader = XmlReader.Create(response.GetResponseStream())) { 
            var dcs = new DataContractSerializer(typeof(XmlElement));
            xReader.MoveToContent();

            while (xReader.Read()) {
                switch (xReader.NodeType) {
                    case XmlNodeType.Element:
                        var e = dcs.ReadObject(xReader, false) as XmlElement;

                        if (e != null) {
                            ProcessResults(e);
                        }

                        break;
                }
            }
        }
    }
    private static async Task ProcessXmlStreamAsync(WebResponse response) {
        using (var xReader = XmlReader.Create(response.GetResponseStream(), new XmlReaderSettings() { Async = true })) {
            var dcs = new DataContractSerializer(typeof(XmlElement));
            await xReader.MoveToContentAsync();

            while (await xReader.ReadAsync()) {
                switch (xReader.NodeType) {
                    case XmlNodeType.Element:
                        var e = dcs.ReadObject(xReader, false) as XmlElement;

                        if (e != null) {
                            ProcessResults(e);
                        }

                        break;
                }
            }
        }
    }

    private static void ProcessResults(XmlElement element) {
        var e = XElement.Parse(element.OuterXml);
        var results = from p in e.DescendantsAndSelf()
                      where p.Name.LocalName == "Holiday"
                      let ns = p.Name.Namespace
                      select new {
                          HolidayCode = (string)p.Element(ns + "HolidayCode"),
                          HolidayDate = (string)p.Element(ns + "Date"),
                      };

        foreach (var item in results) {
            Console.WriteLine("HolidayCode: {0} | HolidayDate: {1}", item.HolidayCode, item.HolidayDate);
        }
    }
}
\$\endgroup\$
4
\$\begingroup\$

I understand that this is an educational exercise, but naming is important. One and two letter names are highly discouraged. When Mr. Maintainer has to map letters to meanings it becomes difficult for him to focus on what's actually happening, because he has to constantly remember thate means element and dcs is a DataContractSerializer. I'm not even going to mention how terrible DoStuff and DoStuffAsync are. Oops, I just did. =;)-

It's also standard in C# to put opening braces on a new line instead of using Java style Egyptian braces. A dev who swims in C# on a regular basis may have a hard time reading this code as it is. You were consistent though, so I can't complain a whole lot about it.

I question the use of a switch statement in ProcessXmlStream. Typically, switch is used when there are multiple cases to test. I think an If statement would work just fine there.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.