SERIALIZE AN OBJECT TO AN URL ENCODED STRING IN C#

9/6/2017 3:06:35 PM




By Arnaud



As a developer, transmit data from one point to another one is a very common task. In most cases, the communication is governed by a well-understood protocol which defined how the message containing the data is structured. 
When working with HTTP, you have different ways to encapsulate your data. It can be within the message body, in a specific header, a cookie or even next to the resource URL you want to reach. If the data is complex, a serialization/deserialization process must be implemented to transmit the data. The serialization process transforms the data in a format (JSON for instance) that can be transmitted according to the protocol constraints.

The HTML Form Case

One of the easiest way to send data through HTTP from a standard client device is to use a HTML Form. When this form is submitted, the data carried by the form entries is sent by building an URL Encoded Content which is inserted within the HTTP message body or next to the URL (query string) according to the form method:

Either the following form:

<form action="/url" method="post">  
  <input type="text" name="data1">
  <input type="text" name="data2">
  <button type="submit">Send</button>
<form>  

When submitted, the input data is encoded as follows:

data1=&data2=  

and can be then deserialized on the server side. With ASP.NET, this task is done by the Model Binder so you can manipulate the data from a C# object:

public class Data  
{
   public string Data1 { get; set; }
   public string Data2 { get; set; }
}

Object To URL Encoded String

It can be useful to manually serialize a C# object to an URL encoded string (building a query string, manual form post for testing purposes, etc.). In .NET, this can be done using the FormUrlEncodedContent class. This class takes as a parameter a collection of data key - data value pairs. 
So how to serialize a C# object to a collection of key-value pairs ? After research, I have not found anything relevant to do that, so here is a solution:

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

    JToken token = metaToken as JToken;
    if (token == null)
    {
        return ToKeyValue(JObject.FromObject(metaToken));
    }

    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) :
                    jValue?.ToString(CultureInfo.InvariantCulture);

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

I simply use JSON.NET to iterate recursively through the object hierarchy and get the JSON path of each property (which is perfectly compliant with the URL format).

Then you can use the FormUrlEncodedContent class to get the URL encoded string:

var keyValueContent = [MY_OBJECT].ToKeyValue();  
var formUrlEncodedContent = new FormUrlEncodedContent(keyValueContent);  
var urlEncodedString = await formUrlEncodedContent.ReadAsStringAsync();  

For example, either the following object definition:

public class Model  
{
    public Guid Id { get; set; }
    public byte Byte { get; set; }
    public string String { get; set; }
    public IList<Guid> GuidList { get; set; }        
    public DateTime Date { get; set; }
    public bool Boolean { get; set; }
    public int? Index { get; set; }
    public Foo1 Foo1 { get; set; }
    public Foo2[] Fooes { get; set; }
}

public class Foo1  
{
    public string Str { get; set; }
    public IEnumerable<int> Ints { get; set; }

}

public class Foo2  
{
    public string Foo { get; set; }
}

with the following initialization:

Model model = new Model  
{
    Id = Guid.NewGuid(),
    String = "Title",              
    GuidList = new List<Guid> { Guid.NewGuid(), Guid.NewGuid() },                
    Index = 1,                
    Byte = 10,                
    Date = DateTime.Parse("08-02-2016"),                
    Foo1 = new Foo1 { Str = "str", Ints = new List<int> { 0, 1, 2, 3 } },
    Fooes = new Foo2[] { new Foo2 { Foo = "Foo1" }, new Foo2 { Foo = "Foo2" } }
};

Thus, this instance could be serialized as the following URL encoded string:

Id=1a9beb6d-4a33-44cd-9bb7-dad72083e7e5&Byte=10&String=Title&  
GuidList%5B0%5D=cc97d760-5515-4a12-a786-133c995bb026&  
GuidList%5B1%5D=46e72600-d560-4ea7-a029-d7b17ba8636f&Date=2016-08-02T00%3A00%3A00.0000000&  
Boolean=False&Index=1&Foo1.Str=str&Foo1.Ints%5B0%5D=0&Foo1.Ints%5B1%5D=1&Foo1.Ints%5B2%5D=2&  
Foo1.Ints%5B3%5D=3&Fooes%5B0%5D.Foo=Foo1&Fooes%5B1%5D.Foo=Foo2  

Besides, you can also directly send your FormUrlEncodedContent object (without stringtransformation). Indeed this class is designed to be used as a HTTP content when sending data with the HttpClient class ;)

Enjoy !

 

Written by source