Today, I had a task to submit data to an PHP API that takes Json data via HTTP POST using ContentType "application/x-www-form-urlencoded" and format like:
order[phone]=+48733552233&order[name]=First Name Last Name&order[deliveryCost]=50&order[deliveryStockCode]=39931b80-e1c2-11e3-8c4a-0050568002cf&order[comment]=test api&orderItems[0][itemID]=194559-0&orderItems[0][salePrice]=9500&orderItems[0][count]=2&orderItems[1][itemID]=071402-0&orderItems[1][salePrice]=750&orderItems[1][count]=5&key=777777777777777
Hah, crazy right? I have never seen such crazy things before and I had no chance except just take it and create myself.
Google did help me a little bit but in my case there were arrays of objects so I had to modify and extend a code I did find on internet.
So, to transform this:
var json = new
{
key = apiKey,
order = new
{
phone = PhoneNumber,
name = LastName + " " + FirstName,
comment = Comment,
deliveryCost = Math.Round(OrderItems.Sum(s => s.AdditionalShippingCharge), 0, MidpointRounding.ToEven).ToString(),
deliveryStockCode = Address
},
orderItems = OrderItems.Select(s => new
{
itemID = s.ProdId,
salePrice = Math.Round(s.Price, 0, MidpointRounding.ToEven),
count = s.Quantity
}).ToArray()
};
into the form data like I shown before, here is my method (it is rough so I'm sure it is not perfect but I had no time to create it cool and clean and may be will re-write it in the future).
Also, in my case , there were anonymous types so it had to identify them somehow, but as you may know already, anonymous types in C# has no explicit and compilation time type declaration to get its typeof().
public string JsonToHttpFormString(object request, string separator = ",")
{
if (request == null)
throw new ArgumentNullException("request");
// Get all properties on the object
var properties = request.GetType().GetProperties()
.Where(x => x.CanRead)
.Where(x => x.GetValue(request, null) != null)
.ToDictionary(x => x.Name, x => x.GetValue(request, null));
// Get names for all IEnumerable properties (excl. string)
var propertyNames = properties
.Where(x => !(x.Value is string) && ((x.Value is IEnumerable) || (x.Value != null && x.Value.GetType().IsConstructedGenericType && x.Value.GetType().Name.Contains("AnonymousType"))))
.Select(x => x.Key)
.ToList();
// Concat all IEnumerable properties into a comma separated string
bool isAnonym = false;
foreach (var key in propertyNames)
{
var valueType = properties[key].GetType();
var valueElemType = valueType.IsGenericType
? valueType.GetGenericArguments()[0]
: valueType.GetElementType();
isAnonym = valueType.Name.Contains("AnonymousType");
if (valueElemType.IsPrimitive || valueElemType == typeof(string) || isAnonym)
{
var enumerable = properties[key] as IEnumerable;
if (isAnonym && !valueType.IsArray)
{
List<string> tempvs = new List<string>();
var item = properties[key];
// Get all properties on the object
var properties2 = item.GetType().GetProperties()
.Where(x => x.CanRead)
.Where(x => x.GetValue(item, null) != null)
.ToDictionary(x => x.Name, x => x.GetValue(item, null));
foreach (var kkey in properties2)
{
var valueType2 = kkey.GetType();
var valueElemType2 = valueType2.IsGenericType
? valueType2.GetGenericArguments()[0]
: valueType2.GetElementType();
if (valueElemType2.IsPrimitive || valueElemType2 == typeof(string))
{
tempvs.Add(HttpUtility.UrlEncode(key + "[" + kkey.Key + "]") + "=" + HttpUtility.UrlEncode(kkey.Value.ToString()));
}
}
properties.Remove(key);
properties.Add(string.Join("&", tempvs), string.Empty);
tempvs.Clear();
tempvs = null;
}
else if (isAnonym && valueType.IsArray)
{
int i = 0;
List<string> tempvs = new List<string>();
foreach (var item in enumerable)
{
// Get all properties on the object
var properties2 = item.GetType().GetProperties()
.Where(x => x.CanRead)
.Where(x => x.GetValue(item, null) != null)
.ToDictionary(x => x.Name, x => x.GetValue(item, null));
foreach (var kkey in properties2)
{
var valueType2 = kkey.GetType();
var valueElemType2 = valueType2.IsGenericType
? valueType2.GetGenericArguments()[0]
: valueType2.GetElementType();
if (valueElemType2.IsPrimitive || valueElemType2 == typeof(string))
{
tempvs.Add(HttpUtility.UrlEncode(key + "[" + i + "][" + kkey.Key + "]") + "=" + HttpUtility.UrlEncode(kkey.Value.ToString()));
}
}
i++;
}
properties.Remove(key);
properties.Add(string.Join("&", tempvs), string.Empty);
tempvs.Clear();
tempvs = null;
}
else
{
properties[key] = string.Join(separator, enumerable.Cast<object>());
}
}
}
// Concat all key/value pairs into a string separated by ampersand and remove trailing '='
string res = string.Join("&", properties
.Select(x => string.Concat(
string.IsNullOrEmpty(x.Value.ToString()) ? x.Key : HttpUtility.UrlEncode(x.Key), "=",
HttpUtility.UrlEncode(x.Value.ToString())).TrimEnd('='))).TrimEnd('=');
return res;
}
Also, it has to do URL encoding to get properly formatted data for form submission style and os we finally could get something like that:
order%5Bphone%5D=%2B+44+%28733%29+55-22-33& order%5Bname%5D=%D0%98%D0%BC%D1%8F+%D0%BF%D0%BE%D0%BB%D1%83% D1%87%D0%B0%D1%82%D0%B5%D0%BB%D1%8F& order%5BdeliveryCost%5D=50&order%5BdeliveryStockCode%5D=39931b80-e1c2-11e3-8c4a-0050568002cf& order%5Bcomment%5D=test+api&orderItems%5B0%5D%5BitemID%5D=194559-0&orderItems%5B0%5D%5BsalePrice%5D=9500&orderItems%5B0%5D%5Bcount%5D=2& orderItems%5B1%5D%5BitemID%5D=071402-0&orderItems%5B1%5D%5BsalePrice%5D=750&orderItems%5B1%5D%5Bcount%5D=5&key=777777777777777
Thank you for reading and see you :)

1vqHSTrq1GEoEF7QsL8dhmJfRMDVxhv2y