From b3cc3e88acbf7fae91ed2084a95b4cfebe35907e Mon Sep 17 00:00:00 2001 From: JamesNK Date: Sat, 28 May 2011 00:46:38 +0000 Subject: [PATCH] -Rewrote LINQ to JSON internals to use lists and dictionaries rather than a linked list -Moved JPropertyDescriptor to Newtonsoft.Json.Linq namespace --- .../JPropertyDescriptorTests.cs | 1 - Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs | 8 +- Src/Newtonsoft.Json.Tests/PerformanceTests.cs | 25 ++ .../Properties/AssemblyInfo.cs | 2 +- Src/Newtonsoft.Json/Linq/JArray.cs | 7 + Src/Newtonsoft.Json/Linq/JConstructor.cs | 6 + Src/Newtonsoft.Json/Linq/JContainer.cs | 378 +++++++----------- Src/Newtonsoft.Json/Linq/JObject.cs | 85 +++- Src/Newtonsoft.Json/Linq/JProperty.cs | 75 ++-- .../JPropertyDescriptor.cs | 2 +- Src/Newtonsoft.Json/Linq/JToken.cs | 37 +- .../Newtonsoft.Json.Net20.csproj | 2 +- .../Newtonsoft.Json.Net35.csproj | 2 +- .../Newtonsoft.Json.Silverlight.csproj | 2 +- .../Newtonsoft.Json.WindowsPhone.csproj | 2 +- Src/Newtonsoft.Json/Newtonsoft.Json.csproj | 3 +- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonTypeReflector.cs | 1 - .../Utilities/CollectionUtils.cs | 34 ++ 19 files changed, 337 insertions(+), 337 deletions(-) rename Src/Newtonsoft.Json/Linq/{ComponentModel => }/JPropertyDescriptor.cs (96%) diff --git a/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs index 32d88ebd..e22adca8 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs @@ -29,7 +29,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; using NUnit.Framework; -using Newtonsoft.Json.Linq.ComponentModel; using Newtonsoft.Json.Linq; namespace Newtonsoft.Json.Tests.Linq.ComponentModel diff --git a/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs index efc2b52f..ebf9c958 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs @@ -165,7 +165,7 @@ Parameter name: arrayIndex")] } [Test] - [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"index is equal to or greater than Count. + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"Index is equal to or greater than Count. Parameter name: index")] public void RemoveAtOutOfRangeIndexShouldError() { @@ -174,7 +174,7 @@ Parameter name: index")] } [Test] - [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"index is less than 0. + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"Index is less than 0. Parameter name: index")] public void RemoveAtNegativeIndexShouldError() { @@ -253,7 +253,7 @@ Parameter name: index")] } [Test] - [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"Specified argument was out of the range of valid values. + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index")] public void InsertNegativeIndexShouldThrow() { @@ -262,7 +262,7 @@ Parameter name: index")] } [Test] - [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"Specified argument was out of the range of valid values. + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"Index must be within the bounds of the List. Parameter name: index")] public void InsertOutOfRangeIndexShouldThrow() { diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index 7667682a..54a7ba96 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -121,6 +121,31 @@ namespace Newtonsoft.Json.Tests return result; } + [Test] + public void BuildJObject() + { + JObject o = new JObject(); + for (int i = 0; i < 200; i++) + { + o[i.ToString()] = i; + } + string jsonText = o.ToString(); + + // this is extremely slow with 5000 interations + int interations = 100; + + TimeOperation(() => + { + JObject oo = null; + for (int i = 0; i < interations; i++) + { + oo = JObject.Parse(jsonText); + } + + return oo; + }, "JObject"); + } + private void SerializeSize(object value) { // this is extremely slow with 5000 interations diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 1c4dd3e5..d26198c2 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -41,5 +41,5 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.2.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.2.13722")] +[assembly: AssemblyFileVersion("4.0.2.13728")] #endif diff --git a/Src/Newtonsoft.Json/Linq/JArray.cs b/Src/Newtonsoft.Json/Linq/JArray.cs index c8d478ef..283de895 100644 --- a/Src/Newtonsoft.Json/Linq/JArray.cs +++ b/Src/Newtonsoft.Json/Linq/JArray.cs @@ -39,6 +39,13 @@ namespace Newtonsoft.Json.Linq /// public class JArray : JContainer, IList { + private IList _values = new List(); + + protected override IList ChildrenTokens + { + get { return _values; } + } + /// /// Gets the node type for this . /// diff --git a/Src/Newtonsoft.Json/Linq/JConstructor.cs b/Src/Newtonsoft.Json/Linq/JConstructor.cs index a8755463..9b0175f7 100644 --- a/Src/Newtonsoft.Json/Linq/JConstructor.cs +++ b/Src/Newtonsoft.Json/Linq/JConstructor.cs @@ -38,6 +38,12 @@ namespace Newtonsoft.Json.Linq public class JConstructor : JContainer { private string _name; + private IList _values = new List(); + + protected override IList ChildrenTokens + { + get { return _values; } + } /// /// Gets or sets the name of this constructor. diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 8dd4ca96..cdf7a7e5 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -33,9 +33,6 @@ using System.Diagnostics; using System.Globalization; using System.ComponentModel; using System.Collections.Specialized; -#if !SILVERLIGHT -using Newtonsoft.Json.Linq.ComponentModel; -#endif namespace Newtonsoft.Json.Linq { @@ -70,16 +67,11 @@ namespace Newtonsoft.Json.Linq public event NotifyCollectionChangedEventHandler CollectionChanged; #endif - private JToken _content; + protected abstract IList ChildrenTokens { get; } + private object _syncRoot; private bool _busy; - internal JToken Content - { - get { return _content; } - set { _content = value; } - } - internal JContainer() { } @@ -88,15 +80,9 @@ namespace Newtonsoft.Json.Linq { ValidationUtils.ArgumentNotNull(other, "c"); - JToken content = other.Last; - if (content != null) + foreach (JToken child in other) { - do - { - content = content._next; - Add(content.CloneToken()); - } - while (content != other.Last); + Add(child); } } @@ -172,7 +158,7 @@ namespace Newtonsoft.Json.Linq /// public override bool HasValues { - get { return (_content != null); } + get { return ChildrenTokens.Count > 0; } } internal bool ContentsEqual(JContainer container) @@ -209,13 +195,7 @@ namespace Newtonsoft.Json.Linq /// public override JToken First { - get - { - if (Last == null) - return null; - - return Last._next; - } + get { return ChildrenTokens.FirstOrDefault(); } } /// @@ -226,8 +206,7 @@ namespace Newtonsoft.Json.Linq /// public override JToken Last { - [DebuggerStepThrough] - get { return _content; } + get { return ChildrenTokens.LastOrDefault(); } } /// @@ -238,21 +217,7 @@ namespace Newtonsoft.Json.Linq /// public override JEnumerable Children() { - return new JEnumerable(ChildrenInternal()); - } - - internal IEnumerable ChildrenInternal() - { - JToken first = First; - JToken current = first; - if (current == null) - yield break; - - do - { - yield return current; - } - while ((current = current.Next) != null); + return new JEnumerable(ChildrenTokens); } /// @@ -264,7 +229,7 @@ namespace Newtonsoft.Json.Linq /// public override IEnumerable Values() { - return Children().Convert(); + return ChildrenTokens.Convert(); } /// @@ -273,7 +238,7 @@ namespace Newtonsoft.Json.Linq /// An containing the descendant tokens of the . public IEnumerable Descendants() { - foreach (JToken o in Children()) + foreach (JToken o in ChildrenTokens) { yield return o; JContainer c = o as JContainer; @@ -292,37 +257,11 @@ namespace Newtonsoft.Json.Linq return (content is IEnumerable && !(content is string) && !(content is JToken) && !(content is byte[])); } - 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 -#if SILVERLIGHT || !(NET20 || NET35) - if (CollectionChanged != null) - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, IndexOfItem(item))); -#endif - } - internal JToken EnsureParentToken(JToken item) { + if (item == null) + return new JValue((object) null); + if (item.Parent != null) { item = item.CloneToken(); @@ -343,142 +282,157 @@ namespace Newtonsoft.Json.Linq return item; } - internal void AddInternal(bool isLast, JToken previous, object content) + private class JTokenReferenceEqualityComparer : IEqualityComparer { - if (IsMultiContent(content)) - { - IEnumerable enumerable = (IEnumerable) content; + public static readonly JTokenReferenceEqualityComparer Instance = new JTokenReferenceEqualityComparer(); - JToken multiPrevious = previous; - foreach (object c in enumerable) - { - AddInternal(isLast, multiPrevious, c); - multiPrevious = (multiPrevious != null) ? multiPrevious._next : Last; - } + public bool Equals(JToken x, JToken y) + { + return ReferenceEquals(x, y); } - else - { - JToken item = CreateFromContent(content); - AddItem(isLast, previous, item); + public int GetHashCode(JToken obj) + { + if (obj == null) + return 0; + + return obj.GetHashCode(); } } internal int IndexOfItem(JToken item) { - int index = 0; - foreach (JToken token in Children()) - { - if (token == item) - return index; - - index++; - } - - return -1; + return ChildrenTokens.IndexOf(item, JTokenReferenceEqualityComparer.Instance); } internal virtual void InsertItem(int index, JToken item) { - if (index == 0) - { - AddFirst(item); - } - else - { - JToken token = GetItem(index); - AddInternal(false, token.Previous, item); - } + if (index > ChildrenTokens.Count) + throw new ArgumentOutOfRangeException("index", "Index must be within the bounds of the List."); + + CheckReentrancy(); + + item = EnsureParentToken(item); + + JToken previous = (index == 0) ? null : ChildrenTokens[index - 1]; + // haven't inserted new token yet so next token is still at the inserting index + JToken next = (index == ChildrenTokens.Count) ? null : ChildrenTokens[index]; + + ValidateToken(item, null); + + item.Parent = this; + + item.Previous = previous; + if (previous != null) + previous.Next = item; + + item.Next = next; + if (next != null) + next.Previous = item; + + ChildrenTokens.Insert(index, item); + +#if !SILVERLIGHT + if (ListChanged != null) + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, index)); +#endif +#if SILVERLIGHT || !(NET20 || NET35) + if (CollectionChanged != null) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); +#endif } internal virtual void RemoveItemAt(int index) { if (index < 0) - throw new ArgumentOutOfRangeException("index", "index is less than 0."); + throw new ArgumentOutOfRangeException("index", "Index is less than 0."); + if (index >= ChildrenTokens.Count) + throw new ArgumentOutOfRangeException("index", "Index is equal to or greater than Count."); CheckReentrancy(); - int currentIndex = 0; - foreach (JToken token in Children()) - { - if (index == currentIndex) - { - token.Remove(); + JToken item = ChildrenTokens[index]; + JToken previous = (index == 0) ? null : ChildrenTokens[index - 1]; + JToken next = (index == ChildrenTokens.Count - 1) ? null : ChildrenTokens[index + 1]; + + if (previous != null) + previous.Next = next; + if (next != null) + next.Previous = previous; + + item.Parent = null; + item.Previous = null; + item.Next = null; + + ChildrenTokens.RemoveAt(index); #if !SILVERLIGHT - OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index)); + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index)); #endif #if SILVERLIGHT || !(NET20 || NET35) - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, token, index)); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, 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) + int index = IndexOfItem(item); + if (index >= 0) { - itemIndex++; - content = content._next; + RemoveItemAt(index); + return true; } - 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 -#if SILVERLIGHT || !(NET20 || NET35) - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, itemIndex)); -#endif - - return true; + return false; } internal virtual JToken GetItem(int index) { - return Children().ElementAt(index); + return ChildrenTokens[index]; } internal virtual void SetItem(int index, JToken item) { + if (index < 0) + throw new ArgumentOutOfRangeException("index", "Index is less than 0."); + if (index >= ChildrenTokens.Count) + throw new ArgumentOutOfRangeException("index", "Index is equal to or greater than Count."); + + JToken existing = ChildrenTokens[index]; + + if (IsTokenUnchanged(existing, item)) + return; + CheckReentrancy(); - JToken token = GetItem(index); - token.Replace(item); + item = EnsureParentToken(item); + + ValidateToken(item, existing); + + JToken previous = (index == 0) ? null : ChildrenTokens[index - 1]; + JToken next = (index == ChildrenTokens.Count - 1) ? null : ChildrenTokens[index + 1]; + + item.Parent = this; + + item.Previous = previous; + if (previous != null) + previous.Next = item; + + item.Next = next; + if (next != null) + next.Previous = item; + + ChildrenTokens[index] = item; + + existing.Parent = null; + existing.Previous = null; + existing.Next = null; #if !SILVERLIGHT OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index)); #endif #if SILVERLIGHT || !(NET20 || NET35) - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, token, index)); + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, existing, index)); #endif } @@ -486,23 +440,15 @@ namespace Newtonsoft.Json.Linq { CheckReentrancy(); - while (_content != null) + foreach (JToken item in ChildrenTokens) { - 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; + item.Parent = null; + item.Previous = null; + item.Next = null; } + ChildrenTokens.Clear(); + #if !SILVERLIGHT OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); #endif @@ -516,51 +462,8 @@ namespace Newtonsoft.Json.Linq 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 -#if SILVERLIGHT || !(NET20 || NET35) - OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, replacement, existing, itemIndex)); -#endif + int index = IndexOfItem(existing); + SetItem(index, replacement); } internal virtual bool ContainsItem(JToken item) @@ -580,7 +483,7 @@ namespace Newtonsoft.Json.Linq 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()) + foreach (JToken token in ChildrenTokens) { array.SetValue(token, arrayIndex + index); index++; @@ -589,7 +492,7 @@ namespace Newtonsoft.Json.Linq internal virtual int CountItems() { - return Children().Count(); + return ChildrenTokens.Count; } internal static bool IsTokenUnchanged(JToken currentValue, JToken newValue) @@ -619,9 +522,9 @@ namespace Newtonsoft.Json.Linq /// Adds the specified content as children of this . /// /// The content to be added. - public void Add(object content) + public virtual void Add(object content) { - AddInternal(true, Last, content); + AddInternal(ChildrenTokens.Count, content); } /// @@ -630,7 +533,28 @@ namespace Newtonsoft.Json.Linq /// The content to be added. public void AddFirst(object content) { - AddInternal(false, Last, content); + AddInternal(0, content); + } + + internal void AddInternal(int index, object content) + { + if (IsMultiContent(content)) + { + IEnumerable enumerable = (IEnumerable)content; + + int multiIndex = index; + foreach (object c in enumerable) + { + AddInternal(multiIndex, c); + multiIndex++; + } + } + else + { + JToken item = CreateFromContent(content); + + InsertItem(index, item); + } } internal JToken CreateFromContent(object content) @@ -790,7 +714,7 @@ namespace Newtonsoft.Json.Linq internal int ContentsHashCode() { int hashCode = 0; - foreach (JToken item in Children()) + foreach (JToken item in ChildrenTokens) { hashCode ^= item.GetDeepHashCode(); } diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 69946f50..c4c1adf4 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -25,6 +25,7 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; #if !(NET35 || NET20 || WINDOWS_PHONE) @@ -35,9 +36,6 @@ using System.Linq; using System.IO; using Newtonsoft.Json.Utilities; using System.Globalization; -#if !PocketPC && !SILVERLIGHT -using Newtonsoft.Json.Linq.ComponentModel; -#endif namespace Newtonsoft.Json.Linq { @@ -52,6 +50,46 @@ namespace Newtonsoft.Json.Linq , INotifyPropertyChanging #endif { + private class JPropertKeyedCollection : KeyedCollection + { + public JPropertKeyedCollection(IEqualityComparer comparer) + : base(comparer) + { + } + + protected override string GetKeyForItem(JToken item) + { + return ((JProperty) item).Name; + } + + protected override void InsertItem(int index, JToken item) + { + if (Dictionary == null) + { + base.InsertItem(index, item); + } + else + { + // need to override so that the dictionary key is always set rather than added + string keyForItem = GetKeyForItem(item); + Dictionary[keyForItem] = item; + Items.Insert(index, item); + } + } + + public new IDictionary Dictionary + { + get { return base.Dictionary; } + } + } + + private JPropertKeyedCollection _properties = new JPropertKeyedCollection(StringComparer.Ordinal); + + protected override IList ChildrenTokens + { + get { return _properties; } + } + /// /// Occurs when a property value changes. /// @@ -111,14 +149,18 @@ namespace Newtonsoft.Json.Linq if (o.Type != JTokenType.Property) throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); - // looping over all properties every time isn't good - // need to think about performance here - JProperty property = (JProperty)o; - foreach (JProperty childProperty in Children()) + JProperty newProperty = (JProperty) o; + + if (existing != null) { - if (childProperty != existing && string.Equals(childProperty.Name, property.Name, StringComparison.Ordinal)) - 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())); + JProperty existingProperty = (JProperty) existing; + + if (newProperty.Name == existingProperty.Name) + return; } + + if (_properties.Dictionary != null && _properties.Dictionary.TryGetValue(newProperty.Name, out existing)) + throw new ArgumentException("Can not add property {0} to {1}. Property with the same name already exists on object.".FormatWith(CultureInfo.InvariantCulture, newProperty.Name, GetType())); } internal void InternalPropertyChanged(JProperty childProperty) @@ -159,7 +201,7 @@ namespace Newtonsoft.Json.Linq /// An of this object's properties. public IEnumerable Properties() { - return Children().Cast(); + return ChildrenTokens.Cast(); } /// @@ -169,9 +211,14 @@ namespace Newtonsoft.Json.Linq /// A with the specified name or null. public JProperty Property(string name) { - return Properties() - .Where(p => string.Equals(p.Name, name, StringComparison.Ordinal)) - .SingleOrDefault(); + if (_properties.Dictionary == null) + return null; + if (name == null) + return null; + + JToken property; + _properties.Dictionary.TryGetValue(name, out property); + return (JProperty)property; } /// @@ -318,7 +365,7 @@ namespace Newtonsoft.Json.Linq { writer.WriteStartObject(); - foreach (JProperty property in ChildrenInternal()) + foreach (JProperty property in ChildrenTokens) { property.WriteTo(writer, converters); } @@ -339,12 +386,12 @@ namespace Newtonsoft.Json.Linq bool IDictionary.ContainsKey(string key) { - return (Property(key) != null); + return _properties.Dictionary.ContainsKey(key); } ICollection IDictionary.Keys { - get { throw new NotImplementedException(); } + get { return _properties.Dictionary.Keys; } } /// @@ -383,7 +430,7 @@ namespace Newtonsoft.Json.Linq ICollection IDictionary.Values { - get { throw new NotImplementedException(); } + get { return _properties.Dictionary.Values; } } #endregion @@ -421,7 +468,7 @@ namespace Newtonsoft.Json.Linq 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()) + foreach (JProperty property in ChildrenTokens) { array[arrayIndex + index] = new KeyValuePair(property.Name, property.Value); index++; @@ -467,7 +514,7 @@ namespace Newtonsoft.Json.Linq /// public IEnumerator> GetEnumerator() { - foreach (JProperty property in Properties()) + foreach (JProperty property in ChildrenTokens) { yield return new KeyValuePair(property.Name, property.Value); } diff --git a/Src/Newtonsoft.Json/Linq/JProperty.cs b/Src/Newtonsoft.Json/Linq/JProperty.cs index 748a3ee3..77496291 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.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; @@ -39,8 +40,14 @@ namespace Newtonsoft.Json.Linq /// public class JProperty : JContainer { + private readonly List _content = new List(); private readonly string _name; + protected override IList ChildrenTokens + { + get { return _content; } + } + /// /// Gets the property name. /// @@ -58,42 +65,24 @@ namespace Newtonsoft.Json.Linq public JToken Value { [DebuggerStepThrough] - get { return Content; } + get { return (ChildrenTokens.Count > 0) ? ChildrenTokens[0] : null; } set { CheckReentrancy(); JToken newValue = value ?? new JValue((object) null); - if (Content == null) + if (ChildrenTokens.Count == 0) { - newValue = EnsureParentToken(newValue); - - Content = newValue; - Content.Parent = this; - Content.Next = Content; + InsertItem(0, newValue); } else { - Content.Replace(newValue); + SetItem(0, 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); - } - /// /// Initializes a new instance of the class from another object. /// @@ -104,14 +93,6 @@ 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) @@ -124,8 +105,17 @@ namespace Newtonsoft.Json.Linq { if (index != 0) throw new ArgumentOutOfRangeException(); - - Value = item; + + if (IsTokenUnchanged(Value, item)) + return; + + if (Parent != null) + ((JObject)Parent).InternalPropertyChanging(this); + + base.SetItem(0, item); + + if (Parent != null) + ((JObject)Parent).InternalPropertyChanged(this); } internal override bool RemoveItem(JToken item) @@ -140,7 +130,10 @@ namespace Newtonsoft.Json.Linq internal override void InsertItem(int index, JToken item) { - throw new Exception("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + if (Value != null) + throw new Exception("{0} cannot have multiple values.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + + base.InsertItem(0, item); } internal override bool ContainsItem(JToken item) @@ -153,22 +146,6 @@ namespace Newtonsoft.Json.Linq throw new Exception("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); } - /// - /// Returns a collection of the child tokens of this token, in document order. - /// - /// - /// An of containing the child tokens of this , in document order. - /// - public override JEnumerable Children() - { - return new JEnumerable(GetValueEnumerable()); - } - - private IEnumerable GetValueEnumerable() - { - yield return Value; - } - internal override bool DeepEquals(JToken node) { JProperty t = node as JProperty; diff --git a/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs b/Src/Newtonsoft.Json/Linq/JPropertyDescriptor.cs similarity index 96% rename from Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs rename to Src/Newtonsoft.Json/Linq/JPropertyDescriptor.cs index 38db030f..dd49c08d 100644 --- a/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs +++ b/Src/Newtonsoft.Json/Linq/JPropertyDescriptor.cs @@ -28,7 +28,7 @@ using System; using System.ComponentModel; using Newtonsoft.Json.Utilities; -namespace Newtonsoft.Json.Linq.ComponentModel +namespace Newtonsoft.Json.Linq { /// /// Represents a view of a . diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 4e07dc6a..a024f208 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -51,7 +51,8 @@ namespace Newtonsoft.Json.Linq #endif { private JContainer _parent; - internal JToken _next; + private JToken _previous; + private JToken _next; private static JTokenEqualityComparer _equalityComparer; private int? _lineNumber; @@ -138,13 +139,7 @@ namespace Newtonsoft.Json.Linq /// The that contains the next sibling token. public JToken Next { - get - { - if (_parent != null && _next != _parent.First) - return _next; - - return null; - } + get { return _next; } internal set { _next = value; } } @@ -154,20 +149,8 @@ namespace Newtonsoft.Json.Linq /// The that contains the previous sibling token. public JToken Previous { - get - { - if (_parent == null) - return null; - - JToken parentNext = _parent.Content._next; - JToken parentNextBefore = null; - while (parentNext != this) - { - parentNextBefore = parentNext; - parentNext = parentNext.Next; - } - return parentNextBefore; - } + get { return _previous; } + internal set { _previous = value; } } internal JToken() @@ -183,7 +166,8 @@ namespace Newtonsoft.Json.Linq if (_parent == null) throw new InvalidOperationException("The parent is missing."); - _parent.AddInternal((Next == null), this, content); + int index = _parent.IndexOfItem(this); + _parent.AddInternal(index + 1, content); } /// @@ -195,11 +179,8 @@ namespace Newtonsoft.Json.Linq if (_parent == null) throw new InvalidOperationException("The parent is missing."); - JToken previous = Previous; - if (previous == null) - previous = _parent.Last; - - _parent.AddInternal(false, previous, content); + int index = _parent.IndexOfItem(this); + _parent.AddInternal(index, content); } /// diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index d3a8d9e0..b9963627 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -96,8 +96,8 @@ - + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj index 7e590cc3..f4b3f4be 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -104,6 +104,7 @@ + @@ -115,7 +116,6 @@ - diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index 54f5f071..96815c01 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -127,7 +127,6 @@ - @@ -137,6 +136,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj index 0ef846e1..766277d0 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj @@ -100,7 +100,6 @@ - @@ -110,6 +109,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 3f6d0269..098ce151 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -114,7 +114,7 @@ - + @@ -262,5 +262,6 @@ + \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index e37cbec1..9712e8bc 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -110,7 +110,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.2.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.2.13722")] +[assembly: AssemblyFileVersion("4.0.2.13728")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index c4e31571..1068e135 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -124,7 +124,6 @@ namespace Newtonsoft.Json.Serialization if (propertyInfo.IsVirtual()) { Type currentType = propertyInfo.DeclaringType; - Type[] types = propertyInfo.GetIndexParameters().Select(p => p.ParameterType).ToArray(); while (result == null && currentType != null) { diff --git a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs index 7e20b0ef..c34a8c72 100644 --- a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs @@ -631,5 +631,39 @@ namespace Newtonsoft.Json.Utilities return -1; } + + /// + /// Returns the index of the first occurrence in a sequence by using the default equality comparer. + /// + /// The type of the elements of source. + /// A sequence in which to locate a value. + /// The object to locate in the sequence + /// The zero-based index of the first occurrence of value within the entire sequence, if found; otherwise, –1. + public static int IndexOf(this IEnumerable list, TSource value) where TSource : IEquatable + { + return list.IndexOf(value, EqualityComparer.Default); + } + + /// + /// Returns the index of the first occurrence in a sequence by using a specified IEqualityComparer. + /// + /// The type of the elements of source. + /// A sequence in which to locate a value. + /// The object to locate in the sequence + /// An equality comparer to compare values. + /// The zero-based index of the first occurrence of value within the entire sequence, if found; otherwise, –1. + public static int IndexOf(this IEnumerable list, TSource value, IEqualityComparer comparer) + { + int index = 0; + foreach (TSource item in list) + { + if (comparer.Equals(item, value)) + { + return index; + } + index++; + } + return -1; + } } } \ No newline at end of file