Ori Refael Ori Refael - 7 days ago 5
C# Question

c# is there a method to serialize to UrlEncoded?

I want to use facebook's API and i find it hard to convert objects to urlEncoded.
so, for now i have something like:

string postData = JsonConvert.SerializeObject(req);

postData = postData.Replace(@"\", "");
postData = HttpUtility.UrlEncode(postData);

byte[] data = Encoding.UTF8.GetBytes(postData);

string facebookUrl = "https://graph.facebook.com/v2.5/";


problem is that facebook doesn't accept jsons but UrlEncoded data, as it seems, correct me if im wrong.

So, Im pretty sure converting objects to UrlEncoded string is impossbile in .Net 4.5.1 because I've tried to use some of the answers for this questions that are while ago they are not working for me.

for example:

var result = new List<string>();
foreach (var property in TypeDescriptor.GetProperties(req))
{
result.Add(property.Name + "=" + property.GetValue(req));
}

postData = string.Join("&", result);


but
.Name
and
.GetValue
aren't defined at all.

Would like to get some help with that, TIA.

Objects i use:

internal sealed class FacebookValidationRequest
{
public string access_token;
public fbReq[] batch;
public string method;
public string format;
public int pretty;
public int suppress_http_code;
public string debug;

public FacebookValidationRequest(string appId, string userToken)
{
access_token = userToken;
batch = new[]
{
//test code
new fbReq("GET", "me"),
new fbReq("GET", "me/friends?limit=50") //,
//new fbReq("GET", "app?access_token=" + userToken)
};
method = "post";
format = "json";
pretty = 0;
suppress_http_code = 1;
debug = "all";
}

}

internal sealed class fbReq
{
public string method;
public string relative_url;

public fbReq(string m, string url)
{
method = m;
relative_url = url;
}
}

FacebookValidationRequest req = new FacebookValidationRequest(appToken, userToken);


Also, took the token for the facebook debugger site

how facebook wants to object to look like after encoding:


access_token=mytoken&batch=%5B%7B%22method%22%3A%22GET%22%2C%20%22relative_url%22%3A%22me%22%7D%2C%7B%22method%22%3A%22GET%22%2C%20%22relative_url%22%3A%22me%2Ffriends%3Flimit%3D50%22%7D%5D&debug=all&fields=id%2Cname&format=json&method=post&pretty=0&suppress_http_code=1

Answer

Seems to me that the easiest way to do this is with Attributes to describe your properties, just like how the .Net Json's DataContract system does it. Basically, you assign an attribute to each property you want serialized, and make that attribute contain the name to serialize it as. I don't think you want to get into the mess of actually writing your own DataContractSerializer, though, so it might be easier to simply create your own Property class and a simple serializer using reflection.

The attribute class:

[AttributeUsage(AttributeTargets.Property)]
public sealed class UrlEncodeAttribute : System.Attribute
{
    public String Name { get; private set; }

    public UrlEncodeAttribute(String name)
    {
        this.Name = name;
    }
}

Then, to apply to your data class... put the attributes on all properties:

internal sealed class FacebookValidationRequest
{
    [UrlEncodeAttribute("access_token")]
    public String AccessToken { get; set; }
    [UrlEncodeAttribute("method")]
    public String Method { get; set; }
    [UrlEncodeAttribute("format")]
    public String Format { get; set; }
    [UrlEncodeAttribute("pretty")]
    public Int32 Pretty { get; set; }
    [UrlEncodeAttribute("suppress_http_code")]
    public Int32 SuppressHttpCode { get; set; }
    [UrlEncodeAttribute("debug")]
    public string Debug { get; set; }

    public fbReq[] Batch { get; set; }

    [UrlEncodeAttribute("batch")]
    public String BatchString
    {
        get
        {
            // put your json serialization code here to return
            // the contents of Batch as json string.
        }
    }
}

As you see, Batch does not have the UrlEncodeAttribute, while its string representation BatchString does. Its get is what will be called by the serializer, so you can put the conversion code in there.

Also note that thanks to the text names you give in the attributes, your properties don't need to have the names you actually get in the serialization, which looks much cleaner in my opinion. C#'s own serialization to xml and json works in the same way.

Now, let's take a look at the actual serialization, using reflection to get those properties:

public static String Serialize(Object obj, Boolean includeEmpty)
{
    // go over the properties, see which ones have a UrlEncodeAttribute, and process them.
    StringBuilder sb = new StringBuilder();
    PropertyInfo[] properties = obj.GetType().GetProperties();
    foreach (PropertyInfo p in properties)
    {
        object[] attrs = p.GetCustomAttributes(true);
        foreach (Object attr in attrs)
        {
            if (attr is UrlEncodeAttribute)
            {
                UrlEncodeAttribute fldAttr = ((UrlEncodeAttribute)attr);
                String objectName = fldAttr.Name;
                Object objectDataObj = p.GetValue(obj, null);
                String objectData = objectDataObj == null ? String.Empty : objectDataObj.ToString();
                if (!String.IsNullOrEmpty(objectData) || includeEmpty)
                {
                    objectData = HttpUtility.UrlEncode(objectData);
                    objectName= HttpUtility.UrlEncode(objectName);
                    if (sb.Length > 0)
                        sb.Append("&");
                    sb.Append(objectName).Append("=").Append(objectData);
                }
                break; // Only handle one UrlEncodeAttribute per property.
            }
        }
    }
    return sb.ToString();
}

A more advanced version of this could be made by including a serialization method property in the UrlEncodeAttribute class (probably best done with an enum), so you can simply specify to serialize the array on the fly using json. You'll obviously need to put the actual json converter into the Serialize function then. I thought using the getter on a dummy property as preparation method was simpler, here.

Obviously, calling it is simply this: (assuming here the Serialize() function is in a class called UrlEncodeSerializer)

FacebookValidationRequest fbreq = new FacebookValidationRequest();
// fill your data into fbreq here
// ...

// includeEmpty is set to true for testing here, but normally in
// UrlEncoded any missing property is just seen as empty anyway, so
// there should be no real difference.
String serialized = UrlEncodeSerializer.Serialize(fbreq, true);
Comments