using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.ComponentModel;
using System.Globalization;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public class RowColumnListConverter<T> : JsonConverter
const string columnsKey = "columns";
const string rowsKey = "rows";
public override bool CanConvert(Type objectType)
if (!typeof(ICollection<T>).IsAssignableFrom(objectType))
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
if (reader.TokenType == JsonToken.Null)
var list = existingValue as ICollection<T> ?? (ICollection<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
var root = JObject.Load(reader);
var rows = root.GetValue(rowsKey, StringComparison.OrdinalIgnoreCase).NullCast<JArray>();
var columns = root.GetValue(columnsKey, StringComparison.OrdinalIgnoreCase).NullCast<JArray>();
throw new JsonSerializationException(columnsKey + " not found.");
var columnNames = columns.Cast<JObject>().SelectMany(o => o.Properties()).Select(p => p.Name).ToArray();
foreach (var row in rows)
if (row == null || row.Type == JTokenType.Null)
else if (row.Type == JTokenType.Array)
var o = new JObject(columnNames.Zip(row, (c, r) => new JProperty(c, r)));
list.Add(o.ToObject<T>(serializer));
throw new JsonSerializationException(string.Format("Row was not an array: {0}", row));
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
throw new NotImplementedException();
public static class JsonExtensions
public static TJTOken NullCast<TJTOken>(this JToken token) where TJTOken : JToken
if (token == null || token.Type == JTokenType.Null)
var typedToken = token as TJTOken;
throw new JsonSerializationException(string.Format("Token {0} was not of type {1}", token.ToString(Formatting.None), typeof(TJTOken)));
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public DateTime? last_updated { get; set; }
public static void Test()
Console.WriteLine("Input JSON: ");
var list = JsonConvert.DeserializeObject<List<Record>>(json, new JsonSerializerSettings
Converters = { new RowColumnListConverter<Record>() },
var json2 = JsonConvert.SerializeObject(list, Formatting.Indented);
Console.WriteLine("Re-serialized list: ");
Console.WriteLine(json2);
{ ""id"": { ""type"": ""Numeric"", ""nullable"": false } },
{ ""name"": { ""type"": ""Text"", ""nullable"": false } },
{ ""description"": { ""type"": ""Text"", ""nullable"": true } },
{ ""last_updated"": { ""type"": ""DateTime"", ""nullable"": false } }
[1, ""foo"", ""Lorem ipsum"", ""2016-10-26T00:09:14Z""],
[4, ""bar"", null, ""2013-07-01T13:04:24Z""]
public static void Main()
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);