Serializing objects to URL encoded form data

While messing with dictionaries to create form data for FormUrlEncodedContent so I can send data to server using HTTP client, I started thinking about easier and cleaner way to do it. I was writing integration tests and I wanted to re-use some model classes instead of dictionaries. Here’s how to do it. Sample of integration test is incluced.

Those who are currently writing integration tests for some ASP.NET Core web application may also find the following writings interesting:


I write integration tests for ASP.NET Core web application. There are tests that use HTTP POST to send some data to server. I can create POST body like shown here.

var formDictionary = new Dictionary<string, string>();
formDictionary.Add("Id", "1");
formDictionary.Add("Title", "Item title");

var formContent = new FormUrlEncodedContent(formDictionary);

var response = await client.PostAsync(url, formContent);

I don’t like this code and I would use it only if other options turn out less optimal. Instead, I want something like shown here.

var folder = new MediaFolder { Id = 1, Title = "Folder title" };

var formBody = GetFormBodyOfObject(folder);

// Act
var response = await client.PostAsync(url, fromBody);

Or why not something more elegant like code here.

var folder = new MediaFolder { Id = 1, Title = "Folder title" };

// Act
var response = await client.PostAsync(url, folder.ToFormBody());

This way I can use model classes from web application because these classes are used on forms anyway to move data between browser and server. If otherwise breaking change is introduced then compiler will tell me about it.

Serializing objects to Dictionary<string,string>

Thanks to Arnaud Auroux from Geek Learning we have ready-made solution to  take. ToKeyValue() method by Arnaud turns object to JSON and then uses Newtonsoft.JSON library convert object to dictionary like FormUrlEncodedContent class wants.

ToKeyValue() method is the main work horse for my solution. I turned it to extension method and made serializer avoid cyclic references.

public static IDictionary<string, string> ToKeyValue(this object metaToken)
    if (metaToken == null)
        return null;

    // Added by me: avoid cyclic references
    var serializer = new JsonSerializer { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
    var token = metaToken as JToken;
    if (token == null)
        // Modified by me: use serializer defined above
        return ToKeyValue(JObject.FromObject(metaToken, serializer));

    if (token.HasValues)
        var contentData = new Dictionary<string, string>();
        foreach (var child in token.Children().ToList())
            var childContent = child.ToKeyValue();
            if (childContent != null)
                contentData = contentData.Concat(childContent)
                                         .ToDictionary(k => k.Key, v => v.Value);

        return contentData;

    var jValue = token as JValue;
    if (jValue?.Value == null)
        return null;

    var value = jValue?.Type == JTokenType.Date ?
                    jValue?.ToString("o", CultureInfo.InvariantCulture) :

    return new Dictionary<string, string> { { token.Path, value } };

Now we can serialize our objects to Dictionary<string,string> and it also works with object hierarchies.

Serializing object to form data

But we are not there yet. ToKeyValue() extension method is useful if we want to modify dictionary before it goes to FormUrlEncodedContent. For cases when we don’t need any modifications to dictionary I wrote ToFormData() extension method.

public static FormUrlEncodedContent ToFormData(this object obj)
    var formData = obj.ToKeyValue();

    return new FormUrlEncodedContent(formData);

Using these extension methods I can write integration tests like shown here.

public async Task CreateFolder_CreatesFolderWithValidData()
    // Arrange
    var factory = GetFactory(hasUser: true);
    var client = factory.CreateClient();
    var url = "Home/CreateFolder";

    var folder = new MediaFolder { Id = 1, Title = "Folder title" };

    // Act
    var response = await client.PostAsync(url, folder.ToFormData());

    // Assert
    response.EnsureSuccessStatusCode(); // Status Code 200-299
    Assert.Equal("text/html; charset=utf-8", response.Content.Headers.ContentType.ToString());

Wrapping up

We started with annoying problem and ended up with pretty nice solution. Instead of manually filling dictionaries used for HTTP POST requests in integration tests we found nice JSON based solution by Arnaud Auroux and we built ToKeyValue() and ToFormData() extension methods to simplify object serialization to .NET Core HttpClient form data. These extension methods are general enough to have them in some common library for our projects.

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

    One thought on “Serializing objects to URL encoded form data

    • April 23, 2019 at 10:30 am

      Am I missing something here? Why the additional dependency on to do this where simply using reflection to get all object properties and their values would’ve been enough?

    Leave a Reply

    Your email address will not be published. Required fields are marked *