https://stackoverflow.com/questions/29710688/how-can-i-convert-a-json-string-to-url-parameters-get-request

CSharp · Console

/Program.cs

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;
		}
	}
;