questions/45777779/parsing-multidimensional-json-array-with-name-value-pairs-using-newtonsoft-json

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 Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;	

    // https://stackoverflow.com/questions/45777779/parsing-multidimensional-json-array-with-name-value-pairs-using-newtonsoft-json
    public class CoordPoint
    {
        public int row { get; set; }
        public int col { get; set; }
    }

    public class CoordPointsArray
    {
        [JsonProperty(Order = 1)]
        public string Name { get; set; }

        [JsonProperty(Order = 2)]
        public List<CoordPoint> Coordinates { get; set; }
    }

    public class ObjectListToSequentialPropertyArrayConverter<T> : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(T) == objectType.GetListType();
        }

        static bool ShouldSkip(JsonProperty property)
        {
            return property.Ignored || !property.Readable || !property.Writable;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var objectType = value.GetType();
            var itemType = objectType.GetListType();
            if (itemType == null)
                throw new ArgumentException(objectType.ToString());
            var itemContract = serializer.ContractResolver.ResolveContract(itemType) as JsonObjectContract;
            if (itemContract == null)
                throw new JsonSerializationException("invalid type " + objectType.FullName);
            var list = (IList)value;
            var propertyList = list
                .OfType<Object>()
                .Where(i => i != null) // Or should we throw an exception?
                .SelectMany(i => itemContract.Properties.Where(p => !ShouldSkip(p)).Select(p => p.ValueProvider.GetValue(i)));
            serializer.Serialize(writer, propertyList);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var itemType = objectType.GetListType();
            if (itemType == null)
                throw new ArgumentException(objectType.ToString());
            if (reader.TokenType == JsonToken.Null)
                return null;
            var array = JArray.Load(reader);
            var listContract = serializer.ContractResolver.ResolveContract(objectType) as JsonArrayContract;
            var itemContract = serializer.ContractResolver.ResolveContract(itemType) as JsonObjectContract;
            if (itemContract == null || listContract == null)
                throw new JsonSerializationException("invalid type " + objectType.FullName);
            var list = existingValue as IList ?? (IList)listContract.DefaultCreator();
            var properties = itemContract.Properties.Where(p => !ShouldSkip(p)).ToArray();

            for (int startIndex = 0; startIndex < array.Count; startIndex += properties.Length)
            {
                var item = itemContract.DefaultCreator();
                for (int iProperty = 0; iProperty < properties.Length; iProperty++)
                {
                    if (startIndex + iProperty >= array.Count)
                        break;
                    var propertyValue = array[startIndex + iProperty].ToObject(properties[iProperty].PropertyType, serializer);
                    properties[iProperty].ValueProvider.SetValue(item, propertyValue);
                }
                list.Add(item);
            }
            return list;
        }
    }

    public static class TypeExtensions
    {
        public static Type GetListType(this Type type)
        {
            while (type != null)
            {
                if (type.IsGenericType)
                {
                    var genType = type.GetGenericTypeDefinition();
                    if (genType == typeof(List<>))
                        return type.GetGenericArguments()[0];
                }
                type = type.BaseType;
            }
            return null;
        }
    }

    class TestClass
    {
        public static void Test()
        {
            var json = "[\"ship5\",[{\"row\":4,\"col\":6},{\"row\":5,\"col\":6},{\"row\":6,\"col\":6}],\"ship4\",[{\"row\":3,\"col\":8},{\"row\":3,\"col\":9},{\"row\":3,\"col\":10},{\"row\":3,\"col\":11}],\"ship3\",[{\"row\":8,\"col\":2},{\"row\":8,\"col\":3},{\"row\":8,\"col\":4},{\"row\":8,\"col\":5}],\"ship2\",[{\"row\":9,\"col\":7},{\"row\":9,\"col\":8},{\"row\":9,\"col\":9},{\"row\":9,\"col\":10},{\"row\":9,\"col\":11}],\"ship1\",[{\"row\":0,\"col\":0},{\"row\":0,\"col\":1},{\"row\":0,\"col\":2},{\"row\":0,\"col\":3},{\"row\":0,\"col\":4},{\"row\":0,\"col\":5}],\"ship0\",[{\"row\":12,\"col\":5},{\"row\":12,\"col\":6},{\"row\":12,\"col\":7},{\"row\":12,\"col\":8},{\"row\":12,\"col\":9},{\"row\":13,\"col\":5},{\"row\":13,\"col\":6},{\"row\":13,\"col\":7},{\"row\":13,\"col\":8},{\"row\":13,\"col\":9}]]";
			Console.WriteLine("Input JSON: ");
			Console.WriteLine(json);
			
            var settings = new JsonSerializerSettings
            {
                Converters = { new ObjectListToSequentialPropertyArrayConverter<CoordPointsArray>() },
            };

            var root = JsonConvert.DeserializeObject<List<CoordPointsArray>>(json, settings);

            var json2 = JsonConvert.SerializeObject(root, Formatting.Indented, settings);

            var ok = JToken.DeepEquals(JToken.Parse(json), JToken.Parse(json2));

            Console.WriteLine(ok ? "Original and re-serialized JSON are equivalent." : "Original and re-serialized JSON are NOT equivalent!");

            Console.WriteLine("Re-serialized JSON: ");
			
			var jsonFormatted = CustomFormatArray(JArray.Parse(json2));
			
			Console.WriteLine(jsonFormatted);			
			
			Assert.IsTrue(ok);
			
			Assert.IsTrue(JToken.DeepEquals(JToken.Parse(jsonFormatted), JToken.Parse(json)));
        }
		
		static string CustomFormatArray(JArray array)
		{
			var sb = new StringBuilder();
			sb.AppendLine("[");
			for (int i = 0; i < array.Count; i++)
			{
				sb.Append("  ");
				sb.Append(array[i].ToString(Formatting.None));
				if (i < array.Count - 1)
					sb.Append(",");
				sb.AppendLine();
			}
			sb.Append("]");
			return sb.ToString();
		}
    }

	public class Program
	{
		public static void Main()
		{
			Console.WriteLine("Environment version: " + Environment.Version);
			Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
			Console.WriteLine();

			try
			{
				TestClass.Test();
			}
			catch (Exception ex)
			{
				Console.WriteLine("Failed with unhandled exception: ");
				Console.WriteLine(ex);
				throw;
			}
		}
	}

    public class AssertionFailedException : System.Exception
    {
        public AssertionFailedException() : base() { }

        public AssertionFailedException(string s) : base(s) { }
    }

    public static class Assert
    {
        public static void IsTrue(bool value)
        {
            IsTrue(value, "failed");
        }

        public static void IsTrue(bool value, string message)
        {
            if (value == false)
                throw new AssertionFailedException(message ?? "failed");
        }
    }
;