diff --git a/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/BindingTests.cs b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/BindingTests.cs index 6ee97a10..da7ef71b 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/BindingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/BindingTests.cs @@ -55,23 +55,22 @@ namespace Newtonsoft.Json.Tests.Linq.ComponentModel object value; - value = DataBinder.Eval(o, "First"); + value = (string)DataBinder.Eval(o, "First.Value"); Assert.AreEqual(value, (string)o["First"]); - value = DataBinder.Eval(o, "Second"); + value = DataBinder.Eval(o, "Second.Value"); Assert.AreEqual(value, (decimal)o["Second"]); value = DataBinder.Eval(o, "Third"); Assert.AreEqual(value, o["Third"]); - value = DataBinder.Eval(o, "Third[0]"); - // urg! - Assert.AreEqual((int)(JToken)value, (int)o["Third"][0]); + value = DataBinder.Eval(o, "Third[0].Value"); + Assert.AreEqual((int)value, (int)o["Third"][0]); - value = DataBinder.Eval(o, "Third[5].Fourth"); + value = DataBinder.Eval(o, "Third[5].Fourth.Value"); Assert.AreEqual(value, (string)o["Third"][5]["Fourth"]); - value = DataBinder.Eval(o, "Third[5].Fifth.Sixth"); + value = DataBinder.Eval(o, "Third[5].Fifth.Sixth.Value"); Assert.AreEqual(value, (string)o["Third"][5]["Fifth"]["Sixth"]); } } diff --git a/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs index 4e82bb9b..32d88ebd 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs @@ -44,7 +44,7 @@ namespace Newtonsoft.Json.Tests.Linq.ComponentModel JPropertyDescriptor prop1 = new JPropertyDescriptor("prop1", typeof(string)); JPropertyDescriptor prop2 = new JPropertyDescriptor("prop2", typeof(JArray)); - Assert.AreEqual("12345!", prop1.GetValue(o)); + Assert.AreEqual("12345!", ((JValue) prop1.GetValue(o)).Value); Assert.AreEqual(o["prop2"], prop2.GetValue(o)); } diff --git a/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs index fff3dc97..9906dac6 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using NUnit.Framework; @@ -366,5 +367,34 @@ Parameter name: index")] i++; } } + + +#if !SILVERLIGHT + [Test] + public void ITypedListGetItemProperties() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + ITypedList a = new JArray(new JObject(p1, p2)); + + PropertyDescriptorCollection propertyDescriptors = a.GetItemProperties(null); + Assert.IsNotNull(propertyDescriptors); + Assert.AreEqual(2, propertyDescriptors.Count); + Assert.AreEqual("Test1", propertyDescriptors[0].Name); + Assert.AreEqual("Test2", propertyDescriptors[1].Name); + } +#endif + + [Test] + public void AddArrayToSelf() + { + JArray a = new JArray(1, 2); + a.Add(a); + + Assert.AreEqual(3, a.Count); + Assert.AreEqual(1, (int)a[0]); + Assert.AreEqual(2, (int)a[1]); + Assert.AreNotSame(a, a[2]); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs index cea8516a..78174e01 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using Newtonsoft.Json.Tests.TestObjects; @@ -7,8 +8,11 @@ using NUnit.Framework; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Converters; using System.IO; +using System.Collections; #if !PocketPC && !SILVERLIGHT using System.Web.UI; +using System.Collections; +using System.ComponentModel; #endif namespace Newtonsoft.Json.Tests.Linq @@ -88,7 +92,7 @@ namespace Newtonsoft.Json.Tests.Linq } [Test] - [ExpectedException(typeof(Exception), ExpectedMessage = "Can not add property PropertyNameValue to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.")] + [ExpectedException(typeof(ArgumentException), 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(); @@ -195,7 +199,6 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(3, (int)a[3].Value); Assert.AreEqual(default(KeyValuePair), a[4]); - } [Test] @@ -560,6 +563,39 @@ Parameter name: arrayIndex")] //} } + [Test] + public void ReplaceJPropertyWithJPropertyWithSameName() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + + JObject o = new JObject(p1, p2); + IList l = o; + Assert.AreEqual(p1, l[0]); + Assert.AreEqual(p2, l[1]); + + JProperty p3 = new JProperty("Test1", "III"); + + p1.Replace(p3); + Assert.AreEqual(null, p1.Parent); + Assert.AreEqual(l, p3.Parent); + + Assert.AreEqual(p3, l[0]); + Assert.AreEqual(p2, l[1]); + + Assert.AreEqual(2, l.Count); + Assert.AreEqual(2, o.Properties().Count()); + + JProperty p4 = new JProperty("Test4", "IV"); + + p2.Replace(p4); + Assert.AreEqual(null, p2.Parent); + Assert.AreEqual(l, p4.Parent); + + Assert.AreEqual(p3, l[0]); + Assert.AreEqual(p4, l[1]); + } + #if !PocketPC && !SILVERLIGHT [Test] public void PropertyChanging() @@ -661,5 +697,615 @@ Parameter name: arrayIndex")] o["NullValue"] = null; Assert.AreEqual(4, changedCount); } + + [Test] + public void IListContains() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.IsTrue(l.Contains(p)); + Assert.IsFalse(l.Contains(new JProperty("Test", 1))); + } + + [Test] + public void IListIndexOf() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.AreEqual(0, l.IndexOf(p)); + Assert.AreEqual(-1, l.IndexOf(new JProperty("Test", 1))); + } + + [Test] + public void IListClear() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.AreEqual(1, l.Count); + + l.Clear(); + + Assert.AreEqual(0, l.Count); + } + + [Test] + public void IListCopyTo() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + object[] a = new object[l.Count]; + + l.CopyTo(a, 0); + + Assert.AreEqual(p1, a[0]); + Assert.AreEqual(p2, a[1]); + } + + [Test] + public void IListAdd() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l.Add(p3); + + Assert.AreEqual(3, l.Count); + Assert.AreEqual(p3, l[2]); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.")] + public void IListAddBadToken() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + l.Add(new JValue("Bad!")); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Argument is not a JToken.")] + public void IListAddBadValue() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + l.Add("Bad!"); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add property Test2 to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.")] + public void IListAddPropertyWithExistingName() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test2", "II"); + + l.Add(p3); + } + + [Test] + public void IListRemove() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + // won't do anything + l.Remove(p3); + Assert.AreEqual(2, l.Count); + + l.Remove(p1); + Assert.AreEqual(1, l.Count); + Assert.IsFalse(l.Contains(p1)); + Assert.IsTrue(l.Contains(p2)); + + l.Remove(p2); + Assert.AreEqual(0, l.Count); + Assert.IsFalse(l.Contains(p2)); + Assert.AreEqual(null, p2.Parent); + } + + [Test] + public void IListRemoveAt() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + // won't do anything + l.RemoveAt(0); + + l.Remove(p1); + Assert.AreEqual(1, l.Count); + + l.Remove(p2); + Assert.AreEqual(0, l.Count); + } + + [Test] + public void IListInsert() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l.Insert(1, p3); + Assert.AreEqual(l, p3.Parent); + + Assert.AreEqual(p1, l[0]); + Assert.AreEqual(p3, l[1]); + Assert.AreEqual(p2, l[2]); + } + + [Test] + public void IListIsReadOnly() + { + IList l = new JObject(); + Assert.IsFalse(l.IsReadOnly); + } + + [Test] + public void IListIsFixedSize() + { + IList l = new JObject(); + Assert.IsFalse(l.IsFixedSize); + } + + [Test] + public void IListSetItem() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l[0] = p3; + + Assert.AreEqual(p3, l[0]); + Assert.AreEqual(p2, l[1]); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add property Test3 to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.")] + public void IListSetItemAlreadyExists() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l[0] = p3; + l[1] = p3; + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.")] + public void IListSetItemInvalid() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + l[0] = new JValue(true); + } + + [Test] + public void IListSyncRoot() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + Assert.IsNotNull(l.SyncRoot); + } + + [Test] + public void IListIsSynchronized() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + Assert.IsFalse(l.IsSynchronized); + } + + [Test] + public void GenericListJTokenContains() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.IsTrue(l.Contains(p)); + Assert.IsFalse(l.Contains(new JProperty("Test", 1))); + } + + [Test] + public void GenericListJTokenIndexOf() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.AreEqual(0, l.IndexOf(p)); + Assert.AreEqual(-1, l.IndexOf(new JProperty("Test", 1))); + } + + [Test] + public void GenericListJTokenClear() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.AreEqual(1, l.Count); + + l.Clear(); + + Assert.AreEqual(0, l.Count); + } + + [Test] + public void GenericListJTokenCopyTo() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JToken[] a = new JToken[l.Count]; + + l.CopyTo(a, 0); + + Assert.AreEqual(p1, a[0]); + Assert.AreEqual(p2, a[1]); + } + + [Test] + public void GenericListJTokenAdd() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l.Add(p3); + + Assert.AreEqual(3, l.Count); + Assert.AreEqual(p3, l[2]); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.")] + public void GenericListJTokenAddBadToken() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + l.Add(new JValue("Bad!")); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.")] + public void GenericListJTokenAddBadValue() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + // string is implicitly converted to JValue + l.Add("Bad!"); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add property Test2 to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.")] + public void GenericListJTokenAddPropertyWithExistingName() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test2", "II"); + + l.Add(p3); + } + + [Test] + public void GenericListJTokenRemove() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + // won't do anything + Assert.IsFalse(l.Remove(p3)); + Assert.AreEqual(2, l.Count); + + Assert.IsTrue(l.Remove(p1)); + Assert.AreEqual(1, l.Count); + Assert.IsFalse(l.Contains(p1)); + Assert.IsTrue(l.Contains(p2)); + + Assert.IsTrue(l.Remove(p2)); + Assert.AreEqual(0, l.Count); + Assert.IsFalse(l.Contains(p2)); + Assert.AreEqual(null, p2.Parent); + } + + [Test] + public void GenericListJTokenRemoveAt() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + // won't do anything + l.RemoveAt(0); + + l.Remove(p1); + Assert.AreEqual(1, l.Count); + + l.Remove(p2); + Assert.AreEqual(0, l.Count); + } + + [Test] + public void GenericListJTokenInsert() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l.Insert(1, p3); + Assert.AreEqual(l, p3.Parent); + + Assert.AreEqual(p1, l[0]); + Assert.AreEqual(p3, l[1]); + Assert.AreEqual(p2, l[2]); + } + + [Test] + public void GenericListJTokenIsReadOnly() + { + IList l = new JObject(); + Assert.IsFalse(l.IsReadOnly); + } + + [Test] + public void GenericListJTokenSetItem() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l[0] = p3; + + Assert.AreEqual(p3, l[0]); + Assert.AreEqual(p2, l[1]); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add property Test3 to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.")] + public void GenericListJTokenSetItemAlreadyExists() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l[0] = p3; + l[1] = p3; + } + +#if !SILVERLIGHT + [Test] + public void IBindingListSortDirection() + { + IBindingList l = new JObject(); + Assert.AreEqual(ListSortDirection.Ascending, l.SortDirection); + } + + [Test] + public void IBindingListSortProperty() + { + IBindingList l = new JObject(); + Assert.AreEqual(null, l.SortProperty); + } + + [Test] + public void IBindingListSupportsChangeNotification() + { + IBindingList l = new JObject(); + Assert.AreEqual(true, l.SupportsChangeNotification); + } + + [Test] + public void IBindingListSupportsSearching() + { + IBindingList l = new JObject(); + Assert.AreEqual(false, l.SupportsSearching); + } + + [Test] + public void IBindingListSupportsSorting() + { + IBindingList l = new JObject(); + Assert.AreEqual(false, l.SupportsSorting); + } + + [Test] + public void IBindingListAllowEdit() + { + IBindingList l = new JObject(); + Assert.AreEqual(true, l.AllowEdit); + } + + [Test] + public void IBindingListAllowNew() + { + IBindingList l = new JObject(); + Assert.AreEqual(true, l.AllowNew); + } + + [Test] + public void IBindingListAllowRemove() + { + IBindingList l = new JObject(); + Assert.AreEqual(true, l.AllowRemove); + } + + [Test] + public void IBindingListAddIndex() + { + IBindingList l = new JObject(); + // do nothing + l.AddIndex(null); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void IBindingListApplySort() + { + IBindingList l = new JObject(); + l.ApplySort(null, ListSortDirection.Ascending); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void IBindingListRemoveSort() + { + IBindingList l = new JObject(); + l.RemoveSort(); + } + + [Test] + public void IBindingListRemoveIndex() + { + IBindingList l = new JObject(); + // do nothing + l.RemoveIndex(null); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void IBindingListFind() + { + IBindingList l = new JObject(); + l.Find(null, null); + } + + [Test] + public void IBindingListIsSorted() + { + IBindingList l = new JObject(); + Assert.AreEqual(false, l.IsSorted); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Could not determine new value to add to 'Newtonsoft.Json.Linq.JObject'.")] + public void IBindingListAddNew() + { + IBindingList l = new JObject(); + l.AddNew(); + } + + [Test] + public void IBindingListAddNewWithEvent() + { + JObject o = new JObject(); + o.AddingNew += (s, e) => e.NewObject = new JProperty("Property!"); + + IBindingList l = o; + object newObject = l.AddNew(); + Assert.IsNotNull(newObject); + + JProperty p = (JProperty) newObject; + Assert.AreEqual("Property!", p.Name); + Assert.AreEqual(o, p.Parent); + } + + [Test] + public void ITypedListGetListName() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + ITypedList l = new JObject(p1, p2); + + Assert.AreEqual(string.Empty, l.GetListName(null)); + } + + [Test] + public void ITypedListGetItemProperties() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + ITypedList l = new JObject(p1, p2); + + PropertyDescriptorCollection propertyDescriptors = l.GetItemProperties(null); + Assert.IsNull(propertyDescriptors); + } + + [Test] + public void ListChanged() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + JObject o = new JObject(p1, p2); + + ListChangedType? changedType = null; + int? index = null; + + o.ListChanged += (s, a) => + { + changedType = a.ListChangedType; + index = a.NewIndex; + }; + + JProperty p3 = new JProperty("Test3", "III"); + + o.Add(p3); + Assert.AreEqual(changedType, ListChangedType.ItemAdded); + Assert.AreEqual(index, 2); + Assert.AreEqual(p3, ((IList)o)[index.Value]); + + JProperty p4 = new JProperty("Test4", "IV"); + + ((IList) o)[index.Value] = p4; + Assert.AreEqual(changedType, ListChangedType.ItemChanged); + Assert.AreEqual(index, 2); + Assert.AreEqual(p4, ((IList)o)[index.Value]); + Assert.IsFalse(((IList)o).Contains(p3)); + Assert.IsTrue(((IList)o).Contains(p4)); + + o["Test1"] = 2; + Assert.AreEqual(changedType, ListChangedType.ItemChanged); + Assert.AreEqual(index, 0); + Assert.AreEqual(2, (int)o["Test1"]); + } +#endif } -} +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JPropertyTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JPropertyTests.cs index c221c74b..b389cc0e 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JPropertyTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JPropertyTests.cs @@ -1,5 +1,7 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using Newtonsoft.Json.Linq; @@ -10,6 +12,82 @@ namespace Newtonsoft.Json.Tests.Linq { public class JPropertyTests : TestFixtureBase { + [Test] + public void NullValue() + { + JProperty p = new JProperty("TestProperty", null); + Assert.IsNotNull(p.Value); + Assert.AreEqual(JTokenType.Null, p.Value.Type); + Assert.AreEqual(p, p.Value.Parent); + + p.Value = null; + Assert.IsNotNull(p.Value); + Assert.AreEqual(JTokenType.Null, p.Value.Type); + Assert.AreEqual(p, p.Value.Parent); + } + +#if !SILVERLIGHT + [Test] + public void ListChanged() + { + JProperty p = new JProperty("TestProperty", null); + IBindingList l = p; + + ListChangedType? listChangedType = null; + int? index = null; + + l.ListChanged += (sender, args) => + { + listChangedType = args.ListChangedType; + index = args.NewIndex; + }; + + p.Value = 1; + + Assert.AreEqual(ListChangedType.ItemChanged, listChangedType.Value); + Assert.AreEqual(0, index.Value); + } +#endif + + [Test] + public void IListCount() + { + JProperty p = new JProperty("TestProperty", null); + IList l = p; + + Assert.AreEqual(1, l.Count); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Cannot add or remove items from Newtonsoft.Json.Linq.JProperty.")] + public void IListClear() + { + JProperty p = new JProperty("TestProperty", null); + IList l = p; + + l.Clear(); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Newtonsoft.Json.Linq.JProperty cannot have multiple values.")] + public void IListAdd() + { + JProperty p = new JProperty("TestProperty", null); + IList l = p; + + l.Add(null); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Cannot add or remove items from Newtonsoft.Json.Linq.JProperty.")] + public void IListRemove() + { + JProperty p = new JProperty("TestProperty", null); + IList l = p; + + l.Remove(p.Value); + } + [Test] public void Load() { @@ -48,5 +126,14 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual("one", (string)a[0]); Assert.AreEqual("two", (string)a[1]); } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Newtonsoft.Json.Linq.JProperty cannot have multiple values.")] + public void IListGenericAdd() + { + IList t = new JProperty("error", new List { "one", "two" }); + t.Add(1); + t.Add(2); + } } } diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs index c5afb059..0e4d238a 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs @@ -94,7 +94,7 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(o.Property("Test1"), v.Parent); // new value should be cloned - Assert.AreNotEqual(p.Value, v); + Assert.AreNotSame(p.Value, v); Assert.AreEqual((DateTime)((JValue)p.Value[1]).Value, (DateTime)((JValue)v[1]).Value); @@ -107,7 +107,7 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreNotEqual(null, o.Parent); JProperty o2 = new JProperty("O2", o); - Assert.AreNotEqual(o1.Value, o2.Value); + Assert.AreNotSame(o1.Value, o2.Value); Assert.AreEqual(o1.Value.Children().Count(), o2.Value.Children().Count()); Assert.AreEqual(false, JToken.DeepEquals(o1, o2)); Assert.AreEqual(true, JToken.DeepEquals(o1.Value, o2.Value)); @@ -505,7 +505,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(ArgumentException), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JProperty to Newtonsoft.Json.Linq.JArray.")] public void AddPropertyToArray() { JArray a = new JArray(); @@ -513,7 +513,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(ArgumentException), 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/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 5f30620f..0b6f0d51 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -1076,7 +1076,7 @@ keyword such as type of business."" [Test] public void SerializeTypeProperty() { - TypeClass typeClass = new TypeClass {TypeProperty = typeof (bool)}; + TypeClass typeClass = new TypeClass { TypeProperty = typeof(bool) }; string json = JsonConvert.SerializeObject(typeClass); Assert.AreEqual(@"{""TypeProperty"":""System.Boolean""}", json); @@ -1221,7 +1221,7 @@ keyword such as type of business."" } [Test] - public void DeserializeGoogleGeoCode() + public void DeserializeGoogleGeoCode() { string json = @"{ ""name"": ""1600 Amphitheatre Parkway, Mountain View, CA, USA"", @@ -1480,7 +1480,7 @@ keyword such as type of business."" ""ReferenceLoopHandlingSerializeProperty"": null }", json); - json = JsonConvert.SerializeObject(o, Formatting.Indented, new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}); + json = JsonConvert.SerializeObject(o, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); Assert.AreEqual(@"{ ""DefaultValueHandlingIncludeProperty"": ""Default!"", @@ -2026,7 +2026,7 @@ keyword such as type of business."" ""Sizes"": null } ]"; - + List products = JsonConvert.DeserializeObject>(json); Console.WriteLine(products.Count); @@ -2202,5 +2202,30 @@ keyword such as type of business."" Assert.AreEqual(1, (int)a[0]); Assert.AreEqual(5, (int)a[a.Count - 1]); } + + [JsonObject(MemberSerialization.OptIn)] + public class list + { + [JsonProperty] + public string id { get; set; } + [JsonProperty] + public List items { get; set; } + + } + + [JsonObject(MemberSerialization.OptIn)] + public class ListItem + { + [JsonProperty] + public string id { get; set; } + } + + [Test] + public void DeserializeOptInClasses() + { + string json = @"{id: ""12"", name: ""test"", items: [{id: ""112"", name: ""testing""}]}"; + + list l = JsonConvert.DeserializeObject(json); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs b/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs index 81edf638..b3445523 100644 --- a/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs +++ b/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs @@ -80,8 +80,8 @@ namespace Newtonsoft.Json.Linq.ComponentModel public override object GetValue(object component) { JToken token = CastInstance(component)[Name]; - if (token is JValue) - return ((JValue) token).Value; + //if (token is JValue) + // return ((JValue) token).Value; return token; } diff --git a/Src/Newtonsoft.Json/Linq/JArray.cs b/Src/Newtonsoft.Json/Linq/JArray.cs index 3d1e666f..abb3481e 100644 --- a/Src/Newtonsoft.Json/Linq/JArray.cs +++ b/Src/Newtonsoft.Json/Linq/JArray.cs @@ -25,6 +25,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using Newtonsoft.Json.Utilities; @@ -87,7 +88,7 @@ namespace Newtonsoft.Json.Linq return (t != null && ContentsEqual(t)); } - internal override JToken CloneNode() + internal override JToken CloneToken() { return new JArray(this); } @@ -138,7 +139,6 @@ namespace Newtonsoft.Json.Linq throw new ArgumentException("An item of type {0} cannot be added to content.".FormatWith(CultureInfo.InvariantCulture, o.Type)); } - /// /// Creates a from an object. /// @@ -195,7 +195,7 @@ namespace Newtonsoft.Json.Linq if (!(key is int)) throw new ArgumentException("Accessed JArray values with invalid key value: {0}. Array position index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); - return this[(int)key]; + return GetItem((int)key); } } @@ -205,15 +205,8 @@ namespace Newtonsoft.Json.Linq /// public JToken this[int index] { - get - { - return GetIndex(this, index); - } - set - { - JToken token = this[(object)index]; - token.Replace(value); - } + get { return GetItem(index); } + set { SetItem(index, value); } } #region IList Members @@ -227,16 +220,7 @@ namespace Newtonsoft.Json.Linq /// public int IndexOf(JToken item) { - int index = 0; - foreach (JToken token in Children()) - { - if (token == item) - return index; - - index++; - } - - return -1; + return IndexOfItem(item); } /// @@ -249,15 +233,7 @@ namespace Newtonsoft.Json.Linq /// The is read-only. public void Insert(int index, JToken item) { - if (index == 0) - { - AddFirst(item); - } - else - { - JToken token = GetIndex(this, index); - AddInternal(false, token.Previous, item); - } + InsertItem(index, item); } /// @@ -269,22 +245,7 @@ namespace Newtonsoft.Json.Linq /// The is read-only. 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()) - { - if (index == currentIndex) - { - token.Remove(); - return; - } - - currentIndex++; - } - - throw new ArgumentOutOfRangeException("index", "index is equal to or greater than Count."); + RemoveItemAt(index); } #endregion @@ -307,7 +268,7 @@ namespace Newtonsoft.Json.Linq /// The is read-only. public void Clear() { - RemoveAll(); + ClearItems(); } /// @@ -319,26 +280,12 @@ namespace Newtonsoft.Json.Linq /// public bool Contains(JToken item) { - return Children().Contains(item); + return ContainsItem(item); } 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()) - { - array[arrayIndex + index] = token; - index++; - } + CopyItemsTo(array, arrayIndex); } /// @@ -348,7 +295,7 @@ namespace Newtonsoft.Json.Linq /// The number of elements contained in the . public int Count { - get { return Children().Count(); } + get { return CountItems(); } } bool ICollection.IsReadOnly @@ -364,13 +311,9 @@ namespace Newtonsoft.Json.Linq /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . /// /// The is read-only. - public new bool Remove(JToken item) + public bool Remove(JToken item) { - if (!((ICollection)this).Contains(item)) - return false; - - item.Remove(); - return true; + return RemoveItem(item); } #endregion diff --git a/Src/Newtonsoft.Json/Linq/JConstructor.cs b/Src/Newtonsoft.Json/Linq/JConstructor.cs index 3ffea382..d3545eb3 100644 --- a/Src/Newtonsoft.Json/Linq/JConstructor.cs +++ b/Src/Newtonsoft.Json/Linq/JConstructor.cs @@ -58,6 +58,13 @@ namespace Newtonsoft.Json.Linq get { return JTokenType.Constructor; } } + /// + /// Initializes a new instance of the class. + /// + public JConstructor() + { + } + /// /// Initializes a new instance of the class from another object. /// @@ -106,7 +113,7 @@ namespace Newtonsoft.Json.Linq return (c != null && _name == c.Name && ContentsEqual(c)); } - internal override JToken CloneNode() + internal override JToken CloneToken() { return new JConstructor(this); } @@ -152,7 +159,7 @@ namespace Newtonsoft.Json.Linq if (!(key is int)) throw new ArgumentException("Accessed JConstructor values with invalid key value: {0}. Argument position index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); - return GetIndex(this, (int)key); + return GetItem((int)key); } } diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 8a1106f0..fd232e8a 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -26,19 +26,43 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using Newtonsoft.Json.Utilities; using System.Collections; using System.Diagnostics; using System.Globalization; +using System.ComponentModel; +#if !SILVERLIGHT +using Newtonsoft.Json.Linq.ComponentModel; +#endif namespace Newtonsoft.Json.Linq { /// /// Represents a token that can contain other tokens. /// - public abstract class JContainer : JToken + public abstract class JContainer : JToken, IList +#if !SILVERLIGHT + , ITypedList, IBindingList +#else + , IList +#endif { +#if !SILVERLIGHT + /// + /// Occurs when the list changes or an item in the list changes. + /// + public event ListChangedEventHandler ListChanged; + + /// + /// Occurs before an item is added to the collection. + /// + public event AddingNewEventHandler AddingNew; +#endif + private JToken _content; + private object _syncRoot; + private bool _busy; internal JToken Content { @@ -60,12 +84,45 @@ namespace Newtonsoft.Json.Linq do { content = content._next; - Add(content.CloneNode()); + Add(content.CloneToken()); } while (content != other.Last); } } + internal void CheckReentrancy() + { + if (_busy) + throw new InvalidOperationException("ObservableCollection_CannotChangeObservableCollection"); + } + + #if !SILVERLIGHT + protected virtual void OnAddingNew(AddingNewEventArgs e) + { + AddingNewEventHandler handler = AddingNew; + if (handler != null) + handler(this, e); + } + + protected virtual void OnListChanged(ListChangedEventArgs e) + { + ListChangedEventHandler handler = ListChanged; + + if (handler != null) + { + _busy = true; + try + { + handler(this, e); + } + finally + { + _busy = false; + } + } + } +#endif + /// /// Gets a value indicating whether this token has childen tokens. /// @@ -190,21 +247,63 @@ namespace Newtonsoft.Json.Linq } } - internal static JToken GetIndex(JContainer c, object o) - { - return c.Children().ElementAt((int)o); - } - internal bool IsMultiContent(object content) { return (content is IEnumerable && !(content is string) && !(content is JToken)); } + internal virtual void AddItem(bool isLast, JToken previous, JToken item) + { + CheckReentrancy(); + + ValidateToken(item, null); + + item = EnsureParentToken(item); + + JToken next = (previous != null) ? previous._next : item; + + item.Parent = this; + item.Next = next; + + if (previous != null) + previous.Next = item; + + if (isLast || previous == null) + _content = item; + +#if !SILVERLIGHT + if (ListChanged != null) + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, IndexOfItem(item))); +#endif + } + + internal JToken EnsureParentToken(JToken item) + { + if (item.Parent != null) + { + item = item.CloneToken(); + } + else + { + // check whether attempting to add a token to itself + JContainer parent = this; + while (parent.Parent != null) + { + parent = parent.Parent; + } + if (item == parent) + { + item = item.CloneToken(); + } + } + return item; + } + internal void AddInternal(bool isLast, JToken previous, object content) { if (IsMultiContent(content)) { - IEnumerable enumerable = (IEnumerable)content; + IEnumerable enumerable = (IEnumerable) content; JToken multiPrevious = previous; foreach (object c in enumerable) @@ -215,53 +314,253 @@ namespace Newtonsoft.Json.Linq } else { - JToken o = CreateFromContent(content); + JToken item = CreateFromContent(content); - ValidateToken(o); - - if (o.Parent != null) - { - o = o.CloneNode(); - } - else - { - JContainer parent = this; - while (parent.Parent != null) - { - parent = parent.Parent; - } - if (o == parent) - { - o = o.CloneNode(); - } - } - - JToken next = (previous != null) ? previous._next : o; - - o.Parent = this; - o.Next = next; - - if (previous != null) - previous.Next = o; - - if (isLast || previous == null) - _content = o; + AddItem(isLast, previous, item); } } - internal virtual void ValidateToken(JToken o) + internal int IndexOfItem(JToken item) + { + int index = 0; + foreach (JToken token in Children()) + { + if (token == item) + return index; + + index++; + } + + return -1; + } + + internal virtual void InsertItem(int index, JToken item) + { + if (index == 0) + { + AddFirst(item); + } + else + { + JToken token = GetItem(index); + AddInternal(false, token.Previous, item); + } + } + + internal virtual void RemoveItemAt(int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index", "index is less than 0."); + + CheckReentrancy(); + + int currentIndex = 0; + foreach (JToken token in Children()) + { + if (index == currentIndex) + { + token.Remove(); + +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index)); +#endif + + return; + } + + currentIndex++; + } + + throw new ArgumentOutOfRangeException("index", "index is equal to or greater than Count."); + } + + internal virtual bool RemoveItem(JToken item) + { + if (item == null || item.Parent != this) + return false; + + CheckReentrancy(); + + JToken content = _content; + + int itemIndex = 0; + while (content._next != item) + { + itemIndex++; + content = content._next; + } + if (content == item) + { + // token is containers last child + _content = null; + } + else + { + if (_content == item) + { + _content = content; + } + content._next = item._next; + } + item.Parent = null; + item.Next = null; + +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, itemIndex)); +#endif + + return true; + } + + internal virtual JToken GetItem(int index) + { + return Children().ElementAt(index); + } + + internal virtual void SetItem(int index, JToken item) + { + CheckReentrancy(); + + JToken token = GetItem(index); + token.Replace(item); + +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index)); +#endif + } + + internal virtual void ClearItems() + { + CheckReentrancy(); + + while (_content != null) + { + JToken o = _content; + + JToken next = o._next; + if (o != _content || next != o._next) + throw new InvalidOperationException("This operation was corrupted by external code."); + + if (next != o) + o._next = next._next; + else + _content = null; + + next.Parent = null; + next._next = null; + } + +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); +#endif + } + + internal virtual void ReplaceItem(JToken existing, JToken replacement) + { + if (existing == null || existing.Parent != this) + return; + + if (IsTokenUnchanged(existing, replacement)) + return; + + CheckReentrancy(); + + replacement = EnsureParentToken(replacement); + + ValidateToken(replacement, existing); + + JToken content = _content; + + int itemIndex = 0; + while (content._next != existing) + { + itemIndex++; + content = content._next; + } + + if (content == existing) + { + // token is containers last child + _content = replacement; + replacement._next = replacement; + } + else + { + if (_content == existing) + { + _content = replacement; + } + content._next = replacement; + replacement._next = existing._next; + } + + replacement.Parent = this; + + existing.Parent = null; + existing.Next = null; + +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, itemIndex)); +#endif + } + + internal virtual bool ContainsItem(JToken item) + { + return (IndexOfItem(item) != -1); + } + + internal virtual void CopyItemsTo(Array 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 (CountItems() > 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()) + { + array.SetValue(token, arrayIndex + index); + index++; + } + } + + internal virtual int CountItems() + { + return Children().Count(); + } + + internal static bool IsTokenUnchanged(JToken currentValue, JToken newValue) + { + JValue v1 = currentValue as JValue; + if (v1 != null) + { + // null will get turned into a JValue of type null + if (v1.Type == JTokenType.Null && newValue == null) + return true; + + return v1.Equals(newValue); + } + + return false; + } + + internal virtual void ValidateToken(JToken o, JToken existing) { ValidationUtils.ArgumentNotNull(o, "o"); if (o.Type == JTokenType.Property) - throw new Exception("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); + throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); } /// /// Adds the specified content as children of this . /// /// The content to be added. - public virtual void Add(object content) + public void Add(object content) { AddInternal(true, Last, content); } @@ -279,8 +578,8 @@ namespace Newtonsoft.Json.Linq { if (content is JToken) return (JToken)content; - else - return new JValue(content); + + return new JValue(content); } /// @@ -298,7 +597,7 @@ namespace Newtonsoft.Json.Linq /// The content. public void ReplaceAll(object content) { - RemoveAll(); + ClearItems(); Add(content); } @@ -307,50 +606,7 @@ namespace Newtonsoft.Json.Linq /// public void RemoveAll() { - while (_content != null) - { - JToken o = _content; - - JToken next = o._next; - if (o != _content || next != o._next) - throw new InvalidOperationException("This operation was corrupted by external code."); - - if (next != o) - o._next = next._next; - else - _content = null; - - next.Parent = null; - next._next = null; - } - } - - internal void Remove(JToken o) - { - if (o.Parent != this) - throw new InvalidOperationException("This operation was corrupted by external code."); - - JToken content = _content; - - while (content._next != o) - { - content = content._next; - } - if (content == o) - { - // token is containers last child - _content = null; - } - else - { - if (_content == o) - { - _content = content; - } - content._next = o._next; - } - o.Parent = null; - o.Next = null; + ClearItems(); } internal abstract void ValidateObject(JToken o, JToken previous); @@ -467,5 +723,278 @@ namespace Newtonsoft.Json.Linq } return hashCode; } + +#if !SILVERLIGHT + string ITypedList.GetListName(PropertyDescriptor[] listAccessors) + { + return string.Empty; + } + + PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) + { + JObject o = First as JObject; + if (o != null) + { + // explicitly use constructor because compact framework has no provider + JTypeDescriptor descriptor = new JTypeDescriptor(o); + return descriptor.GetProperties(); + } + + return null; + } +#endif + + #region IList Members + + int IList.IndexOf(JToken item) + { + return IndexOfItem(item); + } + + void IList.Insert(int index, JToken item) + { + InsertItem(index, item); + } + + void IList.RemoveAt(int index) + { + RemoveItemAt(index); + } + + JToken IList.this[int index] + { + get { return GetItem(index); } + set { SetItem(index, value); } + } + + #endregion + + #region ICollection Members + + void ICollection.Add(JToken item) + { + Add(item); + } + + void ICollection.Clear() + { + ClearItems(); + } + + bool ICollection.Contains(JToken item) + { + return ContainsItem(item); + } + + void ICollection.CopyTo(JToken[] array, int arrayIndex) + { + CopyItemsTo(array, arrayIndex); + } + + int ICollection.Count + { + get { return CountItems(); } + } + + bool ICollection.IsReadOnly + { + get { return false; } + } + + bool ICollection.Remove(JToken item) + { + return RemoveItem(item); + } + + #endregion + + private JToken EnsureValue(object value) + { + if (value == null) + return null; + + if (value is JToken) + return (JToken) value; + + throw new ArgumentException("Argument is not a JToken."); + } + + #region IList Members + + int IList.Add(object value) + { + Add(EnsureValue(value)); + return CountItems() - 1; + } + + void IList.Clear() + { + ClearItems(); + } + + bool IList.Contains(object value) + { + return ContainsItem(EnsureValue(value)); + } + + int IList.IndexOf(object value) + { + return IndexOfItem(EnsureValue(value)); + } + + void IList.Insert(int index, object value) + { + InsertItem(index, EnsureValue(value)); + } + + bool IList.IsFixedSize + { + get { return false; } + } + + bool IList.IsReadOnly + { + get { return false; } + } + + void IList.Remove(object value) + { + RemoveItem(EnsureValue(value)); + } + + void IList.RemoveAt(int index) + { + RemoveItemAt(index); + } + + object IList.this[int index] + { + get { return GetItem(index); } + set { SetItem(index, EnsureValue(value)); } + } + + #endregion + + #region ICollection Members + + void ICollection.CopyTo(Array array, int index) + { + CopyItemsTo(array, index); + } + + int ICollection.Count + { + get { return CountItems(); } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + Interlocked.CompareExchange(ref _syncRoot, new object(), null); + + return _syncRoot; + } + + } + + #endregion + + #region IBindingList Members + +#if !SILVERLIGHT + void IBindingList.AddIndex(PropertyDescriptor property) + { + } + + object IBindingList.AddNew() + { + AddingNewEventArgs args = new AddingNewEventArgs(); + OnAddingNew(args); + + if (args.NewObject == null) + throw new Exception("Could not determine new value to add to '{0}'.".FormatWith(CultureInfo.InvariantCulture, GetType())); + + if (!(args.NewObject is JToken)) + throw new Exception("New item to be added to collection must be compatible with {0}.".FormatWith(CultureInfo.InvariantCulture, typeof (JToken))); + + JToken newItem = (JToken)args.NewObject; + Add(newItem); + + return newItem; + } + + bool IBindingList.AllowEdit + { + get { return true; } + } + + bool IBindingList.AllowNew + { + get { return true; } + } + + bool IBindingList.AllowRemove + { + get { return true; } + } + + void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction) + { + throw new NotSupportedException(); + } + + int IBindingList.Find(PropertyDescriptor property, object key) + { + throw new NotSupportedException(); + } + + bool IBindingList.IsSorted + { + get { return false; } + } + + void IBindingList.RemoveIndex(PropertyDescriptor property) + { + } + + void IBindingList.RemoveSort() + { + throw new NotSupportedException(); + } + + ListSortDirection IBindingList.SortDirection + { + get { return ListSortDirection.Ascending; } + } + + PropertyDescriptor IBindingList.SortProperty + { + get { return null; } + } + + bool IBindingList.SupportsChangeNotification + { + get { return true; } + } + + bool IBindingList.SupportsSearching + { + get { return false; } + } + + bool IBindingList.SupportsSorting + { + get { return false; } + } +#endif + + #endregion } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 2aebaefe..631e3f13 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -98,20 +98,35 @@ namespace Newtonsoft.Json.Linq return (t != null && ContentsEqual(t)); } - internal override void ValidateToken(JToken o) + internal override void ValidateToken(JToken o, JToken existing) { ValidationUtils.ArgumentNotNull(o, "o"); if (o.Type != JTokenType.Property) - throw new Exception("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); + throw new ArgumentException("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); + bool matchingProperty = Properties().Any(p => string.Equals(p.Name, property.Name, StringComparison.Ordinal) && p != existing); 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())); + throw new ArgumentException("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() + internal void InternalPropertyChanged(JProperty childProperty) + { + OnPropertyChanged(childProperty.Name); +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, IndexOfItem(childProperty))); +#endif + } + + internal void InternalPropertyChanging(JProperty childProperty) + { +#if !PocketPC && !SILVERLIGHT + OnPropertyChanging(childProperty.Name); +#endif + } + + internal override JToken CloneToken() { return new JObject(this); } @@ -192,14 +207,7 @@ namespace Newtonsoft.Json.Linq JProperty property = Property(propertyName); if (property != null) { - if (!IsTokenUnchanged(property.Value, value)) - { -#if !PocketPC && !SILVERLIGHT - OnPropertyChanging(property.Name); -#endif - property.Value = value; - OnPropertyChanged(property.Name); - } + property.Value = value; } else { @@ -212,21 +220,6 @@ namespace Newtonsoft.Json.Linq } } - private static bool IsTokenUnchanged(JToken currentValue, JToken newValue) - { - JValue v1 = currentValue as JValue; - if (v1 != null) - { - // null will get turned into a JValue of type null - if (v1.Type == JTokenType.Null && newValue == null) - return true; - - return v1.Equals(newValue); - } - - return false; - } - /// /// Loads an from a . /// diff --git a/Src/Newtonsoft.Json/Linq/JProperty.cs b/Src/Newtonsoft.Json/Linq/JProperty.cs index bedf0318..b434c872 100644 --- a/Src/Newtonsoft.Json/Linq/JProperty.cs +++ b/Src/Newtonsoft.Json/Linq/JProperty.cs @@ -25,6 +25,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using Newtonsoft.Json.Utilities; @@ -57,8 +58,40 @@ namespace Newtonsoft.Json.Linq public JToken Value { [DebuggerStepThrough] - get { return Last; } - set { ReplaceAll(value); } + get { return Content; } + set + { + CheckReentrancy(); + + JToken newValue = value ?? new JValue((object) null); + + if (Content == null) + { + newValue = EnsureParentToken(newValue); + + Content = newValue; + Content.Parent = this; + Content.Next = Content; + } + else + { + Content.Replace(newValue); + } + } + } + + internal override void ReplaceItem(JToken existing, JToken replacement) + { + if (IsTokenUnchanged(existing, replacement)) + return; + + if (Parent != null) + ((JObject)Parent).InternalPropertyChanging(this); + + base.ReplaceItem(existing, replacement); + + if (Parent != null) + ((JObject)Parent).InternalPropertyChanged(this); } /// @@ -71,13 +104,72 @@ namespace Newtonsoft.Json.Linq _name = other.Name; } + internal override void AddItem(bool isLast, JToken previous, JToken item) + { + if (Value != null) + throw new Exception("{0} cannot have multiple values.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + + Value = item; + } + + internal override JToken GetItem(int index) + { + if (index != 0) + throw new ArgumentOutOfRangeException(); + + return Value; + } + + internal override void SetItem(int index, JToken item) + { + if (index != 0) + throw new ArgumentOutOfRangeException(); + + Value = item; + } + + internal override bool RemoveItem(JToken item) + { + throw new Exception("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + } + + internal override void RemoveItemAt(int index) + { + throw new Exception("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + } + + internal override void InsertItem(int index, JToken item) + { + throw new Exception("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + } + + internal override bool ContainsItem(JToken item) + { + return (Value == item); + } + + internal override void ClearItems() + { + throw new Exception("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + } + + public override JEnumerable Children() + { + return new JEnumerable(ChildrenInternal()); + } + + private IEnumerable ChildrenInternal() + { + yield return Value; + } + internal override bool DeepEquals(JToken node) { JProperty t = node as JProperty; return (t != null && _name == t.Name && ContentsEqual(t)); } - internal override JToken CloneNode() + internal override JToken CloneToken() { return new JProperty(this); } @@ -92,12 +184,9 @@ namespace Newtonsoft.Json.Linq get { return JTokenType.Property; } } - /// - /// Initializes a new instance of the class. - /// - /// The property name. - public JProperty(string name) + internal JProperty(string name) { + // called from JTokenWriter ValidationUtils.ArgumentNotNull(name, "name"); _name = name; @@ -148,21 +237,6 @@ namespace Newtonsoft.Json.Linq Value.WriteTo(writer, converters); } - //public static explicit operator JValue(JProperty property) - //{ - // if (property == null) - // return null; - - // JToken value = property.Value; - // if (value == null) - // return null; - - // if (!(value is JValue)) - // throw new Exception("Could not cast {0} to JValue".FormatWith(CultureInfo.InvariantCulture, value.GetType())); - - // return (JValue)value; - //} - internal override int GetDeepHashCode() { return _name.GetHashCode() ^ ((Value != null) ? Value.GetDeepHashCode() : 0); diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 12aa482e..aef1391e 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -31,6 +31,7 @@ using Newtonsoft.Json.Utilities; using System.Diagnostics; using System.Globalization; using System.Collections; +using System.ComponentModel; namespace Newtonsoft.Json.Linq { @@ -93,7 +94,7 @@ namespace Newtonsoft.Json.Linq } } - internal abstract JToken CloneNode(); + internal abstract JToken CloneToken(); internal abstract bool DeepEquals(JToken node); /// @@ -303,7 +304,7 @@ namespace Newtonsoft.Json.Linq if (_parent == null) throw new InvalidOperationException("The parent is missing."); - _parent.Remove(this); + _parent.RemoveItem(this); } /// @@ -315,20 +316,7 @@ namespace Newtonsoft.Json.Linq if (_parent == null) throw new InvalidOperationException("The parent is missing."); - JContainer parent = _parent; - - JToken previous = this; - while (previous._next != this) - { - previous = previous._next; - } - if (previous == this) - previous = null; - - bool isLast = (this == _parent.Last); - - Remove(); - parent.AddInternal(isLast, previous, value); + _parent.ReplaceItem(this, value); } /// diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 97c88667..f9689255 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -163,7 +163,7 @@ namespace Newtonsoft.Json.Linq } } - internal override JToken CloneNode() + internal override JToken CloneToken() { return new JValue(this); }