diff --git a/Src/Newtonsoft.Json.Silverlight/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json.Silverlight/Newtonsoft.Json.Silverlight.csproj index f407d9df..3f45c100 100644 --- a/Src/Newtonsoft.Json.Silverlight/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Silverlight/Newtonsoft.Json.Silverlight.csproj @@ -148,6 +148,9 @@ Linq\JToken.cs + + Linq\JTokenEqualityComparer.cs + Linq\JValue.cs diff --git a/Src/Newtonsoft.Json.Tests.Silverlight/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests.Silverlight/Newtonsoft.Json.Tests.Silverlight.csproj index 2bbef64b..14e1bf35 100644 --- a/Src/Newtonsoft.Json.Tests.Silverlight/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests.Silverlight/Newtonsoft.Json.Tests.Silverlight.csproj @@ -90,6 +90,15 @@ JsonTokenWriterTest.cs + + Linq\JArrayTests.cs + + + Linq\JObjectTests.cs + + + Linq\JTokenEqualityComparerTests.cs + Linq\JTokenTests.cs diff --git a/Src/Newtonsoft.Json.Tests/JavaScriptConvertTest.cs b/Src/Newtonsoft.Json.Tests/JavaScriptConvertTest.cs index a6098906..c55af74b 100644 --- a/Src/Newtonsoft.Json.Tests/JavaScriptConvertTest.cs +++ b/Src/Newtonsoft.Json.Tests/JavaScriptConvertTest.cs @@ -113,5 +113,65 @@ now brown cow?", '"', true); string json = JavaScriptConvert.ToString(StringComparison.CurrentCultureIgnoreCase); Assert.AreEqual("1", json); } + + [Test] + public void ObjectToString() + { + object value; + + value = 1; + Assert.AreEqual("1", JavaScriptConvert.ToString(value)); + + value = 1.1; + Assert.AreEqual("1.1", JavaScriptConvert.ToString(value)); + + value = 1.1m; + Assert.AreEqual("1.1", JavaScriptConvert.ToString(value)); + + value = (float)1.1; + Assert.AreEqual("1.1", JavaScriptConvert.ToString(value)); + + value = (short)1; + Assert.AreEqual("1", JavaScriptConvert.ToString(value)); + + value = (long)1; + Assert.AreEqual("1", JavaScriptConvert.ToString(value)); + + value = (byte)1; + Assert.AreEqual("1", JavaScriptConvert.ToString(value)); + + value = (uint)1; + Assert.AreEqual("1", JavaScriptConvert.ToString(value)); + + value = (ushort)1; + Assert.AreEqual("1", JavaScriptConvert.ToString(value)); + + value = (sbyte)1; + Assert.AreEqual("1", JavaScriptConvert.ToString(value)); + + value = (ulong)1; + Assert.AreEqual("1", JavaScriptConvert.ToString(value)); + + value = new DateTime(JavaScriptConvert.InitialJavaScriptDateTicks, DateTimeKind.Utc); + Assert.AreEqual(@"""\/Date(0)\/""", JavaScriptConvert.ToString(value)); + + value = new DateTimeOffset(JavaScriptConvert.InitialJavaScriptDateTicks, TimeSpan.Zero); + Assert.AreEqual(@"""\/Date(0+0000)\/""", JavaScriptConvert.ToString(value)); + + value = null; + Assert.AreEqual("null", JavaScriptConvert.ToString(value)); + + value = DBNull.Value; + Assert.AreEqual("null", JavaScriptConvert.ToString(value)); + + value = "I am a string"; + Assert.AreEqual(@"""I am a string""", JavaScriptConvert.ToString(value)); + + value = true; + Assert.AreEqual("true", JavaScriptConvert.ToString(value)); + + value = 'c'; + Assert.AreEqual(@"""c""", JavaScriptConvert.ToString(value)); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs new file mode 100644 index 00000000..ec88fcee --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JArrayTests : TestFixtureBase + { + [Test] + public void Clear() + { + JArray a = new JArray { 1 }; + Assert.AreEqual(1, a.Count); + + a.Clear(); + Assert.AreEqual(0, a.Count); + } + + [Test] + public void Contains() + { + JValue v = new JValue(1); + + JArray a = new JArray { v }; + + Assert.AreEqual(false, a.Contains(new JValue(2))); + Assert.AreEqual(false, a.Contains(new JValue(1))); + Assert.AreEqual(false, a.Contains(null)); + Assert.AreEqual(true, a.Contains(v)); + } + + [Test] + public void GenericCollectionCopyTo() + { + JArray j = new JArray(); + j.Add(new JValue(1)); + j.Add(new JValue(2)); + j.Add(new JValue(3)); + Assert.AreEqual(3, j.Count); + + JToken[] a = new JToken[5]; + + ((ICollection)j).CopyTo(a, 1); + + Assert.AreEqual(null, a[0]); + + Assert.AreEqual(1, (int)a[1]); + + Assert.AreEqual(2, (int)a[2]); + + Assert.AreEqual(3, (int)a[3]); + + Assert.AreEqual(null, a[4]); + + } + + [Test] + [ExpectedException(typeof(ArgumentNullException), ExpectedMessage = @"Value cannot be null. +Parameter name: array")] + public void GenericCollectionCopyToNullArrayShouldThrow() + { + JArray j = new JArray(); + ((ICollection)j).CopyTo(null, 0); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"arrayIndex is less than 0. +Parameter name: arrayIndex")] + public void GenericCollectionCopyToNegativeArrayIndexShouldThrow() + { + JArray j = new JArray(); + ((ICollection)j).CopyTo(new JToken[1], -1); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"arrayIndex is equal to or greater than the length of array.")] + public void GenericCollectionCopyToArrayIndexEqualGreaterToArrayLengthShouldThrow() + { + JArray j = new JArray(); + ((ICollection)j).CopyTo(new JToken[1], 1); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array.")] + public void GenericCollectionCopyToInsufficientArrayCapacity() + { + JArray j = new JArray(); + j.Add(new JValue(1)); + j.Add(new JValue(2)); + j.Add(new JValue(3)); + + ((ICollection)j).CopyTo(new JToken[3], 1); + } + + [Test] + public void Remove() + { + JValue v = new JValue(1); + JArray j = new JArray(); + j.Add(v); + + Assert.AreEqual(1, j.Count); + + Assert.AreEqual(false, j.Remove(new JValue(1))); + Assert.AreEqual(false, j.Remove(null)); + Assert.AreEqual(true, j.Remove(v)); + Assert.AreEqual(false, j.Remove(v)); + + Assert.AreEqual(0, j.Count); + } + + [Test] + public void IndexOf() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(1); + JValue v3 = new JValue(1); + + JArray j = new JArray(); + + j.Add(v1); + Assert.AreEqual(0, j.IndexOf(v1)); + + j.Add(v2); + Assert.AreEqual(0, j.IndexOf(v1)); + Assert.AreEqual(1, j.IndexOf(v2)); + + j.AddFirst(v3); + Assert.AreEqual(1, j.IndexOf(v1)); + Assert.AreEqual(2, j.IndexOf(v2)); + Assert.AreEqual(0, j.IndexOf(v3)); + + v3.Remove(); + Assert.AreEqual(0, j.IndexOf(v1)); + Assert.AreEqual(1, j.IndexOf(v2)); + Assert.AreEqual(-1, j.IndexOf(v3)); + } + + [Test] + public void RemoveAt() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(1); + JValue v3 = new JValue(1); + + JArray j = new JArray(); + + j.Add(v1); + j.Add(v2); + j.Add(v3); + + Assert.AreEqual(true, j.Contains(v1)); + j.RemoveAt(0); + Assert.AreEqual(false, j.Contains(v1)); + + Assert.AreEqual(true, j.Contains(v3)); + j.RemoveAt(1); + Assert.AreEqual(false, j.Contains(v3)); + + Assert.AreEqual(1, j.Count); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"index is equal to or greater than Count. +Parameter name: index")] + public void RemoveAtOutOfRangeIndexShouldError() + { + JArray j = new JArray(); + j.RemoveAt(0); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"index is less than 0. +Parameter name: index")] + public void RemoveAtNegativeIndexShouldError() + { + JArray j = new JArray(); + j.RemoveAt(-1); + } + + [Test] + public void Insert() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(2); + JValue v3 = new JValue(3); + JValue v4 = new JValue(4); + + JArray j = new JArray(); + + j.Add(v1); + j.Add(v2); + j.Add(v3); + j.Insert(1, v4); + + Assert.AreEqual(0, j.IndexOf(v1)); + Assert.AreEqual(1, j.IndexOf(v4)); + Assert.AreEqual(2, j.IndexOf(v2)); + Assert.AreEqual(3, j.IndexOf(v3)); + } + + [Test] + public void AddFirstAddedTokenShouldBeFirst() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(2); + JValue v3 = new JValue(3); + + JArray j = new JArray(); + Assert.AreEqual(null, j.First); + Assert.AreEqual(null, j.Last); + + j.AddFirst(v1); + Assert.AreEqual(v1, j.First); + Assert.AreEqual(v1, j.Last); + + j.AddFirst(v2); + Assert.AreEqual(v2, j.First); + Assert.AreEqual(v1, j.Last); + + j.AddFirst(v3); + Assert.AreEqual(v3, j.First); + Assert.AreEqual(v1, j.Last); + } + + [Test] + public void InsertShouldInsertAtZeroIndex() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(2); + + JArray j = new JArray(); + + j.Insert(0, v1); + Assert.AreEqual(0, j.IndexOf(v1)); + + j.Insert(0, v2); + Assert.AreEqual(1, j.IndexOf(v1)); + Assert.AreEqual(0, j.IndexOf(v2)); + } + + [Test] + public void InsertNull() + { + JArray j = new JArray(); + j.Insert(0, null); + + Assert.AreEqual(null, ((JValue)j[0]).Value); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"Specified argument was out of the range of valid values. +Parameter name: index")] + public void InsertNegativeIndexShouldThrow() + { + JArray j = new JArray(); + j.Insert(-1, new JValue(1)); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"Specified argument was out of the range of valid values. +Parameter name: index")] + public void InsertOutOfRangeIndexShouldThrow() + { + JArray j = new JArray(); + j.Insert(2, new JValue(1)); + } + + [Test] + public void Item() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(2); + JValue v3 = new JValue(3); + JValue v4 = new JValue(4); + + JArray j = new JArray(); + + j.Add(v1); + j.Add(v2); + j.Add(v3); + + j[1] = v4; + + Assert.AreEqual(null, v2.Parent); + Assert.AreEqual(-1, j.IndexOf(v2)); + Assert.AreEqual(j, v4.Parent); + Assert.AreEqual(1, j.IndexOf(v4)); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs new file mode 100644 index 00000000..33d5fce0 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JObjectTests : TestFixtureBase + { + [Test] + public void TryGetValue() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + Assert.AreEqual(1, o.Children().Count()); + + JToken t; + Assert.AreEqual(false, o.TryGetValue("sdf", out t)); + Assert.AreEqual(null, t); + + Assert.AreEqual(false, o.TryGetValue(null, out t)); + Assert.AreEqual(null, t); + + Assert.AreEqual(true, o.TryGetValue("PropertyNameValue", out t)); + Assert.AreEqual(true, JToken.DeepEquals(new JValue(1), t)); + } + + [Test] + public void DictionaryItemShouldSet() + { + JObject o = new JObject(); + o["PropertyNameValue"] = new JValue(1); + Assert.AreEqual(1, o.Children().Count()); + + JToken t; + Assert.AreEqual(true, o.TryGetValue("PropertyNameValue", out t)); + Assert.AreEqual(true, JToken.DeepEquals(new JValue(1), t)); + + o["PropertyNameValue"] = new JValue(2); + Assert.AreEqual(1, o.Children().Count()); + + Assert.AreEqual(true, o.TryGetValue("PropertyNameValue", out t)); + Assert.AreEqual(true, JToken.DeepEquals(new JValue(2), t)); + + o["PropertyNameValue"] = null; + Assert.AreEqual(1, o.Children().Count()); + + Assert.AreEqual(true, o.TryGetValue("PropertyNameValue", out t)); + Assert.AreEqual(true, JToken.DeepEquals(new JValue((object)null), t)); + } + + [Test] + public void Remove() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + Assert.AreEqual(1, o.Children().Count()); + + Assert.AreEqual(false, o.Remove("sdf")); + Assert.AreEqual(false, o.Remove(null)); + Assert.AreEqual(true, o.Remove("PropertyNameValue")); + + Assert.AreEqual(0, o.Children().Count()); + } + + [Test] + public void GenericCollectionRemove() + { + JValue v = new JValue(1); + JObject o = new JObject(); + o.Add("PropertyNameValue", v); + Assert.AreEqual(1, o.Children().Count()); + + Assert.AreEqual(false, ((ICollection>)o).Remove(new KeyValuePair("PropertyNameValue1", new JValue(1)))); + Assert.AreEqual(false, ((ICollection>)o).Remove(new KeyValuePair("PropertyNameValue", new JValue(2)))); + Assert.AreEqual(false, ((ICollection>)o).Remove(new KeyValuePair("PropertyNameValue", new JValue(1)))); + Assert.AreEqual(true, ((ICollection>)o).Remove(new KeyValuePair("PropertyNameValue", v))); + + Assert.AreEqual(0, o.Children().Count()); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Can not add property PropertyNameValue to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.")] + public void DuplicatePropertyNameShouldThrow() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", null); + o.Add("PropertyNameValue", null); + } + + [Test] + public void GenericDictionaryAdd() + { + JObject o = new JObject(); + + o.Add("PropertyNameValue", new JValue(1)); + Assert.AreEqual(1, (int)o["PropertyNameValue"]); + + o.Add("PropertyNameValue1", null); + Assert.AreEqual(null, ((JValue)o["PropertyNameValue1"]).Value); + + Assert.AreEqual(2, o.Children().Count()); + } + + [Test] + public void GenericCollectionAdd() + { + JObject o = new JObject(); + ((ICollection>)o).Add(new KeyValuePair("PropertyNameValue", new JValue(1))); + + Assert.AreEqual(1, (int)o["PropertyNameValue"]); + Assert.AreEqual(1, o.Children().Count()); + } + + [Test] + public void GenericCollectionClear() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + Assert.AreEqual(1, o.Children().Count()); + + JProperty p = (JProperty)o.Children().ElementAt(0); + + ((ICollection>)o).Clear(); + Assert.AreEqual(0, o.Children().Count()); + + Assert.AreEqual(null, p.Parent); + } + + [Test] + public void GenericCollectionContains() + { + JValue v = new JValue(1); + JObject o = new JObject(); + o.Add("PropertyNameValue", v); + Assert.AreEqual(1, o.Children().Count()); + + bool contains = ((ICollection>)o).Contains(new KeyValuePair("PropertyNameValue", new JValue(1))); + Assert.AreEqual(false, contains); + + contains = ((ICollection>)o).Contains(new KeyValuePair("PropertyNameValue", v)); + Assert.AreEqual(true, contains); + + contains = ((ICollection>)o).Contains(new KeyValuePair("PropertyNameValue", new JValue(2))); + Assert.AreEqual(false, contains); + + contains = ((ICollection>)o).Contains(new KeyValuePair("PropertyNameValue1", new JValue(1))); + Assert.AreEqual(false, contains); + + contains = ((ICollection>)o).Contains(default(KeyValuePair)); + Assert.AreEqual(false, contains); + } + + [Test] + public void GenericDictionaryContains() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + Assert.AreEqual(1, o.Children().Count()); + + bool contains = ((IDictionary)o).ContainsKey("PropertyNameValue"); + Assert.AreEqual(true, contains); + } + + [Test] + public void GenericCollectionCopyTo() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + o.Add("PropertyNameValue2", new JValue(2)); + o.Add("PropertyNameValue3", new JValue(3)); + Assert.AreEqual(3, o.Children().Count()); + + KeyValuePair[] a = new KeyValuePair[5]; + + ((ICollection>)o).CopyTo(a, 1); + + Assert.AreEqual(default(KeyValuePair), a[0]); + + Assert.AreEqual("PropertyNameValue", a[1].Key); + Assert.AreEqual(1, (int)a[1].Value); + + Assert.AreEqual("PropertyNameValue2", a[2].Key); + Assert.AreEqual(2, (int)a[2].Value); + + Assert.AreEqual("PropertyNameValue3", a[3].Key); + Assert.AreEqual(3, (int)a[3].Value); + + Assert.AreEqual(default(KeyValuePair), a[4]); + + } + + [Test] + [ExpectedException(typeof(ArgumentNullException), ExpectedMessage = @"Value cannot be null. +Parameter name: array")] + public void GenericCollectionCopyToNullArrayShouldThrow() + { + JObject o = new JObject(); + ((ICollection>)o).CopyTo(null, 0); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"arrayIndex is less than 0. +Parameter name: arrayIndex")] + public void GenericCollectionCopyToNegativeArrayIndexShouldThrow() + { + JObject o = new JObject(); + ((ICollection>)o).CopyTo(new KeyValuePair[1], -1); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"arrayIndex is equal to or greater than the length of array.")] + public void GenericCollectionCopyToArrayIndexEqualGreaterToArrayLengthShouldThrow() + { + JObject o = new JObject(); + ((ICollection>)o).CopyTo(new KeyValuePair[1], 1); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array.")] + public void GenericCollectionCopyToInsufficientArrayCapacity() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + o.Add("PropertyNameValue2", new JValue(2)); + o.Add("PropertyNameValue3", new JValue(3)); + + ((ICollection>)o).CopyTo(new KeyValuePair[3], 1); + } + } +} diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenEqualityComparerTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenEqualityComparerTests.cs new file mode 100644 index 00000000..c37bd2f0 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenEqualityComparerTests.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JTokenEqualityComparerTests : TestFixtureBase + { + [Test] + public void JValueDictionary() + { + Dictionary dic = new Dictionary(JToken.EqualityComparer); + JValue v11 = new JValue(1); + JValue v12 = new JValue(1); + + dic[v11] = 1; + dic[v12] += 1; + Assert.AreEqual(2, dic[v11]); + } + + [Test] + public void JArrayDictionary() + { + Dictionary dic = new Dictionary(JToken.EqualityComparer); + JArray v11 = new JArray(); + JArray v12 = new JArray(); + + dic[v11] = 1; + dic[v12] += 1; + Assert.AreEqual(2, dic[v11]); + } + + [Test] + public void JObjectDictionary() + { + Dictionary dic = new Dictionary(JToken.EqualityComparer); + JObject v11 = new JObject() { { "Test", new JValue(1) }, { "Test1", new JValue(1) } }; + JObject v12 = new JObject() { { "Test", new JValue(1) }, { "Test1", new JValue(1) } }; + + dic[v11] = 1; + dic[v12] += 1; + Assert.AreEqual(2, dic[v11]); + } + + [Test] + public void JConstructorDictionary() + { + Dictionary dic = new Dictionary(JToken.EqualityComparer); + JConstructor v11 = new JConstructor("ConstructorValue"); + JConstructor v12 = new JConstructor("ConstructorValue"); + + dic[v11] = 1; + dic[v12] += 1; + Assert.AreEqual(2, dic[v11]); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs index 03d1454f..fee72e56 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs @@ -419,7 +419,7 @@ namespace Newtonsoft.Json.Tests.Linq } [Test] - [ExpectedException(typeof(Exception), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JProperty to Newtonsoft.Json.Linq.JArray")] + [ExpectedException(typeof(Exception), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JProperty to Newtonsoft.Json.Linq.JArray.")] public void AddPropertyToArray() { JArray a = new JArray(); @@ -427,7 +427,7 @@ namespace Newtonsoft.Json.Tests.Linq } [Test] - [ExpectedException(typeof(Exception), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject")] + [ExpectedException(typeof(Exception), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.")] public void AddValueToObject() { JObject o = new JObject(); diff --git a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs index d74dbe23..365dfb54 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs @@ -536,7 +536,7 @@ keyword such as type of business."" [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"Accessed JConstructor values with invalid key value: ""purple"". Argument position index expected.")] public void JConstructorStringIndex() { - JConstructor c = new JConstructor(); + JConstructor c = new JConstructor("ConstructorValue"); Assert.AreEqual(null, c["purple"]); } diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index 29f702be..6bb6f03f 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -78,6 +78,9 @@ + + + diff --git a/Src/Newtonsoft.Json/JavaScriptConvert.cs b/Src/Newtonsoft.Json/JavaScriptConvert.cs index ec7d34fc..6c54dd0f 100644 --- a/Src/Newtonsoft.Json/JavaScriptConvert.cs +++ b/Src/Newtonsoft.Json/JavaScriptConvert.cs @@ -341,49 +341,88 @@ namespace Newtonsoft.Json switch (convertible.GetTypeCode()) { case TypeCode.String: - return ToString((string)convertible); + return ToString(convertible.ToString(CultureInfo.InvariantCulture)); case TypeCode.Char: - return ToString((char)convertible); + return ToString(convertible.ToChar(CultureInfo.InvariantCulture)); case TypeCode.Boolean: - return ToString((bool)convertible); + return ToString(convertible.ToBoolean(CultureInfo.InvariantCulture)); case TypeCode.SByte: - return ToString((sbyte)convertible); + return ToString(convertible.ToSByte(CultureInfo.InvariantCulture)); case TypeCode.Int16: - return ToString((short)convertible); + return ToString(convertible.ToInt16(CultureInfo.InvariantCulture)); case TypeCode.UInt16: - return ToString((ushort)convertible); + return ToString(convertible.ToUInt16(CultureInfo.InvariantCulture)); case TypeCode.Int32: - return ToString((int)convertible); + return ToString(convertible.ToInt32(CultureInfo.InvariantCulture)); case TypeCode.Byte: - return ToString((byte)convertible); + return ToString(convertible.ToByte(CultureInfo.InvariantCulture)); case TypeCode.UInt32: - return ToString((uint)convertible); + return ToString(convertible.ToUInt32(CultureInfo.InvariantCulture)); case TypeCode.Int64: - return ToString((long)convertible); + return ToString(convertible.ToInt64(CultureInfo.InvariantCulture)); case TypeCode.UInt64: - return ToString((ulong)convertible); + return ToString(convertible.ToUInt64(CultureInfo.InvariantCulture)); case TypeCode.Single: - return ToString((float)convertible); + return ToString(convertible.ToSingle(CultureInfo.InvariantCulture)); case TypeCode.Double: - return ToString((double)convertible); + return ToString(convertible.ToDouble(CultureInfo.InvariantCulture)); case TypeCode.DateTime: - return ToString((DateTime)convertible); + return ToString(convertible.ToDateTime(CultureInfo.InvariantCulture)); case TypeCode.Decimal: - return ToString((decimal)convertible); + return ToString(convertible.ToDecimal(CultureInfo.InvariantCulture)); + case TypeCode.DBNull: + return Null; } } else if (value is DateTimeOffset) { return ToString((DateTimeOffset)value); } - else if (value is Guid) - { - return ToString((Guid)value); - } throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType())); } + internal static bool IsJsonPrimitive(object value) + { + if (value == null) + { + return true; + } + else if (value is IConvertible) + { + IConvertible convertible = value as IConvertible; + + switch (convertible.GetTypeCode()) + { + case TypeCode.String: + case TypeCode.Char: + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.Byte: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.DateTime: + case TypeCode.Decimal: + case TypeCode.DBNull: + return true; + default: + return false; + } + } + else if (value is DateTimeOffset) + { + return true; + } + + return false; + } + /// /// Serializes the specified object to a JSON string. /// diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 516944a2..2cfabe3f 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -645,68 +645,9 @@ namespace Newtonsoft.Json { converter.WriteJson(writer, value); } - else if (value is IConvertible) + else if (JavaScriptConvert.IsJsonPrimitive(value)) { - IConvertible convertible = value as IConvertible; - - switch (convertible.GetTypeCode()) - { - case TypeCode.String: - writer.WriteValue(convertible.ToString(CultureInfo.InvariantCulture)); - break; - case TypeCode.Char: - writer.WriteValue(convertible.ToChar(CultureInfo.InvariantCulture)); - break; - case TypeCode.Boolean: - writer.WriteValue(convertible.ToBoolean(CultureInfo.InvariantCulture)); - break; - case TypeCode.SByte: - writer.WriteValue(convertible.ToSByte(CultureInfo.InvariantCulture)); - break; - case TypeCode.Int16: - writer.WriteValue(convertible.ToInt16(CultureInfo.InvariantCulture)); - break; - case TypeCode.UInt16: - writer.WriteValue(convertible.ToUInt16(CultureInfo.InvariantCulture)); - break; - case TypeCode.Int32: - writer.WriteValue(convertible.ToInt32(CultureInfo.InvariantCulture)); - break; - case TypeCode.Byte: - writer.WriteValue(convertible.ToByte(CultureInfo.InvariantCulture)); - break; - case TypeCode.UInt32: - writer.WriteValue(convertible.ToUInt32(CultureInfo.InvariantCulture)); - break; - case TypeCode.Int64: - writer.WriteValue(convertible.ToInt64(CultureInfo.InvariantCulture)); - break; - case TypeCode.UInt64: - writer.WriteValue(convertible.ToUInt64(CultureInfo.InvariantCulture)); - break; - case TypeCode.Single: - writer.WriteValue(convertible.ToSingle(CultureInfo.InvariantCulture)); - break; - case TypeCode.Double: - writer.WriteValue(convertible.ToDouble(CultureInfo.InvariantCulture)); - break; - case TypeCode.DateTime: - writer.WriteValue(convertible.ToDateTime(CultureInfo.InvariantCulture)); - break; - case TypeCode.Decimal: - writer.WriteValue(convertible.ToDecimal(CultureInfo.InvariantCulture)); - break; - case TypeCode.DBNull: - writer.WriteNull(); - break; - default: - SerializeObject(writer, value); - break; - } - } - else if (value is DateTimeOffset) - { - writer.WriteValue((DateTimeOffset)value); + writer.WriteValue(value); } else if (value is IList) { diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index 7414cd82..b727933a 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -736,6 +736,82 @@ namespace Newtonsoft.Json { AutoComplete(JsonToken.Date); } + + /// + /// Writes a value. + /// An error will raised if the value cannot be written as a single JSON token. + /// + /// The value to write. + public virtual void WriteValue(object value) + { + if (value == null) + { + WriteNull(); + } + else if (value is IConvertible) + { + IConvertible convertible = value as IConvertible; + + switch (convertible.GetTypeCode()) + { + case TypeCode.String: + WriteValue(convertible.ToString(CultureInfo.InvariantCulture)); + return; + case TypeCode.Char: + WriteValue(convertible.ToChar(CultureInfo.InvariantCulture)); + return; + case TypeCode.Boolean: + WriteValue(convertible.ToBoolean(CultureInfo.InvariantCulture)); + return; + case TypeCode.SByte: + WriteValue(convertible.ToSByte(CultureInfo.InvariantCulture)); + return; + case TypeCode.Int16: + WriteValue(convertible.ToInt16(CultureInfo.InvariantCulture)); + return; + case TypeCode.UInt16: + WriteValue(convertible.ToUInt16(CultureInfo.InvariantCulture)); + return; + case TypeCode.Int32: + WriteValue(convertible.ToInt32(CultureInfo.InvariantCulture)); + return; + case TypeCode.Byte: + WriteValue(convertible.ToByte(CultureInfo.InvariantCulture)); + return; + case TypeCode.UInt32: + WriteValue(convertible.ToUInt32(CultureInfo.InvariantCulture)); + return; + case TypeCode.Int64: + WriteValue(convertible.ToInt64(CultureInfo.InvariantCulture)); + return; + case TypeCode.UInt64: + WriteValue(convertible.ToUInt64(CultureInfo.InvariantCulture)); + return; + case TypeCode.Single: + WriteValue(convertible.ToSingle(CultureInfo.InvariantCulture)); + return; + case TypeCode.Double: + WriteValue(convertible.ToDouble(CultureInfo.InvariantCulture)); + return; + case TypeCode.DateTime: + WriteValue(convertible.ToDateTime(CultureInfo.InvariantCulture)); + return; + case TypeCode.Decimal: + WriteValue(convertible.ToDecimal(CultureInfo.InvariantCulture)); + return; + case TypeCode.DBNull: + WriteNull(); + return; + } + } + else if (value is DateTimeOffset) + { + WriteValue((DateTimeOffset)value); + return; + } + + throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType())); + } #endregion /// diff --git a/Src/Newtonsoft.Json/Linq/JArray.cs b/Src/Newtonsoft.Json/Linq/JArray.cs index 1441a6c3..4d4e08b6 100644 --- a/Src/Newtonsoft.Json/Linq/JArray.cs +++ b/Src/Newtonsoft.Json/Linq/JArray.cs @@ -220,12 +220,22 @@ namespace Newtonsoft.Json.Linq public void Insert(int index, JToken item) { - JToken token = GetIndex(this, index); - AddInternal(false, token.Previous, item); + if (index == 0) + { + AddFirst(item); + } + else + { + JToken token = GetIndex(this, index); + AddInternal(false, token.Previous, item); + } } public void RemoveAt(int index) { + if (index < 0) + throw new ArgumentOutOfRangeException("index", "index is less than 0."); + int currentIndex = 0; foreach (JToken token in Children()) { @@ -237,18 +247,20 @@ namespace Newtonsoft.Json.Linq currentIndex++; } + + throw new ArgumentOutOfRangeException("index", "index is equal to or greater than Count."); } #endregion #region ICollection Members - void ICollection.Add(JToken item) + public void Add(JToken item) { Add((object)item); } - void ICollection.Clear() + public void Clear() { RemoveAll(); } @@ -260,6 +272,15 @@ namespace Newtonsoft.Json.Linq void ICollection.CopyTo(JToken[] array, int arrayIndex) { + if (array == null) + throw new ArgumentNullException("array"); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0."); + if (arrayIndex >= array.Length) + throw new ArgumentException("arrayIndex is equal to or greater than the length of array."); + if (Count > array.Length - arrayIndex) + throw new ArgumentException("The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array."); + int index = 0; foreach (JToken token in Children()) { @@ -278,7 +299,7 @@ namespace Newtonsoft.Json.Linq get { return false; } } - bool ICollection.Remove(JToken item) + public bool Remove(JToken item) { if (!((ICollection)this).Contains(item)) return false; @@ -288,5 +309,10 @@ namespace Newtonsoft.Json.Linq } #endregion + + internal override int GetDeepHashCode() + { + return ContentsHashCode(); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JConstructor.cs b/Src/Newtonsoft.Json/Linq/JConstructor.cs index 16836375..c381aaea 100644 --- a/Src/Newtonsoft.Json/Linq/JConstructor.cs +++ b/Src/Newtonsoft.Json/Linq/JConstructor.cs @@ -58,13 +58,6 @@ namespace Newtonsoft.Json.Linq get { return JsonTokenType.Constructor; } } - /// - /// Initializes a new instance of the class. - /// - public JConstructor() - { - } - /// /// Initializes a new instance of the class from another object. /// @@ -102,6 +95,8 @@ namespace Newtonsoft.Json.Linq /// The constructor name. public JConstructor(string name) { + ValidationUtils.ArgumentNotNullOrEmpty(name, "name"); + _name = name; } @@ -160,5 +155,10 @@ namespace Newtonsoft.Json.Linq return GetIndex(this, (int)key); } } + + internal override int GetDeepHashCode() + { + return _name.GetHashCode() ^ ContentsHashCode(); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 1ba028ab..d439845b 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -261,7 +261,7 @@ namespace Newtonsoft.Json.Linq if (previous != null) previous.Next = o; - if (isLast) + if (isLast || previous == null) _content = o; } } @@ -271,7 +271,7 @@ namespace Newtonsoft.Json.Linq ValidationUtils.ArgumentNotNull(o, "o"); if (o.Type == JsonTokenType.Property) - throw new Exception("Can not add {0} to {1}".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); + throw new Exception("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); } /// @@ -422,8 +422,7 @@ namespace Newtonsoft.Json.Linq parent = parent.Parent; break; case JsonToken.StartConstructor: - JConstructor constructor = new JConstructor(); - constructor.Name = r.Value.ToString(); + JConstructor constructor = new JConstructor(r.Value.ToString()); parent.AddObjectSkipNotify(constructor); parent = constructor; break; @@ -460,5 +459,15 @@ namespace Newtonsoft.Json.Linq } while (r.Read()); } + + internal int ContentsHashCode() + { + int hashCode = 0; + foreach (JToken item in Children()) + { + hashCode ^= item.GetDeepHashCode(); + } + return hashCode; + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 0aa031eb..232e2e08 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -83,7 +83,12 @@ namespace Newtonsoft.Json.Linq ValidationUtils.ArgumentNotNull(o, "o"); if (o.Type != JsonTokenType.Property) - throw new Exception("Can not add {0} to {1}".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); + throw new Exception("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); + + JProperty property = (JProperty)o; + bool matchingProperty = (Properties().Where(p => string.Compare(p.Name, property.Name, StringComparison.Ordinal) == 0).Count() > 0); + if (matchingProperty) + throw new Exception("Can not add property {0} to {1}. Property with the same name already exists on object.".FormatWith(CultureInfo.InvariantCulture, property.Name, GetType())); } internal override JToken CloneNode() @@ -319,10 +324,20 @@ namespace Newtonsoft.Json.Linq void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { + if (array == null) + throw new ArgumentNullException("array"); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0."); + if (arrayIndex >= array.Length) + throw new ArgumentException("arrayIndex is equal to or greater than the length of array."); + if (Count > array.Length - arrayIndex) + throw new ArgumentException("The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array."); + int index = 0; foreach (JProperty property in Properties()) { array[arrayIndex + index] = new KeyValuePair(property.Name, property.Value); + index++; } } @@ -358,5 +373,10 @@ namespace Newtonsoft.Json.Linq } #endregion + + internal override int GetDeepHashCode() + { + return ContentsHashCode(); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JProperty.cs b/Src/Newtonsoft.Json/Linq/JProperty.cs index 6e07d023..2a26df0c 100644 --- a/Src/Newtonsoft.Json/Linq/JProperty.cs +++ b/Src/Newtonsoft.Json/Linq/JProperty.cs @@ -110,7 +110,7 @@ namespace Newtonsoft.Json.Linq /// The property value. public JProperty(string name, object value) { - ValidationUtils.ArgumentNotNullOrEmpty(name, "name"); + ValidationUtils.ArgumentNotNull(name, "name"); _name = name; Value = CreateFromContent(value); @@ -149,5 +149,10 @@ namespace Newtonsoft.Json.Linq // return (JValue)value; //} + + internal override int GetDeepHashCode() + { + return _name.GetHashCode() ^ ((Value != null) ? Value.GetDeepHashCode() : 0); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index d1d612ad..b18d24fa 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -42,6 +42,18 @@ namespace Newtonsoft.Json.Linq { private JContainer _parent; internal JToken _next; + private static JTokenEqualityComparer _equalityComparer; + + public static JTokenEqualityComparer EqualityComparer + { + get + { + if (_equalityComparer == null) + _equalityComparer = new JTokenEqualityComparer(); + + return _equalityComparer; + } + } /// /// Gets or sets the parent. @@ -705,5 +717,7 @@ namespace Newtonsoft.Json.Linq { return Children().GetEnumerator(); } + + internal abstract int GetDeepHashCode(); } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JTokenEqualityComparer.cs b/Src/Newtonsoft.Json/Linq/JTokenEqualityComparer.cs new file mode 100644 index 00000000..999ccbb7 --- /dev/null +++ b/Src/Newtonsoft.Json/Linq/JTokenEqualityComparer.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Linq +{ + public class JTokenEqualityComparer : IEqualityComparer + { + public bool Equals(JToken x, JToken y) + { + return JToken.DeepEquals(x, y); + } + + public int GetHashCode(JToken obj) + { + if (obj == null) + return 0; + + return obj.GetDeepHashCode(); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index a72d7b2f..dd250d11 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -289,5 +289,12 @@ namespace Newtonsoft.Json.Linq throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", _valueType, "Unexpected token type."); } } + + internal override int GetDeepHashCode() + { + int valueHashCode = (_value != null) ? _value.GetHashCode() : 0; + + return _valueType.GetHashCode() ^ valueHashCode; + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JsonTokenWriter.cs b/Src/Newtonsoft.Json/Linq/JsonTokenWriter.cs index 79c27863..8b96f2f1 100644 --- a/Src/Newtonsoft.Json/Linq/JsonTokenWriter.cs +++ b/Src/Newtonsoft.Json/Linq/JsonTokenWriter.cs @@ -103,7 +103,7 @@ namespace Newtonsoft.Json.Linq { base.WriteStartConstructor(name); - AddParent(new JConstructor()); + AddParent(new JConstructor(name)); } /// diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 9ef8ca05..3a7cdb43 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -76,6 +76,7 @@ +