The code for this post can be downloaded in the MSDN Code Gallery.
The FormUrlEncodedMediaTypeFormatter class shipped with the ASP.NET Web APIs beta is one of the default formatters in the Web APIs and can be used to support incoming data from the application/x-www-form-urlencoded media type. This is the default format used for HTML form submission, and it has always been supported in ASP.NET MVC (but not in WCF, which was a constant feature request). This is also the default format used by jQuery when submitting objects, which makes supporting this format even more important, given the almost ubiquity of that library.
when sent as the body of a POST request (or other requests with a body) is encoded as follows (line breaks added for clarity)
The FormUrlEncodedMediaTypeFormatter, however, can only read form-urlencoded data; it cannot produce them, because the main scenario for which this formatter was created was to consume such data. There was one request for writing data on this format (for an API which needs to invoke an existing service which doesn’t support JSON input, and supports only forms data instead), so this post will show a simple media type formatter which can both read and write forms encoded data.
A small warning before going on: I haven’t been able to find any formal specification for the format used by jQuery when encoding complex types, so this is what I was able to find out from passing many different object types and observing what their wire representation was. In other words, it works on my machine. If you know of any formal specification of that format, please let me know.
Flattening the object means that for each of the object’s keys, we’ll push them to a stack and start flattening their values. At the end of the operation, the stack should be empty (otherwise some error happened), so we check that for debugging sake.
The main logic of this formatter happens in the recursive Flatten method. Here we check all the possible types of objects which can be written out. jQuery doesn’t write out any null values, so we’re doing the same here. For arrays and objects, the method pushes the key into the stack and calls itself recursively for the value. Finally, for “primitive” values (numbers, strings, Boolean), we create the key by traversing the stack, separating them with square brackets. One thing which I noticed is that for arrays, the last element doesn’t have the index in the serialized format, as shown in the “luckyNumbers” member in the example above, so this code also accounts for that.
That’s it. In the project from code gallery I’ll include a test project which shows some examples of the conversions this formatter can do.