From 2e1211f689feb9d054c1a877431b366f3052adea Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Mon, 9 Apr 2012 16:20:42 +1200 Subject: [PATCH] -Fixed comparing JObjects to ignore property order --- .../Linq/JObjectTests.cs | 50 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 2 +- Src/Newtonsoft.Json/Linq/JContainer.cs | 28 ++++------- Src/Newtonsoft.Json/Linq/JObject.cs | 5 +- .../Linq/JPropertyKeyedCollection.cs | 29 +++++++++++ Src/Newtonsoft.Json/Linq/JToken.cs | 4 ++ Src/Newtonsoft.Json/Linq/JValue.cs | 2 + .../Properties/AssemblyInfo.cs | 2 +- 8 files changed, 102 insertions(+), 20 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs index f75d5bda..4892fdf8 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -1746,5 +1746,55 @@ Parameter name: arrayIndex" JObject o = JObject.Parse(json); }); } + + [Test] + public void DeepEqualsIgnoreOrder() + { + JObject o1 = new JObject( + new JProperty("null", null), + new JProperty("integer", 1), + new JProperty("string", "string!"), + new JProperty("decimal", 0.5m), + new JProperty("array", new JArray(1, 2))); + + Assert.IsTrue(o1.DeepEquals(o1)); + + JObject o2 = new JObject( + new JProperty("null", null), + new JProperty("string", "string!"), + new JProperty("decimal", 0.5m), + new JProperty("integer", 1), + new JProperty("array", new JArray(1, 2))); + + Assert.IsTrue(o1.DeepEquals(o2)); + + JObject o3 = new JObject( + new JProperty("null", null), + new JProperty("string", "string!"), + new JProperty("decimal", 0.5m), + new JProperty("integer", 2), + new JProperty("array", new JArray(1, 2))); + + Assert.IsFalse(o1.DeepEquals(o3)); + + JObject o4 = new JObject( + new JProperty("null", null), + new JProperty("string", "string!"), + new JProperty("decimal", 0.5m), + new JProperty("integer", 1), + new JProperty("array", new JArray(2, 1))); + + Assert.IsFalse(o1.DeepEquals(o4)); + + JObject o5 = new JObject( + new JProperty("null", null), + new JProperty("string", "string!"), + new JProperty("decimal", 0.5m), + new JProperty("integer", 1)); + + Assert.IsFalse(o1.DeepEquals(o5)); + + Assert.IsFalse(o1.DeepEquals(null)); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index dd024d2c..92d3cd87 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -72,5 +72,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.1.14808")] +[assembly: AssemblyFileVersion("4.5.1.14809")] #endif diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 472068f0..04ef9682 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -170,28 +170,22 @@ namespace Newtonsoft.Json.Linq internal bool ContentsEqual(JContainer container) { - JToken t1 = First; - JToken t2 = container.First; - - if (t1 == t2) + if (container == this) return true; - do - { - if (t1 == null && t2 == null) - return true; + IList t1 = ChildrenTokens; + IList t2 = container.ChildrenTokens; - if (t1 != null && t2 != null && t1.DeepEquals(t2)) - { - t1 = (t1 != Last) ? t1.Next : null; - t2 = (t2 != container.Last) ? t2.Next : null; - } - else - { + if (t1.Count != t2.Count) + return false; + + for (int i = 0; i < t1.Count; i++) + { + if (!t1[i].DeepEquals(t2[i])) return false; - } } - while (true); + + return true; } /// diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index e7107031..905ee750 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -114,7 +114,10 @@ namespace Newtonsoft.Json.Linq internal override bool DeepEquals(JToken node) { JObject t = node as JObject; - return (t != null && ContentsEqual(t)); + if (t == null) + return false; + + return _properties.Compare(t._properties); } internal override void InsertItem(int index, JToken item, bool skipParentCheck) diff --git a/Src/Newtonsoft.Json/Linq/JPropertyKeyedCollection.cs b/Src/Newtonsoft.Json/Linq/JPropertyKeyedCollection.cs index e72344fc..6a249f47 100644 --- a/Src/Newtonsoft.Json/Linq/JPropertyKeyedCollection.cs +++ b/Src/Newtonsoft.Json/Linq/JPropertyKeyedCollection.cs @@ -164,5 +164,34 @@ namespace Newtonsoft.Json.Linq return _dictionary.Values; } } + + public bool Compare(JPropertyKeyedCollection other) + { + if (this == other) + return true; + + // dictionaries in JavaScript aren't ordered + // ignore order when comparing properties + Dictionary d1 = _dictionary; + Dictionary d2 = other._dictionary; + + if (d1.Count != d2.Count) + return false; + + foreach (KeyValuePair keyAndProperty in d1) + { + JToken secondValue; + if (!d2.TryGetValue(keyAndProperty.Key, out secondValue)) + return false; + + JProperty p1 = (JProperty)keyAndProperty.Value; + JProperty p2 = (JProperty)secondValue; + + if (!p1.Value.DeepEquals(p2.Value)) + return false; + } + + return true; + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 370a0b7d..dcd5b29b 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -208,7 +208,9 @@ namespace Newtonsoft.Json.Linq yield break; for (JToken o = Next; o != null; o = o.Next) + { yield return o; + } } /// @@ -218,7 +220,9 @@ namespace Newtonsoft.Json.Linq public IEnumerable BeforeSelf() { for (JToken o = Parent.First; o != this; o = o.Next) + { yield return o; + } } /// diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 34c030d9..18fe5490 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -153,6 +153,8 @@ namespace Newtonsoft.Json.Linq JValue other = node as JValue; if (other == null) return false; + if (other == this) + return true; return ValuesEquals(this, other); } diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index b418ee13..6cafee23 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -88,7 +88,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.1.14808")] +[assembly: AssemblyFileVersion("4.5.1.14809")] #endif [assembly: CLSCompliant(true)]