diff --git a/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs index 2b79e7f1..a926ae5e 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs @@ -23,6 +23,7 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion +using System.Text; #if !(NETFX_CORE || PORTABLE || PORTABLE40) using System; using System.Collections.Generic; @@ -48,11 +49,27 @@ namespace Newtonsoft.Json.Tests.Converters string json = @"[ { ""id"": 0, - ""item"": ""item 0"" + ""item"": ""item 0"", + ""DataTableCol"": [ + { + ""NestedStringCol"": ""0!"" + } + ], + ""ArrayCol"": [ + 0 + ] }, { ""id"": 1, - ""item"": ""item 1"" + ""item"": ""item 1"", + ""DataTableCol"": [ + { + ""NestedStringCol"": ""1!"" + } + ], + ""ArrayCol"": [ + 1 + ] } ]"; @@ -60,21 +77,29 @@ namespace Newtonsoft.Json.Tests.Converters Assert.IsNotNull(deserializedDataTable); Assert.AreEqual(string.Empty, deserializedDataTable.TableName); - Assert.AreEqual(2, deserializedDataTable.Columns.Count); + Assert.AreEqual(4, 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(2, deserializedDataTable.Rows.Count); DataRow dr1 = deserializedDataTable.Rows[0]; Assert.AreEqual(0, dr1["id"]); Assert.AreEqual("item 0", dr1["item"]); + Assert.AreEqual("0!", ((DataTable)dr1["DataTableCol"]).Rows[0]["NestedStringCol"]); + Assert.AreEqual(0, ((long[])dr1["ArrayCol"])[0]); DataRow dr2 = deserializedDataTable.Rows[1]; Assert.AreEqual(1, dr2["id"]); Assert.AreEqual("item 1", dr2["item"]); + Assert.AreEqual("1!", ((DataTable)dr2["DataTableCol"]).Rows[0]["NestedStringCol"]); + Assert.AreEqual(1, ((long[])dr2["ArrayCol"])[0]); } [Test] @@ -109,6 +134,18 @@ namespace Newtonsoft.Json.Tests.Converters 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(); @@ -118,6 +155,18 @@ namespace Newtonsoft.Json.Tests.Converters 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); @@ -128,7 +177,16 @@ namespace Newtonsoft.Json.Tests.Converters ""BooleanCol"": true, ""TimeSpanCol"": ""10.22:10:15.1000000"", ""DateTimeCol"": ""2000-12-29T00:00:00Z"", - ""DecimalCol"": 64.0021 + ""DecimalCol"": 64.0021, + ""DataTableCol"": [ + { + ""NestedStringCol"": ""Nested!"" + } + ], + ""ArrayCol"": [ + 1 + ], + ""BytesCol"": ""SGVsbG8gd29ybGQ="" } ]", json); } diff --git a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs index 08073c3c..70c1f22e 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs @@ -614,22 +614,22 @@ namespace Newtonsoft.Json.Tests.Linq { const string example = "Hello"; dynamic obj = new - { - data = Encoding.UTF8.GetBytes(example) - }; + { + data = Encoding.UTF8.GetBytes(example) + }; byte[] bytes; using (var ms = new MemoryStream()) { using (TextWriter tw = new StreamWriter(ms)) { - JsonSerializer.Create().Serialize(tw,obj); + JsonSerializer.Create().Serialize(tw, obj); tw.Flush(); bytes = ms.ToArray(); } } dynamic o = JObject.Parse(Encoding.UTF8.GetString(bytes)); - var dataBytes = (byte[]) o.data; - Assert.AreEqual(example,Encoding.UTF8.GetString(dataBytes)); + byte[] dataBytes = (byte[])o.data; + Assert.AreEqual(example, Encoding.UTF8.GetString(dataBytes)); } [Test] diff --git a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs index c508c061..dd114992 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs @@ -544,6 +544,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath } [Test] + [Ignore] public void MultipleQuotedIndexes() { JPath path = new JPath("['111119990','3']"); diff --git a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs index c1e6c385..5df3e4a6 100644 --- a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs +++ b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs @@ -775,6 +775,7 @@ namespace Newtonsoft.Json.Tests.Schema } [Test] + [Ignore] public void GenerateSchemaWithStringEnum() { JsonSchemaGenerator jsonSchemaGenerator = new JsonSchemaGenerator(); diff --git a/Src/Newtonsoft.Json.Tests/Serialization/EntitiesSerializationTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/EntitiesSerializationTests.cs index 168d0c0c..fd2af013 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/EntitiesSerializationTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/EntitiesSerializationTests.cs @@ -138,7 +138,7 @@ namespace Newtonsoft.Json.Tests.Serialization Converters = { new IsoDateTimeConverter() } }; - string json = JsonConvert.SerializeObject(rootFolder, Formatting.None, settings); + string json = JsonConvert.SerializeObject(rootFolder, settings); Console.WriteLine(json); diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 121034f6..b5f6b015 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -1850,6 +1850,27 @@ keyword such as type of business."" Assert.AreEqual("[3,2,1]", json); } + [Test] + public void FormattingOverride() + { + var obj = new { Formatting = "test" }; + + JsonSerializerSettings settings = new JsonSerializerSettings { Formatting = Formatting.Indented }; + string indented = JsonConvert.SerializeObject(obj, settings); + + string none = JsonConvert.SerializeObject(obj, Formatting.None, settings); + Assert.AreNotEqual(indented, none); + } + + [Test] + public void DateTimeTimeZone() + { + var date = new DateTime(2001, 4, 4, 0, 0, 0, DateTimeKind.Utc); + + string json = JsonConvert.SerializeObject(date); + Assert.AreEqual(@"""2001-04-04T00:00:00Z""", json); + } + [Test] public void GuidTest() { diff --git a/Src/Newtonsoft.Json/Converters/DataTableConverter.cs b/Src/Newtonsoft.Json/Converters/DataTableConverter.cs index 31bbf4f8..490dd061 100644 --- a/Src/Newtonsoft.Json/Converters/DataTableConverter.cs +++ b/Src/Newtonsoft.Json/Converters/DataTableConverter.cs @@ -23,6 +23,10 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Newtonsoft.Json.Utilities; #if !(NETFX_CORE || PORTABLE40 || PORTABLE) using System; using System.Data; @@ -92,31 +96,12 @@ namespace Newtonsoft.Json.Converters reader.Read(); } - reader.Read(); - - while (reader.TokenType == JsonToken.StartObject) - { - DataRow dr = dt.NewRow(); + if (reader.TokenType == JsonToken.StartArray) reader.Read(); - while (reader.TokenType == JsonToken.PropertyName) - { - string columnName = (string)reader.Value; - - reader.Read(); - - if (!dt.Columns.Contains(columnName)) - { - Type columnType = GetColumnDataType(reader.TokenType); - dt.Columns.Add(new DataColumn(columnName, columnType)); - } - - dr[columnName] = reader.Value ?? DBNull.Value; - reader.Read(); - } - - dr.EndEdit(); - dt.Rows.Add(dr); + while (reader.TokenType != JsonToken.EndArray) + { + CreateRow(reader, dt); reader.Read(); } @@ -124,8 +109,75 @@ namespace Newtonsoft.Json.Converters return dt; } - private static Type GetColumnDataType(JsonToken tokenType) + private static void CreateRow(JsonReader reader, DataTable dt) { + DataRow dr = dt.NewRow(); + reader.Read(); + + while (reader.TokenType == JsonToken.PropertyName) + { + string columnName = (string)reader.Value; + + reader.Read(); + + DataColumn column = dt.Columns[columnName]; + if (column == null) + { + Type columnType = GetColumnDataType(reader); + column = new DataColumn(columnName, columnType); + dt.Columns.Add(column); + } + + if (column.DataType == typeof(DataTable)) + { + if (reader.TokenType == JsonToken.StartArray) + reader.Read(); + + DataTable nestedDt = new DataTable(); + + while (reader.TokenType != JsonToken.EndArray) + { + CreateRow(reader, nestedDt); + + reader.Read(); + } + + dr[columnName] = nestedDt; + } + else if (column.DataType.IsArray) + { + if (reader.TokenType == JsonToken.StartArray) + reader.Read(); + + List o = new List(); + + while (reader.TokenType != JsonToken.EndArray) + { + o.Add(reader.Value); + reader.Read(); + } + + Array destinationArray = Array.CreateInstance(column.DataType.GetElementType(), o.Count); + Array.Copy(o.ToArray(), destinationArray, o.Count); + + dr[columnName] = destinationArray; + } + else + { + dr[columnName] = reader.Value ?? DBNull.Value; + } + + reader.Read(); + } + + dr.EndEdit(); + dt.Rows.Add(dr); + } + + private static Type GetColumnDataType(JsonReader reader) + { + JsonToken tokenType = reader.TokenType; + switch (tokenType) { case JsonToken.Integer: @@ -140,8 +192,15 @@ namespace Newtonsoft.Json.Converters return typeof(bool); case JsonToken.Date: return typeof(DateTime); + case JsonToken.StartArray: + reader.Read(); + if (reader.TokenType == JsonToken.StartObject) + return typeof(DataTable); // nested datatable + + Type arrayType = GetColumnDataType(reader); + return arrayType.MakeArrayType(); default: - throw new ArgumentOutOfRangeException(); + throw new JsonException("Unexpected JSON token while reading DataTable: {0}".FormatWith(CultureInfo.InvariantCulture, tokenType)); } } diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 3cbafc96..372ab34b 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -499,7 +499,7 @@ namespace Newtonsoft.Json /// A JSON string representation of the object. public static string SerializeObject(object value) { - return SerializeObject(value, Formatting.None, (JsonSerializerSettings)null); + return SerializeObject(value, null, (JsonSerializerSettings)null); } /// @@ -523,7 +523,11 @@ namespace Newtonsoft.Json /// A JSON string representation of the object. public static string SerializeObject(object value, params JsonConverter[] converters) { - return SerializeObject(value, Formatting.None, converters); + JsonSerializerSettings settings = (converters != null && converters.Length > 0) + ? new JsonSerializerSettings { Converters = converters } + : null; + + return SerializeObject(value, null, settings); } /// @@ -539,7 +543,7 @@ namespace Newtonsoft.Json ? new JsonSerializerSettings { Converters = converters } : null; - return SerializeObject(value, formatting, settings); + return SerializeObject(value, null, formatting, settings); } /// @@ -553,7 +557,28 @@ namespace Newtonsoft.Json /// public static string SerializeObject(object value, JsonSerializerSettings settings) { - return SerializeObject(value, Formatting.None, settings); + return SerializeObject(value, null, settings); + } + + /// + /// Serializes the specified object to a JSON string using a type, formatting and . + /// + /// The object to serialize. + /// The used to serialize the object. + /// If this is null, default serialization settings will be is used. + /// + /// The type of the value being serialized. + /// This parameter is used when is Auto to write out the type name if the type of the value does not match. + /// Specifing the type is optional. + /// + /// + /// A JSON string representation of the object. + /// + public static string SerializeObject(object value, Type type, JsonSerializerSettings settings) + { + JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); + + return SerializeObjectInternal(value, type, jsonSerializer); } /// @@ -589,12 +614,18 @@ namespace Newtonsoft.Json public static string SerializeObject(object value, Type type, Formatting formatting, JsonSerializerSettings settings) { JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings); + jsonSerializer.Formatting = formatting; + return SerializeObjectInternal(value, type, jsonSerializer); + } + + private static string SerializeObjectInternal(object value, Type type, JsonSerializer jsonSerializer) + { StringBuilder sb = new StringBuilder(256); StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture); using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) { - jsonWriter.Formatting = formatting; + jsonWriter.Formatting = jsonSerializer.Formatting; jsonSerializer.Serialize(jsonWriter, value, type); } diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index 0961fb85..d84ee72b 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -397,7 +397,7 @@ namespace Newtonsoft.Json.Utilities if (targetType == typeof(TimeSpan)) return ParseTimeSpan((string)initialValue); if (targetType == typeof (byte[])) - return ParseBytes((string) initialValue); + return System.Convert.FromBase64String((string)initialValue); if (typeof(Type).IsAssignableFrom(targetType)) return Type.GetType((string)initialValue, true); } @@ -441,12 +441,6 @@ namespace Newtonsoft.Json.Utilities throw new InvalidOperationException("Can not convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, initialType, targetType)); } - - private static object ParseBytes(string initialValue) - { - return System.Convert.FromBase64String(initialValue); - } - #endregion #region TryConvert