If you want to send some URL encoded content to an API you can use the built in FormUrlEncodedContent to convert an IEnumerable<KeyValuePair<string, string>> to a correctly coded content object.

The most basic way to get the required KeyValuePair IEnumerable is to manually create it.

FormUrlEncodedContent content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
    ["client_id"] = authentication.ClientId,
    ["client_secret"] = authentication.ClientSecret,
    ["scope"] = authentication.Scope,
    ["code"] = authentication.Code,
    ["grant_type"] = authentication.GrantType,
    ["redirect_uri"] = authentication.RedirectUri
});

As this is quite a manual process a better option is an extension method to serialize an object into the required KeyValuePair IEnumerable. This can either be done by using reflection on the objects properties or by converting the object to JSON and processing the resulting tokens.

ToDictionary

This extension is based on that provided here.

public static class ObjectExtensions
{
    public static IDictionary<string, object> ToDictionary(this object source)
    {
        return source.ToDictionary<object>();
    }

    public static IDictionary<string, T> ToDictionary<T>(this object source)
    {
        if (source == null) ThrowExceptionWhenSourceArgumentIsNull();

        var dictionary = new Dictionary<string, T>();
        foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(source))
        {
            object value = property.GetValue(source);
            if (IsOfType<T>(value))
            {
                dictionary.Add(property.Name, (T)value);
            }
        }
        return dictionary;
    }

    private static bool IsOfType<T>(object value)
    {
        return value is T;
    }

    private static void ThrowExceptionWhenSourceArgumentIsNull()
    {
        throw new NullReferenceException("Unable to convert anonymous object to a dictionary. The source anonymous object is null.");
    }
}

This produces a Dictionary rather than an IEnumerable<KeyValuePair<string, string>> so it needs to be converted before using.

FormUrlEncodedContent content = new FormUrlEncodedContent(authentication.ToDictionary<string>().AsEnumerable());

ToKeyValue

This extension is based on that provided here.

public static class ObjectExtensions
{
	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 } };
	}
}

This extension method has the advantage of using any JsonProperty decorations you might have on the object model so that you can control the key names.

FormUrlEncodedContent content = new FormUrlEncodedContent(authentication.ToKeyValue());

Leave a Reply

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