CSharp · Console
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters;
using System.IO;
using System.Data;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Reflection;
using System.Collections.ObjectModel;
using System.Web;
using System.Collections.Specialized;
using System.Dynamic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using NUnit.Framework; // Throws NUnit.Framework.AssertionException
//https://stackoverflow.com/questions/29710688/how-can-i-convert-a-json-string-to-url-parameters-get-request
public static partial class JsonExtensions
{
public static string ToUrlEncodedQueryString(this JContainer container)
{
return container.ToQueryStringKeyValuePairs().ToUrlEncodedQueryString();
}
public static IEnumerable<KeyValuePair<string, string>> ToQueryStringKeyValuePairs(this JContainer container)
{
return container.Descendants()
.OfType<JValue>()
.Select(v => new KeyValuePair<string, string>(v.ToQueryStringParameterName(), (string)v));
}
public static string ToUrlEncodedQueryString(this IEnumerable<KeyValuePair<string, string>> pairs)
{
return string.Join("&", pairs.Select(p => HttpUtility.UrlEncode(p.Key) + "=" + HttpUtility.UrlEncode(p.Value)));
//The following works but it seems heavy to construct and await a task just to built a string:
//return new System.Net.Http.FormUrlEncodedContent(pairs).ReadAsStringAsync().Result;
//The following works and eliminates allocation of one intermediate string per pair, but requires more code:
//return pairs.Aggregate(new StringBuilder(), (sb, p) => (sb.Length > 0 ? sb.Append("&") : sb).Append(HttpUtility.UrlEncode(p.Key)).Append("=").Append(HttpUtility.UrlEncode(p.Value))).ToString();
//Answers from https://stackoverflow.com/questions/3865975/namevaluecollection-to-url-query that use HttpUtility.ParseQueryString() are wrong because that class doesn't correctly escape the keys names.
}
public static string ToQueryStringParameterName(this JToken token)
{
// Loosely modeled on JToken.Path
// https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Linq/JToken.cs#L184
// By https://github.com/JamesNK
if (token == null || token.Parent == null)
return string.Empty;
var positions = new List<string>();
for (JToken previous = null, current = token; current != null; previous = current, current = current.Parent)
{
switch (current)
{
case JProperty property:
positions.Add(property.Name);
break;
case JArray array:
case JConstructor constructor:
if (previous != null)
positions.Add(((IList<JToken>)current).IndexOf(previous).ToString(CultureInfo.InvariantCulture)); // Don't localize the indices!
break;
}
}
var sb = new StringBuilder();
for (var i = positions.Count - 1; i >= 0; i--)
{
var name = positions[i];
// TODO: decide what should happen if the name contains the characters `[` or `]`.
if (sb.Length == 0)
sb.Append(name);
else
sb.Append('[').Append(name).Append(']');
}
return sb.ToString();
}
}
class TestClass
{
public static void Test()
{
var jsonString = GetJson();
TestJsonString(jsonString);
var model = JsonConvert.DeserializeObject<ExpandoObject>(jsonString);
TestDataModel(model);
Console.WriteLine("Desired query string:");
Console.WriteLine(GetDesiredFormUrlEncodedString());
PrintKeysAndValues2(HttpUtility.ParseQueryString(GetDesiredFormUrlEncodedString()));
}
static void TestJsonString(string jsonString)
{
Console.WriteLine("\nTesting JSON string...");
var obj = JObject.Parse(jsonString);
var queryString = obj.ToUrlEncodedQueryString();
Console.WriteLine("Generated query string: ");
Console.WriteLine(queryString);
PrintKeysAndValues2(HttpUtility.ParseQueryString(queryString));
Assert.IsTrue(GetDesiredFormUrlEncodedString().Equals(queryString, StringComparison.OrdinalIgnoreCase));
}
static void TestDataModel<T>(T myModel)
{
Console.WriteLine("\nTesting model {0}...", myModel);
var obj = JObject.FromObject(myModel);
var queryString = obj.ToUrlEncodedQueryString();
Console.WriteLine("Generated query string: ");
Console.WriteLine(queryString);
PrintKeysAndValues2(HttpUtility.ParseQueryString(queryString));
Assert.IsTrue(GetDesiredFormUrlEncodedString().Equals(queryString, StringComparison.OrdinalIgnoreCase));
}
public static void PrintKeysAndValues2( NameValueCollection myCol )
{
// Modified from https://docs.microsoft.com/en-us/dotnet/api/system.collections.specialized.namevaluecollection?view=netcore-3.1#examples
Console.WriteLine("{");
for ( int i = 0; i < myCol.Count; i++ )
Console.WriteLine(" {{ {0}, {1} }} ", JsonConvert.SerializeObject(myCol.GetKey(i)), JsonConvert.SerializeObject(myCol.Get(i)));
Console.WriteLine("}\n");
}
static string GetDesiredFormUrlEncodedString()
{
return @"foo=bar&purchase_invoice%5Bdate%5D=14%2F04%2F2015&purchase_invoice%5Bdue_date%5D=14%2F04%2F2015&purchase_invoice%5Bcontact_id%5D=500&purchase_invoice%5Bcontact_name%5D=TestContact&purchase_invoice%5Breference%5D=TestReference&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bunit_price%5D=10&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bquantity%5D=1&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bdescription%5D=TestLineItemAttDesc&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_code_id%5D=1&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bledger_account_id%5D=501&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_rate_percentage%5D=19&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_amount%5D=1.6";
}
static string GetJson()
{
return @"{
""foo"":""bar"",
""purchase_invoice"":
{
""date"":""14/04/2015"",
""due_date"":""14/04/2015"",
""contact_id"":500,
""contact_name"":""TestContact"",
""reference"":""TestReference"",
""line_items_attributes"":[
{
""unit_price"":10.00,
""quantity"":1,
""description"":""TestLineItemAttDesc"",
""tax_code_id"":1,
""ledger_account_id"":501,
""tax_rate_percentage"":19.0,
""tax_amount"":1.60
}]
}
}";
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Assembly.GetName().Name, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine();
try
{
TestClass.Test();
}
catch (Exception ex)
{
Console.WriteLine("Failed with unhandled exception: ");
Console.WriteLine(ex);
throw;
}
}
public static string GetNetCoreVersion()
{
//https://techblog.dorogin.com/how-to-detect-net-core-version-in-runtime-ecd65ad695be
//
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];
return null;
}
}