#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

#if !(PORTABLE || DNXCORE50 || PORTABLE40) || NETSTANDARD2_0
using System.IO;
using System.Text;
using Newtonsoft.Json.Bson;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
#if DNXCORE50
using Xunit;
using Test = Xunit.FactAttribute;
using Assert = Newtonsoft.Json.Tests.XUnitAssert;
#else
using NUnit.Framework;
#endif
using System.Data;
using System.Data.SqlTypes;
using System.Linq;
using Newtonsoft.Json.Tests.TestObjects;
#if !(NET20 || NET35)
using System.Numerics;
#endif

namespace Newtonsoft.Json.Tests.Converters
{
    public class DataTableConverterTests : TestFixtureBase
    {
        [Test]
        public void DeserializeEmptyNestedArray()
        {
            var jsonString2 = @"[{""col1"": []}]";

            DataTable dt = JsonConvert.DeserializeObject<DataTable>(jsonString2);

            Assert.AreEqual(1, dt.Columns.Count);
            Assert.AreEqual(typeof(string[]), dt.Columns["col1"].DataType);

            Assert.AreEqual(1, dt.Rows.Count);
            Assert.IsNotNull(dt.Rows[0]["col1"]);

            object[] value = (object[])dt.Rows[0]["col1"];
            Assert.AreEqual(0, value.Length);
        }

#if !(NET20 || NET35)
        [Test]
        public void SerializeNullValues()
        {
            DataTable dt = new DataTable();
            List<Type> types = new List<Type>
            {
                typeof(TimeSpan),
                typeof(char[]),
                typeof(Type),
                typeof(Object),
                typeof(byte[]),
                typeof(Uri),
                typeof(Guid),
                typeof(BigInteger)
            };

            foreach (var ss in types)
            {
                dt.Columns.Add(ss.Name, ss);
            }

            dt.Rows.Add(types.Select(t => (object)null).ToArray());

            StringWriter sw = new StringWriter();
            JsonTextWriter jsonWriter = new JsonTextWriter(sw);
            jsonWriter.Formatting = Formatting.Indented;

            DataTableConverter converter = new DataTableConverter();
            converter.WriteJson(jsonWriter, dt, new JsonSerializer());

            StringAssert.AreEqual(@"[
  {
    ""TimeSpan"": null,
    ""Char[]"": null,
    ""Type"": null,
    ""Object"": null,
    ""Byte[]"": null,
    ""Uri"": null,
    ""Guid"": null,
    ""BigInteger"": null
  }
]", sw.ToString());
        }

        [Test]
        public void SerializeValues()
        {
            DataTable dt = new DataTable();
            Dictionary<Type, object> types = new Dictionary<Type, object>
            {
                [typeof(TimeSpan)] = TimeSpan.Zero,
                [typeof(char[])] = new char[] {'a', 'b', 'c' },
                [typeof(Type)] = typeof(string),
                [typeof(Object)] = new object(),
                [typeof(byte[])] = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 },
                [typeof(Uri)] = new Uri("http://localhost"),
                [typeof(Guid)] = new Guid(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
                [typeof(BigInteger)] = BigInteger.Parse("10000000000000000000000000000000000")
            };

            foreach (var ss in types)
            {
                dt.Columns.Add(ss.Key.Name, ss.Key);
            }

            dt.Rows.Add(types.Select(t => t.Value).ToArray());

            StringWriter sw = new StringWriter();
            JsonTextWriter jsonWriter = new JsonTextWriter(sw);
            jsonWriter.Formatting = Formatting.Indented;

            DataTableConverter converter = new DataTableConverter();
            converter.WriteJson(jsonWriter, dt, new JsonSerializer());

            string stringName = typeof(string).AssemblyQualifiedName;

            StringAssert.AreEqual(@"[
  {
    ""TimeSpan"": ""00:00:00"",
    ""Char[]"": [
      ""a"",
      ""b"",
      ""c""
    ],
    ""Type"": """ + stringName + @""",
    ""Object"": {},
    ""Byte[]"": ""AQIDBAUGBwg="",
    ""Uri"": ""http://localhost"",
    ""Guid"": ""00000001-0002-0003-0405-060708090a0b"",
    ""BigInteger"": 10000000000000000000000000000000000
  }
]", sw.ToString());
        }
#endif

        [Test]
        public void WriteJsonNull()
        {
            StringWriter sw = new StringWriter();
            JsonTextWriter jsonWriter = new JsonTextWriter(sw);

            DataTableConverter converter = new DataTableConverter();
            converter.WriteJson(jsonWriter, null, null);

            StringAssert.AreEqual(@"null", sw.ToString());
        }

        [Test]
        public void Deserialize()
        {
            string json = @"[
  {
    ""id"": 0,
    ""item"": ""item 0"",
    ""DataTableCol"": [
      {
        ""NestedStringCol"": ""0!""
      }
    ],
    ""ArrayCol"": [
      0
    ],
    ""DateCol"": ""2000-12-29T00:00:00Z""
  },
  {
    ""id"": 1,
    ""item"": ""item 1"",
    ""DataTableCol"": [
      {
        ""NestedStringCol"": ""1!""
      }
    ],
    ""ArrayCol"": [
      1
    ],
    ""DateCol"": ""2000-12-29T00:00:00Z""
  }
]";

            DataTable deserializedDataTable = JsonConvert.DeserializeObject<DataTable>(json);
            Assert.IsNotNull(deserializedDataTable);

            Assert.AreEqual(string.Empty, deserializedDataTable.TableName);
            Assert.AreEqual(5, deserializedDataTable.Columns.Count);
            Assert.AreEqual("id", deserializedDataTable.Columns[0].ColumnName);
            Assert.AreEqual(typeof(long), deserializedDataTable.Columns[0].DataType);
            Assert.AreEqual("item", deserializedDataTable.Columns[1].ColumnName);
            Assert.AreEqual(typeof(string), deserializedDataTable.Columns[1].DataType);
            Assert.AreEqual("DataTableCol", deserializedDataTable.Columns[2].ColumnName);
            Assert.AreEqual(typeof(DataTable), deserializedDataTable.Columns[2].DataType);
            Assert.AreEqual("ArrayCol", deserializedDataTable.Columns[3].ColumnName);
            Assert.AreEqual(typeof(long[]), deserializedDataTable.Columns[3].DataType);
            Assert.AreEqual("DateCol", deserializedDataTable.Columns[4].ColumnName);
            Assert.AreEqual(typeof(DateTime), deserializedDataTable.Columns[4].DataType);

            Assert.AreEqual(2, deserializedDataTable.Rows.Count);

            DataRow dr1 = deserializedDataTable.Rows[0];
            Assert.AreEqual(0L, dr1["id"]);
            Assert.AreEqual("item 0", dr1["item"]);
            Assert.AreEqual("0!", ((DataTable)dr1["DataTableCol"]).Rows[0]["NestedStringCol"]);
            Assert.AreEqual(0L, ((long[])dr1["ArrayCol"])[0]);
            Assert.AreEqual(new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc), dr1["DateCol"]);

            DataRow dr2 = deserializedDataTable.Rows[1];
            Assert.AreEqual(1L, dr2["id"]);
            Assert.AreEqual("item 1", dr2["item"]);
            Assert.AreEqual("1!", ((DataTable)dr2["DataTableCol"]).Rows[0]["NestedStringCol"]);
            Assert.AreEqual(1L, ((long[])dr2["ArrayCol"])[0]);
            Assert.AreEqual(new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc), dr2["DateCol"]);
        }

#if !NET20
        [Test]
        public void DeserializeParseHandling()
        {
            string json = @"[
  {
    ""DateCol"": ""2000-12-29T00:00:00Z"",
    ""FloatCol"": 99.9999999999999999999
  },
  {
    ""DateCol"": ""2000-12-29T00:00:00Z"",
    ""FloatCol"": 99.9999999999999999999
  }
]";

            DataTable deserializedDataTable = JsonConvert.DeserializeObject<DataTable>(json, new JsonSerializerSettings
            {
                DateParseHandling = DateParseHandling.DateTimeOffset,
                FloatParseHandling = FloatParseHandling.Decimal
            });
            Assert.IsNotNull(deserializedDataTable);

            Assert.AreEqual(string.Empty, deserializedDataTable.TableName);
            Assert.AreEqual(2, deserializedDataTable.Columns.Count);
            Assert.AreEqual("DateCol", deserializedDataTable.Columns[0].ColumnName);
            Assert.AreEqual(typeof(DateTimeOffset), deserializedDataTable.Columns[0].DataType);
            Assert.AreEqual("FloatCol", deserializedDataTable.Columns[1].ColumnName);
            Assert.AreEqual(typeof(decimal), deserializedDataTable.Columns[1].DataType);

            Assert.AreEqual(2, deserializedDataTable.Rows.Count);

            DataRow dr1 = deserializedDataTable.Rows[0];
            Assert.AreEqual(new DateTimeOffset(2000, 12, 29, 0, 0, 0, TimeSpan.Zero), dr1["DateCol"]);
            Assert.AreEqual(99.9999999999999999999m, dr1["FloatCol"]);

            DataRow dr2 = deserializedDataTable.Rows[1];
            Assert.AreEqual(new DateTimeOffset(2000, 12, 29, 0, 0, 0, TimeSpan.Zero), dr2["DateCol"]);
            Assert.AreEqual(99.9999999999999999999m, dr2["FloatCol"]);
        }
#endif

        [Test]
        public void Serialize()
        {
            // create a new DataTable.
            DataTable myTable = new DataTable("blah");

            // create DataColumn objects of data types.
            DataColumn colString = new DataColumn("StringCol");
            colString.DataType = typeof(string);
            myTable.Columns.Add(colString);

            DataColumn colInt32 = new DataColumn("Int32Col");
            colInt32.DataType = typeof(int);
            myTable.Columns.Add(colInt32);

            DataColumn colBoolean = new DataColumn("BooleanCol");
            colBoolean.DataType = typeof(bool);
            myTable.Columns.Add(colBoolean);

            DataColumn colTimeSpan = new DataColumn("TimeSpanCol");
            colTimeSpan.DataType = typeof(TimeSpan);
            myTable.Columns.Add(colTimeSpan);

            DataColumn colDateTime = new DataColumn("DateTimeCol");
            colDateTime.DataType = typeof(DateTime);
            colDateTime.DateTimeMode = DataSetDateTime.Utc;
            myTable.Columns.Add(colDateTime);

            DataColumn colDecimal = new DataColumn("DecimalCol");
            colDecimal.DataType = typeof(decimal);
            myTable.Columns.Add(colDecimal);

            DataColumn colDataTable = new DataColumn("DataTableCol");
            colDataTable.DataType = typeof(DataTable);
            myTable.Columns.Add(colDataTable);

            DataColumn colArray = new DataColumn("ArrayCol");
            colArray.DataType = typeof(int[]);
            myTable.Columns.Add(colArray);

            DataColumn colBytes = new DataColumn("BytesCol");
            colBytes.DataType = typeof(byte[]);
            myTable.Columns.Add(colBytes);

            // populate one row with values.
            DataRow myNewRow = myTable.NewRow();

            myNewRow["StringCol"] = "Item Name";
            myNewRow["Int32Col"] = 2147483647;
            myNewRow["BooleanCol"] = true;
            myNewRow["TimeSpanCol"] = new TimeSpan(10, 22, 10, 15, 100);
            myNewRow["DateTimeCol"] = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc);
            myNewRow["DecimalCol"] = 64.0021;
            myNewRow["ArrayCol"] = new[] { 1 };
            myNewRow["BytesCol"] = Encoding.UTF8.GetBytes("Hello world");

            DataTable nestedTable = new DataTable("Nested");
            DataColumn nestedColString = new DataColumn("NestedStringCol");
            nestedColString.DataType = typeof(string);
            nestedTable.Columns.Add(nestedColString);
            DataRow myNewNestedRow = nestedTable.NewRow();
            myNewNestedRow["NestedStringCol"] = "Nested!";
            nestedTable.Rows.Add(myNewNestedRow);

            myNewRow["DataTableCol"] = nestedTable;
            myTable.Rows.Add(myNewRow);

            string json = JsonConvert.SerializeObject(myTable, Formatting.Indented);
            StringAssert.AreEqual(@"[
  {
    ""StringCol"": ""Item Name"",
    ""Int32Col"": 2147483647,
    ""BooleanCol"": true,
    ""TimeSpanCol"": ""10.22:10:15.1000000"",
    ""DateTimeCol"": ""2000-12-29T00:00:00Z"",
    ""DecimalCol"": 64.0021,
    ""DataTableCol"": [
      {
        ""NestedStringCol"": ""Nested!""
      }
    ],
    ""ArrayCol"": [
      1
    ],
    ""BytesCol"": ""SGVsbG8gd29ybGQ=""
  }
]", json);
        }

        public class TestDataTableConverter : JsonConverter
        {
            public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
            {
                DataTable d = (DataTable)value;
                writer.WriteValue(d.TableName);
            }

            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            {
                //reader.Read();
                DataTable d = new DataTable((string)reader.Value);

                return d;
            }

            public override bool CanConvert(Type objectType)
            {
                return (objectType == typeof(DataTable));
            }
        }

        [Test]
        public void PassedInJsonConverterOverridesInternalConverter()
        {
            DataTable t1 = new DataTable("Custom");

            string json = JsonConvert.SerializeObject(t1, Formatting.Indented, new TestDataTableConverter());
            Assert.AreEqual(@"""Custom""", json);

            DataTable t2 = JsonConvert.DeserializeObject<DataTable>(json, new TestDataTableConverter());
            Assert.AreEqual(t1.TableName, t2.TableName);
        }

