using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
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();
throw new ArgumentException(objectType.ToString());
var itemContract = serializer.ContractResolver.ResolveContract(itemType) as JsonObjectContract;
if (itemContract == null)
throw new JsonSerializationException("invalid type " + objectType.FullName);
.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();
throw new ArgumentException(objectType.ToString());
if (reader.TokenType == JsonToken.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)
var propertyValue = array[startIndex + iProperty].ToObject(properties[iProperty].PropertyType, serializer);
properties[iProperty].ValueProvider.SetValue(item, propertyValue);
public static class TypeExtensions
public static Type GetListType(this Type type)
var genType = type.GetGenericTypeDefinition();
if (genType == typeof(List<>))
return type.GetGenericArguments()[0];
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: ");
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(JToken.DeepEquals(JToken.Parse(jsonFormatted), JToken.Parse(json)));
static string CustomFormatArray(JArray array)
var sb = new StringBuilder();
for (int i = 0; i < array.Count; i++)
sb.Append(array[i].ToString(Formatting.None));
public static void Main()
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public class AssertionFailedException : System.Exception
public AssertionFailedException() : base() { }
public AssertionFailedException(string s) : base(s) { }
public static class Assert
public static void IsTrue(bool value)
public static void IsTrue(bool value, string message)
throw new AssertionFailedException(message ?? "failed");