Files

726 lines
25 KiB
C#

#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 || NET6_0_OR_GREATER
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