REST in ASP.NET MVC – Generating XML

Nowadays I have a hobby project – a beer catalog for mobile devices. It sounds pretty simple, but I managed to make it complex – and robust as well.
The solution has three parts: an ASP.NET MVC web site backed up by SQL Server, an iPhone and an Android client. I struggled a lot with WCF, I thought it would be the right way to implement a REST-based service, but couldn’t figure it out myself. Anyway, I had an MVC site up and running, so I thought I can sort this out pretty easy, since I had an object model with five entities – that means five services to the mobile apps.
I’ll write about the mobile clients in a future post. It’s enough to know that they pull the data from the services and store it in their own SQLite 3 databases. Sometimes (maybe once a week) they perform an update, request the data again and overwrite their existing databases – that’s all that there’s relevant now.
So how would you create a REST API in ASP.NET MVC. I felt that I want something terribly simple. I want to say that return this data as XML, without any duplication. The obvious way was to subclass ActionResult. After some inspiration from William Duffy’s RSSResult implementation, I came up with the following code:

    1. public class XMLResult<T> : ActionResult
    2.   {
    3.     private IEnumerable<T> _items;
    4.     private string _rootName;
    5.     private string _typeName;
    6.     public XMLResult(IEnumerable<T> items, string rootName, string typeName)
    7.     {
    8.       _items = items;
    9.       _rootName = rootName;
    10.       _typeName = typeName;
    11.     }
    12.     public override void ExecuteResult(ControllerContext context)
    13.     {
    14.       if (_items == null)
    15.       {
    16.         throw new NullReferenceException();
    17.       }
    18.       XmlWriterSettings settings = new XmlWriterSettings() { Indent = true, NewLineHandling = NewLineHandling.Entitize };
    19.       context.HttpContext.Response.ContentType = “text/xml”;
    20.       using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.OutputStream, settings))
    21.       {
    22.         writer.WriteStartElement(_rootName);
    23.         foreach (T item in _items)
    24.         {
    25.           if (string.IsNullOrEmpty(_typeName))
    26.           {
    27.             writer.WriteStartElement(item.GetType().Name);
    28.           }
    29.           else
    30.           {
    31.             writer.WriteStartElement(_typeName);
    32.           }
    33.           foreach (PropertyInfo pi in item.GetType().GetProperties())
    34.           {
    35.             if (pi.PropertyType.IsValueType || pi.PropertyType == typeof(string))
    36.             {
    37.               if (pi.GetValue(item, null) != null)
    38.               {
    39.                 writer.WriteElementString(pi.Name, pi.GetValue(item, null).ToString());
    40.               }
    41.             }
    42.           }
    43.           writer.WriteEndElement();
    44.         }
    45.         writer.WriteEndElement();
    46.       }
    47.     }
    48.   }

* This source code was highlighted with Source Code Highlighter.

I think it speaks for itself. The only thing needs a little explanation is the narrowing of the types in the PropertyInfo loop. I needed only value types and string, nothing else. Since my entities contained BLOB data and navigation properties, I got rid of the rest.
This little class is used as follows:

    1. public XMLResult<Beer> Beers()
    2.     {
    3.       return new XMLResult<Beer>(context.Beers.Where(x=>x.IsApproved.Value).OrderBy(x=>x.Name), “Beers”, “Beer”);
    4.     }

* This source code was highlighted with Source Code Highlighter.

And it works with basically anything, thanks to the generic type parameter. From this startpoint, you can easily implement your own POX (Plain Old XML) services with ASP.NET MVC. In the next post we consider how to extract the entities from the generated XML on the iPhone.

Tags: , , ,

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: