DataTableConverter
Converts a  DataTable to and from JSON.
            
                using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Utilities;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Runtime.CompilerServices;
namespace Newtonsoft.Json.Converters
{
    [System.Runtime.CompilerServices.NullableContext(1)]
    [System.Runtime.CompilerServices.Nullable(0)]
    public class DataTableConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, [System.Runtime.CompilerServices.Nullable(2)] object value, JsonSerializer serializer)
        {
            if (value == null)
                writer.WriteNull();
            else {
                DataTable obj = (DataTable)value;
                DefaultContractResolver defaultContractResolver = serializer.ContractResolver as DefaultContractResolver;
                writer.WriteStartArray();
                foreach (DataRow row in obj.Rows) {
                    writer.WriteStartObject();
                    foreach (DataColumn column in row.Table.Columns) {
                        object obj2 = row[column];
                        if (serializer.NullValueHandling != NullValueHandling.Ignore || (obj2 != null && obj2 != DBNull.Value)) {
                            writer.WritePropertyName((defaultContractResolver != null) ? defaultContractResolver.GetResolvedPropertyName(column.ColumnName) : column.ColumnName);
                            serializer.Serialize(writer, obj2);
                        }
                    }
                    writer.WriteEndObject();
                }
                writer.WriteEndArray();
            }
        }
        [return: System.Runtime.CompilerServices.Nullable(2)]
        public override object ReadJson(JsonReader reader, Type objectType, [System.Runtime.CompilerServices.Nullable(2)] object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null)
                return null;
            DataTable dataTable = existingValue as DataTable;
            if (dataTable == null)
                dataTable = (((object)objectType == typeof(DataTable)) ? new DataTable() : ((DataTable)Activator.CreateInstance(objectType)));
            if (reader.TokenType == JsonToken.PropertyName) {
                dataTable.TableName = (string)reader.Value;
                reader.ReadAndAssert();
                if (reader.TokenType == JsonToken.Null)
                    return dataTable;
            }
            if (reader.TokenType != JsonToken.StartArray)
                throw JsonSerializationException.Create(reader, "Unexpected JSON token when reading DataTable. Expected StartArray, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
            reader.ReadAndAssert();
            while (reader.TokenType != JsonToken.EndArray) {
                CreateRow(reader, dataTable, serializer);
                reader.ReadAndAssert();
            }
            return dataTable;
        }
        private static void CreateRow(JsonReader reader, DataTable dt, JsonSerializer serializer)
        {
            DataRow dataRow = dt.NewRow();
            reader.ReadAndAssert();
            while (reader.TokenType == JsonToken.PropertyName) {
                string text = (string)reader.Value;
                reader.ReadAndAssert();
                DataColumn dataColumn = dt.Columns[text];
                if (dataColumn == null) {
                    Type columnDataType = GetColumnDataType(reader);
                    dataColumn = new DataColumn(text, columnDataType);
                    dt.Columns.Add(dataColumn);
                }
                if ((object)dataColumn.DataType == typeof(DataTable)) {
                    if (reader.TokenType == JsonToken.StartArray)
                        reader.ReadAndAssert();
                    DataTable dataTable = new DataTable();
                    while (reader.TokenType != JsonToken.EndArray) {
                        CreateRow(reader, dataTable, serializer);
                        reader.ReadAndAssert();
                    }
                    dataRow[text] = dataTable;
                } else if (!dataColumn.DataType.IsArray || (object)dataColumn.DataType == typeof(byte[])) {
                    object obj2 = dataRow[text] = ((reader.Value != null) ? (serializer.Deserialize(reader, dataColumn.DataType) ?? DBNull.Value) : DBNull.Value);
                } else {
                    if (reader.TokenType == JsonToken.StartArray)
                        reader.ReadAndAssert();
                    List<object> list = new List<object>();
                    while (reader.TokenType != JsonToken.EndArray) {
                        list.Add(reader.Value);
                        reader.ReadAndAssert();
                    }
                    Array array = Array.CreateInstance(dataColumn.DataType.GetElementType(), list.Count);
                    ((ICollection)list).CopyTo(array, 0);
                    dataRow[text] = array;
                }
                reader.ReadAndAssert();
            }
            dataRow.EndEdit();
            dt.Rows.Add(dataRow);
        }
        private static Type GetColumnDataType(JsonReader reader)
        {
            JsonToken tokenType = reader.TokenType;
            switch (tokenType) {
            case JsonToken.Integer:
            case JsonToken.Float:
            case JsonToken.String:
            case JsonToken.Boolean:
            case JsonToken.Date:
            case JsonToken.Bytes:
                return reader.ValueType;
            case JsonToken.Null:
            case JsonToken.Undefined:
            case JsonToken.EndArray:
                return typeof(string);
            case JsonToken.StartArray:
                reader.ReadAndAssert();
                if (reader.TokenType == JsonToken.StartObject)
                    return typeof(DataTable);
                return GetColumnDataType(reader).MakeArrayType();
            default:
                throw JsonSerializationException.Create(reader, "Unexpected JSON token when reading DataTable: {0}".FormatWith(CultureInfo.InvariantCulture, tokenType));
            }
        }
        public override bool CanConvert(Type valueType)
        {
            return typeof(DataTable).IsAssignableFrom(valueType);
        }
    }
}