From 3ad33f73299e6a8f05405d5b49a30ad1702a756a Mon Sep 17 00:00:00 2001 From: JamesNK Date: Mon, 27 Dec 2010 21:25:06 +0000 Subject: [PATCH] -Added ICloneable and DeepClone to JToken -Changed DeepEquals to take into account possible floating point errors --- Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs | 76 +++++++++++++++++++ Src/Newtonsoft.Json/Linq/JToken.cs | 25 +++++- Src/Newtonsoft.Json/Linq/JValue.cs | 8 +- Src/Newtonsoft.Json/Utilities/MathUtils.cs | 6 ++ 4 files changed, 111 insertions(+), 4 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs index b33bd86b..1114d284 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs @@ -646,5 +646,81 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual("secondlastpie", (string)a[5]); Assert.AreEqual(7, a.Count()); } + + [Test] + public void DeepClone() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3), + new JObject( + new JProperty("First", new JValue(Encoding.UTF8.GetBytes("Hi"))), + new JProperty("Second", 1), + new JProperty("Third", null), + new JProperty("Fourth", new JConstructor("Date", 12345)), + new JProperty("Fifth", double.PositiveInfinity), + new JProperty("Sixth", double.NaN) + ) + ); + + JArray a2 = (JArray)a.DeepClone(); + + Console.WriteLine(a2.ToString(Formatting.Indented)); + + Assert.IsTrue(a.DeepEquals(a2)); + } + +#if !SILVERLIGHT + [Test] + public void Clone() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3), + new JObject( + new JProperty("First", new JValue(Encoding.UTF8.GetBytes("Hi"))), + new JProperty("Second", 1), + new JProperty("Third", null), + new JProperty("Fourth", new JConstructor("Date", 12345)), + new JProperty("Fifth", double.PositiveInfinity), + new JProperty("Sixth", double.NaN) + ) + ); + + ICloneable c = a; + + JArray a2 = (JArray) c.Clone(); + + Assert.IsTrue(a.DeepEquals(a2)); + } +#endif + + [Test] + public void DoubleDeepEquals() + { + JArray a = + new JArray( + double.NaN, + double.PositiveInfinity, + double.NegativeInfinity + ); + + JArray a2 = (JArray)a.DeepClone(); + + Assert.IsTrue(a.DeepEquals(a2)); + + double d = 1 + 0.1 + 0.1 + 0.1; + + JValue v1 = new JValue(d); + JValue v2 = new JValue(1.3); + + Assert.IsTrue(v1.DeepEquals(v2)); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 3d323006..d48e969a 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -43,10 +43,13 @@ namespace Newtonsoft.Json.Linq /// Represents an abstract JSON token. /// public abstract class JToken : IJEnumerable, IJsonLineInfo -#if !(NET35 || NET20 || SILVERLIGHT) - , IDynamicMetaObjectProvider +#if !SILVERLIGHT +, ICloneable #endif - { +#if !(NET35 || NET20 || SILVERLIGHT) +, IDynamicMetaObjectProvider +#endif + { private JContainer _parent; internal JToken _next; private static JTokenEqualityComparer _equalityComparer; @@ -1298,5 +1301,21 @@ namespace Newtonsoft.Json.Linq return GetMetaObject(parameter); } #endif + +#if !SILVERLIGHT + object ICloneable.Clone() + { + return DeepClone(); + } +#endif + + /// + /// Creates a new instance of the . All child tokens are recursively cloned. + /// + /// A new instance of the . + public JToken DeepClone() + { + return CloneToken(); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 68ec2830..e8ad67d6 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -159,7 +159,13 @@ namespace Newtonsoft.Json.Linq else return Convert.ToInt64(objA, CultureInfo.InvariantCulture).Equals(Convert.ToInt64(objB, CultureInfo.InvariantCulture)); case JTokenType.Float: - return Convert.ToDouble(objA, CultureInfo.InvariantCulture).Equals(Convert.ToDouble(objB, CultureInfo.InvariantCulture)); + double d1 = Convert.ToDouble(objA, CultureInfo.InvariantCulture); + double d2 = Convert.ToDouble(objB, CultureInfo.InvariantCulture); + if (d1.Equals(d2)) + return true; + + // take into account possible floating point errors + return MathUtils.ApproxEquals(d1, d2); case JTokenType.Comment: case JTokenType.String: case JTokenType.Boolean: diff --git a/Src/Newtonsoft.Json/Utilities/MathUtils.cs b/Src/Newtonsoft.Json/Utilities/MathUtils.cs index db489eb9..e60ed056 100644 --- a/Src/Newtonsoft.Json/Utilities/MathUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/MathUtils.cs @@ -124,5 +124,11 @@ namespace Newtonsoft.Json.Utilities return Math.Max(val1.Value, val2.Value); } + + public static bool ApproxEquals(double d1, double d2) + { + // are values equal to within 12 (or so) digits of precision? + return Math.Abs(d1 - d2) < (Math.Abs(d1) * 1e-12); + } } } \ No newline at end of file