diff --git a/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs new file mode 100644 index 00000000..6f4afaa1 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs @@ -0,0 +1,199 @@ +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; +using System.Data; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class DataSetConverterTests : TestFixtureBase + { + [Test] + public void SerializeAndDeserialize() + { + DataSet dataSet = new DataSet("dataSet"); + dataSet.Namespace = "NetFrameWork"; + 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); + dataSet.Tables.Add(table); + + for (int i = 0; i < 2; i++) + { + DataRow newRow = table.NewRow(); + newRow["item"] = "item " + i; + table.Rows.Add(newRow); + } + + dataSet.AcceptChanges(); + + string json = JsonConvert.SerializeObject(dataSet, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Table1"": [ + { + ""id"": 0, + ""item"": ""item 0"" + }, + { + ""id"": 1, + ""item"": ""item 1"" + } + ] +}", json); + + DataSet deserializedDataSet = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(deserializedDataSet); + + Assert.AreEqual(1, deserializedDataSet.Tables.Count); + + DataTable dt = deserializedDataSet.Tables[0]; + + Assert.AreEqual("Table1", dt.TableName); + Assert.AreEqual(2, dt.Columns.Count); + Assert.AreEqual("id", dt.Columns[0].ColumnName); + Assert.AreEqual(typeof(long), dt.Columns[0].DataType); + Assert.AreEqual("item", dt.Columns[1].ColumnName); + Assert.AreEqual(typeof(string), dt.Columns[1].DataType); + + Assert.AreEqual(2, dt.Rows.Count); + } + + public void SerializeMultiTableDataSet() + { + DataSet ds = new DataSet(); + ds.Tables.Add(CreateDataTable("FirstTable")); + ds.Tables.Add(CreateDataTable("SecondTable")); + + string json = JsonConvert.SerializeObject(ds, Formatting.Indented, new IsoDateTimeConverter()); + + Assert.AreEqual(@"{ + ""FirstTable"": [ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 2147483647, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00"", + ""DecimalCol"": 64.0021 + } + ], + ""SecondTable"": [ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 2147483647, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00"", + ""DecimalCol"": 64.0021 + } + ] +}", json); + } + + [Test] + public void DeserializeMultiTableDataSet() + { + string json = @"{ + ""FirstTable"": [ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 2147483647, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00"", + ""DecimalCol"": 64.0021 + } + ], + ""SecondTable"": [ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 2147483647, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00"", + ""DecimalCol"": 64.0021 + } + ] +}"; + + DataSet ds = JsonConvert.DeserializeObject(json, new IsoDateTimeConverter()); + Assert.IsNotNull(ds); + + Assert.AreEqual(2, ds.Tables.Count); + Assert.AreEqual("FirstTable", ds.Tables[0].TableName); + Assert.AreEqual("SecondTable", ds.Tables[1].TableName); + + DataTable dt = ds.Tables[0]; + Assert.AreEqual("StringCol", dt.Columns[0].ColumnName); + Assert.AreEqual(typeof(string), dt.Columns[0].DataType); + Assert.AreEqual("Int32Col", dt.Columns[1].ColumnName); + Assert.AreEqual(typeof(long), dt.Columns[1].DataType); + Assert.AreEqual("BooleanCol", dt.Columns[2].ColumnName); + Assert.AreEqual(typeof(bool), dt.Columns[2].DataType); + Assert.AreEqual("TimeSpanCol", dt.Columns[3].ColumnName); + Assert.AreEqual(typeof(string), dt.Columns[3].DataType); + Assert.AreEqual("DateTimeCol", dt.Columns[4].ColumnName); + Assert.AreEqual(typeof(string), dt.Columns[4].DataType); + Assert.AreEqual("DecimalCol", dt.Columns[5].ColumnName); + Assert.AreEqual(typeof(double), dt.Columns[5].DataType); + + Assert.AreEqual(1, ds.Tables[0].Rows.Count); + Assert.AreEqual(1, ds.Tables[1].Rows.Count); + } + + private DataTable CreateDataTable(string dataTableName) + { + // create a new DataTable. + DataTable myTable = new DataTable(dataTableName); + + // 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); + + // 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; + myTable.Rows.Add(myNewRow); + + return myTable; + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs new file mode 100644 index 00000000..1790c689 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs @@ -0,0 +1,107 @@ +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; +using System.Data; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class DataTableConverterTests : TestFixtureBase + { + [Test] + public void Deserialize() + { + string json = @"[ + { + ""id"": 0, + ""item"": ""item 0"" + }, + { + ""id"": 1, + ""item"": ""item 1"" + } +]"; + + DataTable deserializedDataTable = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(deserializedDataTable); + + Assert.AreEqual(string.Empty, deserializedDataTable.TableName); + Assert.AreEqual(2, 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(2, deserializedDataTable.Rows.Count); + + DataRow dr1 = deserializedDataTable.Rows[0]; + Assert.AreEqual(0, dr1["id"]); + Assert.AreEqual("item 0", dr1["item"]); + + DataRow dr2 = deserializedDataTable.Rows[1]; + Assert.AreEqual(1, dr2["id"]); + Assert.AreEqual("item 1", dr2["item"]); + } + + [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); + + // 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; + myTable.Rows.Add(myNewRow); + + string json = JsonConvert.SerializeObject(myTable, Formatting.Indented); + Assert.AreEqual(@"[ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 2147483647, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""\/Date(978048000000)\/"", + ""DecimalCol"": 64.0021 + } +]", json); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs index 71aaaf8e..a9e49cbc 100644 --- a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs @@ -154,6 +154,21 @@ now brown cow?", '"', true); Assert.AreEqual(result, @"'Fred\'s ""cat"".'"); } + [Test] + public void EscapeJavaScriptString_UnicodeLinefeeds() + { + string result; + + result = JavaScriptUtils.ToEscapedJavaScriptString("before" + '\u0085' + "after", '"', true); + Assert.AreEqual(@"""before\u0085after""", result); + + result = JavaScriptUtils.ToEscapedJavaScriptString("before" + '\u2028' + "after", '"', true); + Assert.AreEqual(@"""before\u2028after""", result); + + result = JavaScriptUtils.ToEscapedJavaScriptString("before" + '\u2029' + "after", '"', true); + Assert.AreEqual(@"""before\u2029after""", result); + } + [Test] [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Unsupported type: System.Version. Use the JsonSerializer class to get the object's JSON representation.")] public void ToStringInvalid() diff --git a/Src/Newtonsoft.Json.Tests/Linq/JPathTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JPathTests.cs new file mode 100644 index 00000000..639c0af7 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Linq/JPathTests.cs @@ -0,0 +1,246 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Converters; +using System.IO; +using System.Collections; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JPathTests : TestFixtureBase + { + [Test] + public void SingleProperty() + { + JPath path = new JPath("Blah"); + Assert.AreEqual(1, path.Parts.Count); + Assert.AreEqual("Blah", path.Parts[0]); + } + + [Test] + public void TwoProperties() + { + JPath path = new JPath("Blah.Two"); + Assert.AreEqual(2, path.Parts.Count); + Assert.AreEqual("Blah", path.Parts[0]); + Assert.AreEqual("Two", path.Parts[1]); + } + + [Test] + public void SinglePropertyAndIndexer() + { + JPath path = new JPath("Blah[0]"); + Assert.AreEqual(2, path.Parts.Count); + Assert.AreEqual("Blah", path.Parts[0]); + Assert.AreEqual(0, path.Parts[1]); + } + + [Test] + public void MultiplePropertiesAndIndexers() + { + JPath path = new JPath("Blah[0].Two.Three[1].Four"); + Assert.AreEqual(6, path.Parts.Count); + Assert.AreEqual("Blah", path.Parts[0]); + Assert.AreEqual(0, path.Parts[1]); + Assert.AreEqual("Two", path.Parts[2]); + Assert.AreEqual("Three", path.Parts[3]); + Assert.AreEqual(1, path.Parts[4]); + Assert.AreEqual("Four", path.Parts[5]); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Unexpected character while parsing path indexer: [")] + public void BadCharactersInIndexer() + { + new JPath("Blah[[0]].Two.Three[1].Four"); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Path ended with open indexer. Expected ]")] + public void UnclosedIndexer() + { + new JPath("Blah[0"); + } + + [Test] + public void AdditionalDots() + { + JPath path = new JPath(".Blah..[0]..Two.Three....[1].Four."); + Assert.AreEqual(6, path.Parts.Count); + Assert.AreEqual("Blah", path.Parts[0]); + Assert.AreEqual(0, path.Parts[1]); + Assert.AreEqual("Two", path.Parts[2]); + Assert.AreEqual("Three", path.Parts[3]); + Assert.AreEqual(1, path.Parts[4]); + Assert.AreEqual("Four", path.Parts[5]); + } + + [Test] + public void IndexerOnly() + { + JPath path = new JPath("[111119990]"); + Assert.AreEqual(1, path.Parts.Count); + Assert.AreEqual(111119990, path.Parts[0]); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Empty path indexer.")] + public void EmptyIndexer() + { + new JPath("[]"); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Unexpected character while parsing path: ]")] + public void IndexerCloseInProperty() + { + new JPath("]"); + } + + [Test] + public void AdjacentIndexers() + { + JPath path = new JPath("[1][0][0][" + int.MaxValue + "]"); + Assert.AreEqual(4, path.Parts.Count); + Assert.AreEqual(1, path.Parts[0]); + Assert.AreEqual(0, path.Parts[1]); + Assert.AreEqual(0, path.Parts[2]); + Assert.AreEqual(int.MaxValue, path.Parts[3]); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Unexpected character following indexer: B")] + public void MissingDotAfterIndexer() + { + new JPath("[1]Blah"); + } + + [Test] + public void EvaluateSingleProperty() + { + JObject o = new JObject( + new JProperty("Blah", 1)); + + JToken t = o.SelectToken("Blah"); + Assert.IsNotNull(t); + Assert.AreEqual(JTokenType.Integer, t.Type); + Assert.AreEqual(1, (int)t); + } + + [Test] + public void EvaluateMissingProperty() + { + JObject o = new JObject( + new JProperty("Blah", 1)); + + JToken t = o.SelectToken("Missing[1]"); + Assert.IsNull(t); + } + + [Test] + public void EvaluateIndexerOnObject() + { + JObject o = new JObject( + new JProperty("Blah", 1)); + + JToken t = o.SelectToken("[1]"); + Assert.IsNull(t); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Index 1 not valid on JObject.")] + public void EvaluateIndexerOnObjectWithError() + { + JObject o = new JObject( + new JProperty("Blah", 1)); + + o.SelectToken("[1]", true); + } + + [Test] + public void EvaluatePropertyOnArray() + { + JArray a = new JArray(1, 2, 3, 4, 5); + + JToken t = a.SelectToken("BlahBlah"); + Assert.IsNull(t); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Property 'BlahBlah' not valid on JArray.")] + public void EvaluatePropertyOnArrayWithError() + { + JArray a = new JArray(1, 2, 3, 4, 5); + + a.SelectToken("BlahBlah", true); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Index 1 not valid on JConstructor.")] + public void EvaluateIndexerOnConstructorWithError() + { + JConstructor c = new JConstructor("Blah"); + + c.SelectToken("[1]", true); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Property 'Missing' does not exist on JObject.")] + public void EvaluateMissingPropertyWithError() + { + JObject o = new JObject( + new JProperty("Blah", 1)); + + o.SelectToken("Missing", true); + } + + [Test] + public void EvaluateOutOfBoundsIndxer() + { + JArray a = new JArray(1, 2, 3, 4, 5); + + JToken t = a.SelectToken("[1000].Ha"); + Assert.IsNull(t); + } + + [Test] + [ExpectedException(typeof(IndexOutOfRangeException), ExpectedMessage = "Index 1000 outside the bounds of JArray.")] + public void EvaluateOutOfBoundsIndxerWithError() + { + JArray a = new JArray(1, 2, 3, 4, 5); + + a.SelectToken("[1000].Ha", true); + } + + [Test] + public void EvaluateArray() + { + JArray a = new JArray(1, 2, 3, 4); + + JToken t = a.SelectToken("[1]"); + Assert.IsNotNull(t); + Assert.AreEqual(JTokenType.Integer, t.Type); + Assert.AreEqual(2, (int)t); + } + + [Test] + public void EvaluateSinglePropertyReturningArray() + { + JObject o = new JObject( + new JProperty("Blah", new [] { 1, 2, 3 })); + + JToken t = o.SelectToken("Blah"); + Assert.IsNotNull(t); + Assert.AreEqual(JTokenType.Array, t.Type); + + t = o.SelectToken("Blah[2]"); + Assert.AreEqual(JTokenType.Integer, t.Type); + Assert.AreEqual(3, (int)t); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj index 84fa77bc..638b89e9 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj @@ -60,11 +60,14 @@ + + + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj index 00e59e60..a8938eac 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj @@ -64,6 +64,8 @@ + + @@ -72,6 +74,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index 3f39a17d..c9171db4 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -64,6 +64,8 @@ + + @@ -71,6 +73,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index 14f00162..a8b752c2 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -84,6 +84,8 @@ + + @@ -109,6 +111,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs index 0b4034dc..9a765bf3 100644 --- a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs +++ b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs @@ -498,5 +498,82 @@ namespace Newtonsoft.Json.Tests.Schema } }", json); } + + [Test] + public void CircularCollectionReferences() + { + Type type = typeof (Workspace); + JsonSchemaGenerator jsonSchemaGenerator = new JsonSchemaGenerator(); + + jsonSchemaGenerator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + JsonSchema jsonSchema = jsonSchemaGenerator.Generate(type); + + // should succeed + Assert.IsNotNull(jsonSchema); + } + } + + public class DMDSLBase + { + public String Comment; + } + + public class Workspace : DMDSLBase + { + public ControlFlowItemCollection Jobs = new ControlFlowItemCollection(); + } + + public class ControlFlowItemBase : DMDSLBase + { + public String Name; + } + + public class ControlFlowItem : ControlFlowItemBase//A Job + { + public TaskCollection Tasks = new TaskCollection(); + public ContainerCollection Containers = new ContainerCollection(); + } + + public class ControlFlowItemCollection : List + { + } + + public class Task : ControlFlowItemBase + { + public DataFlowTaskCollection DataFlowTasks = new DataFlowTaskCollection(); + public BulkInsertTaskCollection BulkInsertTask = new BulkInsertTaskCollection(); + } + + public class TaskCollection : List + { + } + + public class Container : ControlFlowItemBase + { + public ControlFlowItemCollection ContainerJobs = new ControlFlowItemCollection(); + } + + public class ContainerCollection : List + { + } + + public class DataFlowTask_DSL : ControlFlowItemBase + { + } + + public class DataFlowTaskCollection : List + { + } + + public class SequenceContainer_DSL : Container + { + } + + public class BulkInsertTaskCollection : List + { + } + + public class BulkInsertTask_DSL + { } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index eaa03aa7..fc0e3139 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -659,6 +659,25 @@ keyword such as type of business."" Assert.AreEqual(StringComparison.CurrentCultureIgnoreCase, s); } + public class ClassWithTimeSpan + { + public TimeSpan TimeSpanField; + } + + [Test] + public void TimeSpanTest() + { + TimeSpan ts = new TimeSpan(200, 23, 59, 1); + + string json = JsonConvert.SerializeObject(new ClassWithTimeSpan { TimeSpanField = ts }, Formatting.Indented); + Assert.AreEqual(@"{ + ""TimeSpanField"": ""200.23:59:01"" +}", json); + + ClassWithTimeSpan c = JsonConvert.DeserializeObject(json); + Assert.AreEqual(ts, c.TimeSpanField); + } + [Test] public void JsonIgnoreAttributeOnClassTest() { diff --git a/Src/Newtonsoft.Json/Converters/DataSetConverter.cs b/Src/Newtonsoft.Json/Converters/DataSetConverter.cs new file mode 100644 index 00000000..692de6bd --- /dev/null +++ b/Src/Newtonsoft.Json/Converters/DataSetConverter.cs @@ -0,0 +1,55 @@ +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Converters +{ + public class DataSetConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + DataSet dataSet = (DataSet)value; + + DataTableConverter converter = new DataTableConverter(); + + writer.WriteStartObject(); + + foreach (DataTable table in dataSet.Tables) + { + writer.WritePropertyName(table.TableName); + + converter.WriteJson(writer, table, serializer); + } + + writer.WriteEndObject(); + } + + public override object ReadJson(JsonReader reader, Type objectType, JsonSerializer serializer) + { + DataSet ds = new DataSet(); + + DataTableConverter converter = new DataTableConverter(); + + reader.Read(); + + while (reader.TokenType == JsonToken.PropertyName) + { + DataTable dt = (DataTable)converter.ReadJson(reader, typeof (DataTable), serializer); + ds.Tables.Add(dt); + } + + reader.Read(); + + return ds; + } + + public override bool CanConvert(Type valueType) + { + return typeof(DataSet).IsAssignableFrom(valueType); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Converters/DataTableConverter.cs b/Src/Newtonsoft.Json/Converters/DataTableConverter.cs new file mode 100644 index 00000000..675d7330 --- /dev/null +++ b/Src/Newtonsoft.Json/Converters/DataTableConverter.cs @@ -0,0 +1,107 @@ +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Data; + +namespace Newtonsoft.Json.Converters +{ + public class DataTableConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + DataTable table = (DataTable)value; + + writer.WriteStartArray(); + + foreach (DataRow row in table.Rows) + { + writer.WriteStartObject(); + foreach (DataColumn column in row.Table.Columns) + { + writer.WritePropertyName(column.ColumnName); + serializer.Serialize(writer, row[column]); + } + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + } + + public override object ReadJson(JsonReader reader, Type objectType, JsonSerializer serializer) + { + DataTable dt; + + if (reader.TokenType == JsonToken.PropertyName) + { + dt = new DataTable((string)reader.Value); + reader.Read(); + } + else + { + dt = new DataTable(); + } + + reader.Read(); + + while (reader.TokenType == JsonToken.StartObject) + { + DataRow dr = dt.NewRow(); + 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; + reader.Read(); + } + + dr.EndEdit(); + dt.Rows.Add(dr); + + reader.Read(); + } + + reader.Read(); + + return dt; + } + + private static Type GetColumnDataType(JsonToken tokenType) + { + switch (tokenType) + { + case JsonToken.Integer: + return typeof (long); + case JsonToken.Float: + return typeof (double); + case JsonToken.String: + case JsonToken.Null: + case JsonToken.Undefined: + return typeof (string); + case JsonToken.Boolean: + return typeof (bool); + case JsonToken.Date: + return typeof (DateTime); + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override bool CanConvert(Type valueType) + { + return typeof(DataTable).IsAssignableFrom(valueType); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 77bdefa4..2d65567b 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -57,7 +57,12 @@ namespace Newtonsoft.Json private static readonly IList BuiltInConverters = new List { #if !PocketPC && !SILVERLIGHT && !NET20 - new EntityKeyMemberConverter() + new EntityKeyMemberConverter(), +#endif + new BinaryConverter(), +#if !SILVERLIGHT + new DataSetConverter(), + new DataTableConverter() #endif }; diff --git a/Src/Newtonsoft.Json/Linq/JPath.cs b/Src/Newtonsoft.Json/Linq/JPath.cs new file mode 100644 index 00000000..0b1b3e37 --- /dev/null +++ b/Src/Newtonsoft.Json/Linq/JPath.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq +{ + internal class JPath + { + private readonly string _expression; + public List Parts { get; private set; } + + private int _currentIndex; + + public JPath(string expression) + { + ValidationUtils.ArgumentNotNull(expression, "expression"); + _expression = expression; + Parts = new List(); + + ParseMain(); + } + + private void ParseMain() + { + int currentPartStartIndex = _currentIndex; + bool followingIndexer = false; + + while (_currentIndex < _expression.Length) + { + char currentChar = _expression[_currentIndex]; + + switch (currentChar) + { + case '[': + case '(': + if (_currentIndex > currentPartStartIndex) + { + string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex); + Parts.Add(member); + } + + ParseIndexer(currentChar); + currentPartStartIndex = _currentIndex + 1; + followingIndexer = true; + break; + case ']': + case ')': + throw new Exception("Unexpected character while parsing path: " + currentChar); + case '.': + if (_currentIndex > currentPartStartIndex) + { + string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex); + Parts.Add(member); + } + currentPartStartIndex = _currentIndex + 1; + followingIndexer = false; + break; + default: + if (followingIndexer) + throw new Exception("Unexpected character following indexer: " + currentChar); + break; + } + + _currentIndex++; + } + + if (_currentIndex - 1 > currentPartStartIndex) + { + string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex); + Parts.Add(member); + } + } + + private void ParseIndexer(char indexerOpenChar) + { + _currentIndex++; + + char indexerCloseChar = (indexerOpenChar == '[') ? ']' : ')'; + int indexerStart = _currentIndex; + int indexerLength = 0; + bool indexerClosed = false; + + while (_currentIndex < _expression.Length) + { + char currentCharacter = _expression[_currentIndex]; + if (char.IsDigit(currentCharacter)) + { + indexerLength++; + } + else if (currentCharacter == indexerCloseChar) + { + indexerClosed = true; + break; + } + else + { + throw new Exception("Unexpected character while parsing path indexer: " + currentCharacter); + } + + _currentIndex++; + } + + if (!indexerClosed) + throw new Exception("Path ended with open indexer. Expected " + indexerCloseChar); + + if (indexerLength == 0) + throw new Exception("Empty path indexer."); + + string indexer = _expression.Substring(indexerStart, indexerLength); + Parts.Add(Convert.ToInt32(indexer, CultureInfo.InvariantCulture)); + } + + internal JToken Evaluate(JToken root, bool errorWhenNoMatch) + { + JToken current = root; + + foreach (object part in Parts) + { + string propertyName = part as string; + if (propertyName != null) + { + JObject o = current as JObject; + if (o != null) + { + current = o[propertyName]; + + if (current == null && errorWhenNoMatch) + throw new Exception("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, propertyName)); + } + else + { + if (errorWhenNoMatch) + throw new Exception("Property '{0}' not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, propertyName, current.GetType().Name)); + + return null; + } + } + else + { + int index = (int) part; + + JArray a = current as JArray; + + if (a != null) + { + if (a.Count <= index) + { + if (errorWhenNoMatch) + throw new IndexOutOfRangeException("Index {0} outside the bounds of JArray.".FormatWith(CultureInfo.InvariantCulture, index)); + + return null; + } + + current = a[index]; + } + else + { + if (errorWhenNoMatch) + throw new Exception("Index {0} not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, index, current.GetType().Name)); + + return null; + } + } + } + + return current; + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index eec3db6c..3cde6282 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -1095,5 +1095,16 @@ namespace Newtonsoft.Json.Linq { get { return _linePosition ?? 0; } } + + public JToken SelectToken(string path) + { + return SelectToken(path, false); + } + + public JToken SelectToken(string path, bool errorWhenNoMatch) + { + JPath p = new JPath(path); + return p.Evaluate(this, errorWhenNoMatch); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj index 148bc41c..a45bdf8e 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj @@ -60,6 +60,8 @@ + + @@ -75,6 +77,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index e1cdbd1b..63d83c86 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -80,11 +80,14 @@ + + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index d6f605ff..10e55eb7 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -52,6 +52,8 @@ + + @@ -93,6 +95,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 867c8a07..76bd1db8 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -87,10 +87,13 @@ + + + diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs index 45f5ac9b..1b76a1e1 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs @@ -265,6 +265,8 @@ namespace Newtonsoft.Json.Schema // TODO: include null CurrentSchema.Type = JsonSchemaType.Array; + CurrentSchema.Id = GetTypeId(type, false); + JsonArrayAttribute arrayAttribute = JsonTypeReflector.GetJsonContainerAttribute(type) as JsonArrayAttribute; bool allowNullItem = (arrayAttribute != null) ? arrayAttribute.AllowNullItems : false; diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 9b615bb6..89cc76cc 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -241,7 +241,7 @@ namespace Newtonsoft.Json.Serialization } } #else - if (value is Guid || value is Type || value is Uri) + if (value is Guid || value is Type || value is Uri || value is TimeSpan) { s = value.ToString(); return true; diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index 9441fe2f..2486328e 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -166,10 +166,12 @@ namespace Newtonsoft.Json.Utilities if (initialValue is string) { - if (targetType == typeof(Guid)) - return new Guid((string)initialValue); - if (targetType == typeof(Uri)) + if (targetType == typeof (Guid)) + return new Guid((string) initialValue); + if (targetType == typeof (Uri)) return new Uri((string) initialValue); + if (targetType == typeof (TimeSpan)) + return TimeSpan.Parse((string) initialValue); } #if !PocketPC diff --git a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs index 22a1e58a..8bfccdaf 100644 --- a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs @@ -57,11 +57,15 @@ namespace Newtonsoft.Json.Utilities case '\\': writer.Write(@"\\"); break; - //case '<': - //case '>': - //case '\'': - // StringUtils.WriteCharAsUnicode(writer, c); - // break; + case '\u0085': // Next Line + writer.Write(@"\u0085"); + break; + case '\u2028': // Line Separator + writer.Write(@"\u2028"); + break; + case '\u2029': // Paragraph Separator + writer.Write(@"\u2029"); + break; case '\'': // only escape if this charater is being used as the delimiter writer.Write((delimiter == '\'') ? @"\'" : @"'");