#pragma warning disable 618
        [Test]
        public void RoundtripBsonBytes()
        {
            Guid g = new Guid("EDE9A599-A7D9-44A9-9243-7C287049DD20");

            var table = new DataTable();
            table.Columns.Add("data", typeof(byte[]));
            table.Columns.Add("id", typeof(Guid));
            table.Rows.Add(Encoding.UTF8.GetBytes("Hello world!"), g);

            JsonSerializer serializer = new JsonSerializer();

            MemoryStream ms = new MemoryStream();
            BsonWriter bw = new BsonWriter(ms);

            serializer.Serialize(bw, table);

            JToken o = JToken.ReadFrom(new BsonReader(new MemoryStream(ms.ToArray())) { ReadRootValueAsArray = true });
            StringAssert.AreEqual(@"[
  {
    ""data"": ""SGVsbG8gd29ybGQh"",
    ""id"": ""ede9a599-a7d9-44a9-9243-7c287049dd20""
  }
]", o.ToString());

            DataTable deserializedDataTable = serializer.Deserialize<DataTable>(new BsonReader(new MemoryStream(ms.ToArray())) { ReadRootValueAsArray = true });

            Assert.AreEqual(string.Empty, deserializedDataTable.TableName);
            Assert.AreEqual(2, deserializedDataTable.Columns.Count);
            Assert.AreEqual("data", deserializedDataTable.Columns[0].ColumnName);
            Assert.AreEqual(typeof(byte[]), deserializedDataTable.Columns[0].DataType);
            Assert.AreEqual("id", deserializedDataTable.Columns[1].ColumnName);
            Assert.AreEqual(typeof(Guid), deserializedDataTable.Columns[1].DataType);

            Assert.AreEqual(1, deserializedDataTable.Rows.Count);

            DataRow dr1 = deserializedDataTable.Rows[0];
            CollectionAssert.AreEquivalent(Encoding.UTF8.GetBytes("Hello world!"), (byte[])dr1["data"]);
            Assert.AreEqual(g, (Guid)dr1["id"]);
        }
