diff --git a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs index 21b615bd..23c360b1 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs @@ -168,6 +168,282 @@ namespace Newtonsoft.Json.Tests.Linq Assert.IsTrue(d.Bytes == new JValue(Encoding.UTF8.GetBytes("A string lol!"))); } + [Test] + public void JValueAddition() + { + JObject o = new JObject( + new JProperty("Null", new JValue(null, JTokenType.Null)), + new JProperty("Integer", new JValue(1)), + new JProperty("Float", new JValue(1.1d)), + new JProperty("Decimal", new JValue(1.1m)), + new JProperty("DateTime", new JValue(new DateTime(2000, 12, 29, 23, 51, 10, DateTimeKind.Utc))), + new JProperty("Boolean", new JValue(true)), + new JProperty("String", new JValue("A string lol!")), + new JProperty("Bytes", new JValue(Encoding.UTF8.GetBytes("A string lol!"))) + ); + + dynamic d = o; + dynamic r; + + #region Add + r = d.String + " LAMO!"; + Assert.AreEqual("A string lol! LAMO!", (string)r); + r += " gg"; + Assert.AreEqual("A string lol! LAMO! gg", (string)r); + + r = d.String + null; + Assert.AreEqual("A string lol!", (string)r); + r += null; + Assert.AreEqual("A string lol!", (string)r); + + r = d.Integer + 1; + Assert.AreEqual(2, (int)r); + r += 2; + Assert.AreEqual(4, (int)r); + + r = d.Integer + 1.1; + Assert.AreEqual(2.1, (double)r); + r += 2; + Assert.AreEqual(4.1, (double)r); + + r = d.Integer + 1.1d; + Assert.AreEqual(2.1, (decimal)r); + r += 2; + Assert.AreEqual(4.1, (decimal)r); + + r = d.Integer + null; + Assert.AreEqual(null, r.Value); + r += 2; + Assert.AreEqual(null, r.Value); + + r = d.Float + 1; + Assert.AreEqual(2.1, (double)r); + r += 2; + Assert.AreEqual(4.1, (double)r); + + r = d.Float + 1.1; + Assert.AreEqual(2.2, (double)r); + r += 2; + Assert.AreEqual(4.2, (double)r); + + r = d.Float + 1.1d; + Assert.AreEqual(2.2, (decimal)r); + r += 2; + Assert.AreEqual(4.2, (decimal)r); + + r = d.Float + null; + Assert.AreEqual(null, r.Value); + r += 2; + Assert.AreEqual(null, r.Value); + + r = d.Decimal + 1; + Assert.AreEqual(2.1, (decimal)r); + r += 2; + Assert.AreEqual(4.1, (decimal)r); + + r = d.Decimal + 1.1; + Assert.AreEqual(2.2, (decimal)r); + r += 2; + Assert.AreEqual(4.2, (decimal)r); + + r = d.Decimal + 1.1d; + Assert.AreEqual(2.2, (decimal)r); + r += 2; + Assert.AreEqual(4.2, (decimal)r); + + r = d.Decimal + null; + Assert.AreEqual(null, r.Value); + r += 2; + Assert.AreEqual(null, r.Value); + #endregion + + #region Subtract + r = d.Integer - 1; + Assert.AreEqual(0, (int)r); + r -= 2; + Assert.AreEqual(-2, (int)r); + + r = d.Integer - 1.1; + Assert.AreEqual(-0.1, (double)r, 0.00001); + r -= 2; + Assert.AreEqual(-2.1, (double)r); + + r = d.Integer - 1.1d; + Assert.AreEqual(-0.1, (decimal)r); + r -= 2; + Assert.AreEqual(-2.1, (decimal)r); + + r = d.Integer - null; + Assert.AreEqual(null, r.Value); + r -= 2; + Assert.AreEqual(null, r.Value); + + r = d.Float - 1; + Assert.AreEqual(0.1, (double)r, 0.00001); + r -= 2; + Assert.AreEqual(-1.9, (double)r); + + r = d.Float - 1.1; + Assert.AreEqual(0, (double)r); + r -= 2; + Assert.AreEqual(-2, (double)r); + + r = d.Float - 1.1d; + Assert.AreEqual(0, (decimal)r); + r -= 2; + Assert.AreEqual(-2, (decimal)r); + + r = d.Float - null; + Assert.AreEqual(null, r.Value); + r -= 2; + Assert.AreEqual(null, r.Value); + + r = d.Decimal - 1; + Assert.AreEqual(0.1, (decimal)r); + r -= 2; + Assert.AreEqual(-1.9, (decimal)r); + + r = d.Decimal - 1.1; + Assert.AreEqual(0, (decimal)r); + r -= 2; + Assert.AreEqual(-2, (decimal)r); + + r = d.Decimal - 1.1d; + Assert.AreEqual(0, (decimal)r); + r -= 2; + Assert.AreEqual(-2, (decimal)r); + + r = d.Decimal - null; + Assert.AreEqual(null, r.Value); + r -= 2; + Assert.AreEqual(null, r.Value); + #endregion + + #region Multiply + r = d.Integer * 1; + Assert.AreEqual(1, (int)r); + r *= 2; + Assert.AreEqual(2, (int)r); + + r = d.Integer * 1.1; + Assert.AreEqual(1.1, (double)r); + r *= 2; + Assert.AreEqual(2.2, (double)r); + + r = d.Integer * 1.1d; + Assert.AreEqual(1.1, (decimal)r); + r *= 2; + Assert.AreEqual(2.2, (decimal)r); + + r = d.Integer * null; + Assert.AreEqual(null, r.Value); + r *= 2; + Assert.AreEqual(null, r.Value); + + r = d.Float * 1; + Assert.AreEqual(1.1, (double)r); + r *= 2; + Assert.AreEqual(2.2, (double)r); + + r = d.Float * 1.1; + Assert.AreEqual(1.21, (double)r, 0.00001); + r *= 2; + Assert.AreEqual(2.42, (double)r, 0.00001); + + r = d.Float * 1.1d; + Assert.AreEqual(1.21, (decimal)r); + r *= 2; + Assert.AreEqual(2.42, (decimal)r); + + r = d.Float * null; + Assert.AreEqual(null, r.Value); + r *= 2; + Assert.AreEqual(null, r.Value); + + r = d.Decimal * 1; + Assert.AreEqual(1.1, (decimal)r); + r *= 2; + Assert.AreEqual(2.2, (decimal)r); + + r = d.Decimal * 1.1; + Assert.AreEqual(1.21, (decimal)r); + r *= 2; + Assert.AreEqual(2.42, (decimal)r); + + r = d.Decimal * 1.1d; + Assert.AreEqual(1.21, (decimal)r); + r *= 2; + Assert.AreEqual(2.42, (decimal)r); + + r = d.Decimal * null; + Assert.AreEqual(null, r.Value); + r *= 2; + Assert.AreEqual(null, r.Value); + #endregion + + #region Divide + r = d.Integer / 1; + Assert.AreEqual(1, (int)r); + r /= 2; + Assert.AreEqual(0, (int)r); + + r = d.Integer / 1.1; + Assert.AreEqual(0.9090909090909091, (double)r); + r /= 2; + Assert.AreEqual(0.454545454545455, (double)r, 0.00001); + + r = d.Integer / 1.1d; + Assert.AreEqual(0.909090909090909m, (decimal)r); + r /= 2; + Assert.AreEqual(0.454545454545454m, (decimal)r); + + r = d.Integer / null; + Assert.AreEqual(null, r.Value); + r /= 2; + Assert.AreEqual(null, r.Value); + + r = d.Float / 1; + Assert.AreEqual(1.1, (double)r); + r /= 2; + Assert.AreEqual(0.55, (double)r); + + r = d.Float / 1.1; + Assert.AreEqual(1, (double)r, 0.00001); + r /= 2; + Assert.AreEqual(0.5, (double)r, 0.00001); + + r = d.Float / 1.1d; + Assert.AreEqual(1m, (decimal)r); + r /= 2; + Assert.AreEqual(0.5m, (decimal)r); + + r = d.Float / null; + Assert.AreEqual(null, r.Value); + r /= 2; + Assert.AreEqual(null, r.Value); + + r = d.Decimal / 1; + Assert.AreEqual(1.1d, (decimal)r); + r /= 2; + Assert.AreEqual(0.55d, (decimal)r); + + r = d.Decimal / 1.1; + Assert.AreEqual(1d, (decimal)r); + r /= 2; + Assert.AreEqual(0.5d, (decimal)r); + + r = d.Decimal / 1.1d; + Assert.AreEqual(1d, (decimal)r); + r /= 2; + Assert.AreEqual(0.5d, (decimal)r); + + r = d.Decimal / null; + Assert.AreEqual(null, r.Value); + r /= 2; + Assert.AreEqual(null, r.Value); + #endregion + } + [Test] public void JValueToString() { diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 6bd01a96..927e36b4 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -227,6 +227,118 @@ namespace Newtonsoft.Json.Linq return d1.CompareTo(d2); } +#if !(NET35 || NET20 || WINDOWS_PHONE) + private static bool Operation(ExpressionType operation, object objA, object objB, out object result) + { + if (objA is string || objB is string) + { + if (operation == ExpressionType.Add || operation == ExpressionType.AddAssign) + { + result = ((objA != null) ? objA.ToString() : null) + ((objB != null) ? objB.ToString() : null); + return true; + } + } + + if (objA is ulong || objB is ulong || objA is decimal || objB is decimal) + { + if (objA == null || objB == null) + { + result = null; + return true; + } + + decimal d1 = Convert.ToDecimal(objA, CultureInfo.InvariantCulture); + decimal d2 = Convert.ToDecimal(objB, CultureInfo.InvariantCulture); + + switch (operation) + { + case ExpressionType.Add: + case ExpressionType.AddAssign: + result = d1 + d2; + return true; + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + result = d1 - d2; + return true; + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + result = d1 * d2; + return true; + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + result = d1 / d2; + return true; + } + } + else if (objA is float || objB is float || objA is double || objB is double) + { + if (objA == null || objB == null) + { + result = null; + return true; + } + + double d1 = Convert.ToDouble(objA, CultureInfo.InvariantCulture); + double d2 = Convert.ToDouble(objB, CultureInfo.InvariantCulture); + + switch (operation) + { + case ExpressionType.Add: + case ExpressionType.AddAssign: + result = d1 + d2; + return true; + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + result = d1 - d2; + return true; + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + result = d1 * d2; + return true; + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + result = d1 / d2; + return true; + } + } + else if (objA is int || objA is uint || objA is long || objA is short || objA is ushort || objA is sbyte || objA is byte || + objB is int || objB is uint || objB is long || objB is short || objB is ushort || objB is sbyte || objB is byte) + { + if (objA == null || objB == null) + { + result = null; + return true; + } + + long l1 = Convert.ToInt64(objA, CultureInfo.InvariantCulture); + long l2 = Convert.ToInt64(objB, CultureInfo.InvariantCulture); + + switch (operation) + { + case ExpressionType.Add: + case ExpressionType.AddAssign: + result = l1 + l2; + return true; + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + result = l1 - l2; + return true; + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + result = l1 * l2; + return true; + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + result = l1 / l2; + return true; + } + } + + result = null; + return false; + } +#endif + internal override JToken CloneToken() { return new JValue(this); @@ -565,6 +677,20 @@ namespace Newtonsoft.Json.Linq case ExpressionType.LessThanOrEqual: result = (Compare(instance.Type, instance.Value, compareValue) <= 0); return true; + case ExpressionType.Add: + case ExpressionType.AddAssign: + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + if (Operation(binder.Operation, instance.Value, compareValue, out result)) + { + result = new JValue(result); + return true; + } + break; } result = null;