using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string Error { get; set; }
public string DisplayName { get; set; }
public string Value { get; set; }
public List<Page> pages { get; set; }
public List<Label> labels { get; set; }
public class PolymorphicArrayToObjectConverter<TObject> : JsonConverter
public override bool CanConvert(Type objectType)
return typeof(TObject).IsAssignableFrom(objectType);
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
throw new NotImplementedException();
static JsonObjectContract FindContract(JObject obj, IEnumerable<Type> derivedTypes, JsonSerializer serializer)
List<JsonObjectContract> bestContracts = new List<JsonObjectContract>();
foreach (var type in derivedTypes)
var contract = serializer.ContractResolver.ResolveContract(type) as JsonObjectContract;
if (obj.Properties().Select(p => p.Name).Any(n => contract.Properties.GetClosestMatchProperty(n) == null))
if (bestContracts.Count == 0 || bestContracts[0].Properties.Count > contract.Properties.Count)
bestContracts.Add(contract);
else if (contract.Properties.Count == bestContracts[0].Properties.Count)
bestContracts.Add(contract);
return bestContracts.Single();
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
if (reader.TokenType == JsonToken.Null)
else if (reader.TokenType != JsonToken.StartArray)
throw new InvalidOperationException("JSON token is not an array at path: " + reader.Path);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
existingValue = existingValue ?? contract.DefaultCreator();
.Select(p => new { Property = p, PropertyContract = serializer.ContractResolver.ResolveContract(p.PropertyType) as JsonArrayContract })
.Where(i => i.PropertyContract != null)
.ToDictionary(i => i.PropertyContract.CollectionItemType);
var types = lookup.Select(i => i.Key).ToList();
switch (reader.TokenType)
var itemObj = JObject.Load(reader);
var itemContract = FindContract(itemObj, types, serializer);
if (itemContract == null)
var item = serializer.Deserialize(itemObj.CreateReader(), itemContract.UnderlyingType);
var propertyData = lookup[itemContract.UnderlyingType];
var collection = propertyData.Property.ValueProvider.GetValue(existingValue);
collection = propertyData.PropertyContract.DefaultCreator();
propertyData.Property.ValueProvider.SetValue(existingValue, collection);
collection.GetType().GetMethod("Add").Invoke(collection, new [] { item });
throw new JsonSerializationException("Unclosed array at path: " + reader.Path);
public static void Test()
var jsonString = GetJson();
var settings = new JsonSerializerSettings
Converters = { new PolymorphicArrayToObjectConverter<RootObject>() },
var root = new List<RootObject> { JsonConvert.DeserializeObject<RootObject>(jsonString, settings) };
var outputJson = JsonConvert.SerializeObject(root, Formatting.Indented);
Console.WriteLine("Result of deserializing and re-serializing the JSON: ");
Console.WriteLine(outputJson);
Assert.IsTrue(JToken.DeepEquals(JToken.Parse(outputJson), JToken.Parse(GetDesiredJson())));
Console.WriteLine("Result is identical to desired JSON.");
""FirstName"": ""Test1"",
""Address"": ""London, GB"",
""Error"": ""Something's gone wrong""
""FirstName"": ""Test3"",
""Address"": ""NewYork, US"",
""Error"": ""Something's gone wrong""
""DisplayName"": ""ContactNumber"",
""Value"": ""01234 123 123""
static string GetDesiredJson()
""FirstName"": ""Test1"",
""Address"": ""London, GB"",
""Error"": ""Something's gone wrong""
""FirstName"": ""Test3"",
""Address"": ""NewYork, US"",
""Error"": ""Something's gone wrong""
""DisplayName"": ""ContactNumber"",
""Value"": ""01234 123 123""
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");