#pragma warning restore 618

        [Test]
        public void SerializeDataTableWithNull()
        {
            var table = new DataTable();
            table.Columns.Add("item");
            table.Columns.Add("price", typeof(double));
            table.Rows.Add("shirt", 49.99);
            table.Rows.Add("pants", 54.99);
            table.Rows.Add("shoes"); // no price

            var json = JsonConvert.SerializeObject(table);
            Assert.AreEqual(@"["
                            + @"{""item"":""shirt"",""price"":49.99},"
                            + @"{""item"":""pants"",""price"":54.99},"
                            + @"{""item"":""shoes"",""price"":null}]", json);
        }

        [Test]
        public void SerializeDataTableWithNullAndIgnoreNullHandling()
        {
            var table = new DataTable();
            table.Columns.Add("item");
            table.Columns.Add("price", typeof(double));
            table.Rows.Add("shirt", 49.99);
            table.Rows.Add("pants", 54.99);
            table.Rows.Add("shoes"); // no price

            var json = JsonConvert.SerializeObject(table, Formatting.None, new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore
            });
            Assert.AreEqual(@"["
                            + @"{""item"":""shirt"",""price"":49.99},"
                            + @"{""item"":""pants"",""price"":54.99},"
                            + @"{""item"":""shoes""}]", json);
        }

        [Test]
        public void DerializeDataTableWithImplicitNull()
        {
            const string json = @"["
                                + @"{""item"":""shirt"",""price"":49.99},"
                                + @"{""item"":""pants"",""price"":54.99},"
                                + @"{""item"":""shoes""}]";
            var table = JsonConvert.DeserializeObject<DataTable>(json);
            Assert.AreEqual("shirt", table.Rows[0]["item"]);
            Assert.AreEqual("pants", table.Rows[1]["item"]);
            Assert.AreEqual("shoes", table.Rows[2]["item"]);
            Assert.AreEqual(49.99, (double)table.Rows[0]["price"], 0.01);
            Assert.AreEqual(54.99, (double)table.Rows[1]["price"], 0.01);
            CustomAssert.IsInstanceOfType(typeof(System.DBNull), table.Rows[2]["price"]);
        }

        [Test]
        public void DerializeDataTableWithExplicitNull()
        {
            const string json = @"["
                                + @"{""item"":""shirt"",""price"":49.99},"
                                + @"{""item"":""pants"",""price"":54.99},"
                                + @"{""item"":""shoes"",""price"":null}]";
            var table = JsonConvert.DeserializeObject<DataTable>(json);
            Assert.AreEqual("shirt", table.Rows[0]["item"]);
            Assert.AreEqual("pants", table.Rows[1]["item"]);
            Assert.AreEqual("shoes", table.Rows[2]["item"]);
            Assert.AreEqual(49.99, (double)table.Rows[0]["price"], 0.01);
            Assert.AreEqual(54.99, (double)table.Rows[1]["price"], 0.01);
            CustomAssert.IsInstanceOfType(typeof(System.DBNull), table.Rows[2]["price"]);
        }

        [Test]
        public void SerializeKeyValuePairWithDataTableKey()
        {
            DataTable table = new DataTable();
            DataColumn idColumn = new DataColumn("id", typeof(int));
            idColumn.AutoIncrement = true;

            DataColumn itemColumn = new DataColumn("item");
            table.Columns.Add(idColumn);
            table.Columns.Add(itemColumn);

            DataRow r = table.NewRow();
            r["item"] = "item!";
            r.EndEdit();
            table.Rows.Add(r);

            KeyValuePair<DataTable, int> pair = new KeyValuePair<DataTable, int>(table, 1);
            string serializedpair = JsonConvert.SerializeObject(pair, Formatting.Indented);

            StringAssert.AreEqual(@"{
  ""Key"": [
    {
      ""id"": 0,
      ""item"": ""item!""
    }
  ],
  ""Value"": 1
}", serializedpair);

            var pair2 = (KeyValuePair<DataTable, int>)JsonConvert.DeserializeObject(serializedpair, typeof(KeyValuePair<DataTable, int>));

            Assert.AreEqual(1, pair2.Value);
            Assert.AreEqual(1, pair2.Key.Rows.Count);
            Assert.AreEqual("item!", pair2.Key.Rows[0]["item"]);
        }

        [Test]
        public void SerializedTypedDataTable()
        {
            CustomerDataSet.CustomersDataTable dt = new CustomerDataSet.CustomersDataTable();
            dt.AddCustomersRow("432");

            string json = JsonConvert.SerializeObject(dt, Formatting.Indented);

            StringAssert.AreEqual(@"[
  {
    ""CustomerID"": ""432""
  }
]", json);
        }

        [Test]
        public void DeserializedTypedDataTable()
        {
            string json = @"[
  {
    ""CustomerID"": ""432""
  }
]";

            var dt = JsonConvert.DeserializeObject<CustomerDataSet.CustomersDataTable>(json);

            Assert.AreEqual("432", dt[0].CustomerID);
        }

        public class DataTableTestClass
        {
            public DataTable Table { get; set; }
        }

        [Test]
        public void SerializeNull()
        {
            DataTableTestClass c1 = new DataTableTestClass
            {
                Table = null
            };

            string json = JsonConvert.SerializeObject(c1, Formatting.Indented);

            StringAssert.AreEqual(@"{
  ""Table"": null
}", json);

            DataTableTestClass c2 = JsonConvert.DeserializeObject<DataTableTestClass>(json);

            Assert.AreEqual(null, c2.Table);
        }

        [Test]
        public void SerializeNullRoot()
        {
            string json = JsonConvert.SerializeObject(null, typeof(DataTable), new JsonSerializerSettings
            {
                Formatting = Formatting.Indented
            });

            StringAssert.AreEqual(@"null", json);
        }

#if !(NET20 || PORTABLE || PORTABLE40)
        [Test]
        public void DeserializedTypedDataTableWithConverter()
        {
            string json = @"{
  ""TestTable"": [
    {
      ""DateTimeValue"": ""2015-11-28T00:00:00""
    },
    {
      ""DateTimeValue"": null
    }
  ]
}";

            SqlTypesDataSet ds = JsonConvert.DeserializeObject<SqlTypesDataSet>(json, new SqlDateTimeConverter());

            Assert.AreEqual(new System.Data.SqlTypes.SqlDateTime(2015, 11, 28), ds.TestTable[0].DateTimeValue);
            Assert.AreEqual(System.Data.SqlTypes.SqlDateTime.Null, ds.TestTable[1].DateTimeValue);

            string json2 = JsonConvert.SerializeObject(ds, Formatting.Indented, new SqlDateTimeConverter());

            StringAssert.AreEqual(json, json2);
        }

        internal class SqlDateTimeConverter : JsonConverter
        {
            public override bool CanConvert(Type objectType)
            {
                return typeof(System.Data.SqlTypes.SqlDateTime).IsAssignableFrom(objectType);
            }

            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            {
                if (reader.Value == null || reader.Value == DBNull.Value)
                {
                    return System.Data.SqlTypes.SqlDateTime.Null;
                }
                else
                {
                    return new System.Data.SqlTypes.SqlDateTime((DateTime)serializer.Deserialize(reader));
                }
            }

            public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
            {
                if (((System.Data.SqlTypes.SqlDateTime)value).IsNull)
                {
                    writer.WriteNull();
                }
                else
                {
                    writer.WriteValue(((System.Data.SqlTypes.SqlDateTime)value).Value);
                }
            }
        }
#endif

        [Test]
        public void HandleColumnOnError()
        {
            string json = "[{\"timeCol\":\"\"}]";
            DataTable table = JsonConvert.DeserializeObject<CustomDataTable>(json);

            Assert.AreEqual(DBNull.Value, table.Rows[0]["timeCol"]);
        }

        [JsonConverter(typeof(DataTableConverterTest))]
        public class CustomDataTable : DataTable
        {
        }

        public class DataTableConverterTest : DataTableConverter
        {
            protected DataTable CreateTable()
            {
                CustomDataTable table = new CustomDataTable();
                table.Columns.Add("timeCol", typeof(DateTime));
                return table;
            }

            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            {
                if (existingValue == null)
                {
                    existingValue = CreateTable();
                }

                serializer.Error += OnError;
                try
                {
                    return base.ReadJson(reader, objectType, existingValue, serializer);
                }
                finally
                {
                    serializer.Error -= OnError;
                }
            }

            private void OnError(object sender, Json.Serialization.ErrorEventArgs e)
            {
                e.ErrorContext.Handled = true;
            }
        }
    }
}

#endif