From 4099870390838843ea562ed8d662c03ee8d59201 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Mon, 26 Dec 2016 14:43:07 +1300 Subject: [PATCH] -Added support for any combination of paths and values in JSONPath filter expressions --- .../Linq/JsonPath/JPathExecuteTests.cs | 88 +++++++++++ .../Linq/JsonPath/JPathParseTests.cs | 95 ++++++++---- .../Linq/JsonPath/QueryExpressionTests.cs | 16 +- Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs | 145 +++++++++++------- .../Linq/JsonPath/QueryExpression.cs | 144 ++++++++++------- 5 files changed, 347 insertions(+), 141 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs index 2ca2a7e0..303c5f6e 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs @@ -723,6 +723,21 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("hi", 3)), t[1])); } + [Test] + public void LesserQuery_ValueFirst() + { + JArray a = new JArray( + new JObject(new JProperty("hi", 1)), + new JObject(new JProperty("hi", 2)), + new JObject(new JProperty("hi", 3))); + + IList t = a.SelectTokens("[ ?( 1 < @.hi ) ]").ToList(); + Assert.IsNotNull(t); + Assert.AreEqual(2, t.Count); + Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("hi", 2)), t[0])); + Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("hi", 3)), t[1])); + } + #if !(PORTABLE || DNXCORE50 || PORTABLE40 || NET35 || NET20) || NETSTANDARD1_1 [Test] public void GreaterQueryBigInteger() @@ -811,6 +826,79 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath Assert.AreEqual(1L, v.Value); } + [Test] + public void MultiplePaths() + { + JArray a = JArray.Parse(@"[ + { + ""price"": 199, + ""max_price"": 200 + }, + { + ""price"": 200, + ""max_price"": 200 + }, + { + ""price"": 201, + ""max_price"": 200 + } +]"); + + var results = a.SelectTokens("[?(@.price > @.max_price)]").ToList(); + Assert.AreEqual(1, results.Count); + Assert.AreEqual(a[2], results[0]); + } + + [Test] + public void Exists_True() + { + JArray a = JArray.Parse(@"[ + { + ""price"": 199, + ""max_price"": 200 + }, + { + ""price"": 200, + ""max_price"": 200 + }, + { + ""price"": 201, + ""max_price"": 200 + } +]"); + + var results = a.SelectTokens("[?(true)]").ToList(); + Assert.AreEqual(3, results.Count); + Assert.AreEqual(a[0], results[0]); + Assert.AreEqual(a[1], results[1]); + Assert.AreEqual(a[2], results[2]); + } + + [Test] + public void Exists_Null() + { + JArray a = JArray.Parse(@"[ + { + ""price"": 199, + ""max_price"": 200 + }, + { + ""price"": 200, + ""max_price"": 200 + }, + { + ""price"": 201, + ""max_price"": 200 + } +]"); + + var results = a.SelectTokens("[?(true)]").ToList(); + Assert.AreEqual(3, results.Count); + Assert.AreEqual(a[0], results[0]); + Assert.AreEqual(a[1], results[1]); + Assert.AreEqual(a[2], results[2]); + } + [Test] public void WildcardWithProperty() { diff --git a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs index 3a113d68..d2faab98 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs @@ -46,6 +46,31 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath [TestFixture] public class JPathParseTests : TestFixtureBase { + [Test] + public void BooleanQuery_TwoValues() + { + JPath path = new JPath("[?(1 > 2)]"); + Assert.AreEqual(1, path.Filters.Count); + BooleanQueryExpression booleanExpression = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression; + Assert.AreEqual(new JValue(1), booleanExpression.Left); + Assert.AreEqual(new JValue(1), booleanExpression.Right); + Assert.AreEqual(QueryOperator.GreaterThan, booleanExpression.Operator); + } + + [Test] + public void BooleanQuery_TwoPaths() + { + JPath path = new JPath("[?(@.price > @.max_price)]"); + Assert.AreEqual(1, path.Filters.Count); + BooleanQueryExpression booleanExpression = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression; + List leftPaths = (List)booleanExpression.Left; + List rightPaths = (List)booleanExpression.Right; + + Assert.AreEqual("price", ((FieldFilter)leftPaths[0]).Name); + Assert.AreEqual("max_price", ((FieldFilter)rightPaths[0]).Name); + Assert.AreEqual(QueryOperator.GreaterThan, booleanExpression.Operator); + } + [Test] public void SingleProperty() { @@ -228,8 +253,9 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression; Assert.AreEqual(QueryOperator.Exists, expressions.Operator); - Assert.AreEqual(1, expressions.Path.Count); - Assert.AreEqual("name", ((ScanFilter)expressions.Path[0]).Name); + List paths = (List)expressions.Left; + Assert.AreEqual(1, paths.Count); + Assert.AreEqual("name", ((ScanFilter)paths[0]).Name); } [Test] @@ -240,7 +266,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression; Assert.AreEqual(QueryOperator.Equals, expressions.Operator); - Assert.AreEqual("hi", (string)expressions.Value); + Assert.AreEqual("hi", (string)(JToken)expressions.Right); } [Test] @@ -251,7 +277,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression; Assert.AreEqual(QueryOperator.Equals, expressions.Operator); - Assert.AreEqual("h'i", (string)expressions.Value); + Assert.AreEqual("h'i", (string)(JToken)expressions.Right); } [Test] @@ -262,7 +288,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression; Assert.AreEqual(QueryOperator.Equals, expressions.Operator); - Assert.AreEqual("h\\i", (string)expressions.Value); + Assert.AreEqual("h\\i", (string)(JToken)expressions.Right); } [Test] @@ -279,7 +305,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression; Assert.AreEqual(QueryOperator.Equals, expressions.Operator); - Assert.AreEqual(false, (bool)expressions.Value); + Assert.AreEqual(false, (bool)(JToken)expressions.Right); } [Test] @@ -290,7 +316,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression; Assert.AreEqual(QueryOperator.Equals, expressions.Operator); - Assert.AreEqual(true, (bool)expressions.Value); + Assert.AreEqual(true, (bool)(JToken)expressions.Right); } [Test] @@ -301,7 +327,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression; Assert.AreEqual(QueryOperator.Equals, expressions.Operator); - Assert.AreEqual(null, expressions.Value.Value); + Assert.AreEqual(null, ((JValue)expressions.Right).Value); } [Test] @@ -309,7 +335,8 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath { JPath path = new JPath("[?(@..name<>null)]"); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression; - Assert.AreEqual("name", ((ScanFilter)expressions.Path[0]).Name); + List paths = (List)expressions.Left; + Assert.AreEqual("name", ((ScanFilter)paths[0]).Name); } [Test] @@ -365,7 +392,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath { JPath path = new JPath("[?(@.name>=12)]"); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression; - Assert.AreEqual(12, (int)expressions.Value); + Assert.AreEqual(12, (int)(JToken)expressions.Right); } [Test] @@ -373,7 +400,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath { JPath path = new JPath("[?(@.name>=-12)]"); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression; - Assert.AreEqual(-12, (int)expressions.Value); + Assert.AreEqual(-12, (int)(JToken)expressions.Right); } [Test] @@ -381,7 +408,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath { JPath path = new JPath("[?(@.name>=12.1)]"); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression; - Assert.AreEqual(12.1d, (double)expressions.Value); + Assert.AreEqual(12.1d, (double)(JToken)expressions.Right); } [Test] @@ -391,10 +418,16 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath CompositeExpression expressions = (CompositeExpression)((QueryFilter)path.Filters[0]).Expression; Assert.AreEqual(QueryOperator.And, expressions.Operator); Assert.AreEqual(2, expressions.Expressions.Count); - Assert.AreEqual("name", ((FieldFilter)((BooleanQueryExpression)expressions.Expressions[0]).Path[0]).Name); - Assert.AreEqual(QueryOperator.Exists, expressions.Expressions[0].Operator); - Assert.AreEqual("title", ((FieldFilter)((BooleanQueryExpression)expressions.Expressions[1]).Path[0]).Name); - Assert.AreEqual(QueryOperator.Exists, expressions.Expressions[1].Operator); + + var first = (BooleanQueryExpression)expressions.Expressions[0]; + var firstPaths = (List)first.Left; + Assert.AreEqual("name", ((FieldFilter)firstPaths[0]).Name); + Assert.AreEqual(QueryOperator.Exists, first.Operator); + + var second = (BooleanQueryExpression)expressions.Expressions[1]; + var secondPaths = (List)second.Left; + Assert.AreEqual("title", ((FieldFilter)secondPaths[0]).Name); + Assert.AreEqual(QueryOperator.Exists, second.Operator); } [Test] @@ -404,15 +437,24 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath CompositeExpression andExpression = (CompositeExpression)((QueryFilter)path.Filters[0]).Expression; Assert.AreEqual(QueryOperator.And, andExpression.Operator); Assert.AreEqual(2, andExpression.Expressions.Count); - Assert.AreEqual("name", ((FieldFilter)((BooleanQueryExpression)andExpression.Expressions[0]).Path[0]).Name); - Assert.AreEqual(QueryOperator.Exists, andExpression.Expressions[0].Operator); + + var first = (BooleanQueryExpression)andExpression.Expressions[0]; + var firstPaths = (List)first.Left; + Assert.AreEqual("name", ((FieldFilter)firstPaths[0]).Name); + Assert.AreEqual(QueryOperator.Exists, first.Operator); CompositeExpression orExpression = (CompositeExpression)andExpression.Expressions[1]; Assert.AreEqual(2, orExpression.Expressions.Count); - Assert.AreEqual("title", ((FieldFilter)((BooleanQueryExpression)orExpression.Expressions[0]).Path[0]).Name); - Assert.AreEqual(QueryOperator.Exists, orExpression.Expressions[0].Operator); - Assert.AreEqual("pie", ((FieldFilter)((BooleanQueryExpression)orExpression.Expressions[1]).Path[0]).Name); - Assert.AreEqual(QueryOperator.Exists, orExpression.Expressions[1].Operator); + + var orFirst = (BooleanQueryExpression)orExpression.Expressions[0]; + var orFirstPaths = (List)orFirst.Left; + Assert.AreEqual("title", ((FieldFilter)orFirstPaths[0]).Name); + Assert.AreEqual(QueryOperator.Exists, orFirst.Operator); + + var orSecond = (BooleanQueryExpression)orExpression.Expressions[1]; + var orSecondPaths = (List)orSecond.Left; + Assert.AreEqual("pie", ((FieldFilter)orSecondPaths[0]).Name); + Assert.AreEqual(QueryOperator.Exists, orSecond.Operator); } [Test] @@ -420,9 +462,10 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath { JPath path = new JPath("[?($.name>=12.1)]"); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression; - Assert.AreEqual(2, expressions.Path.Count); - Assert.IsInstanceOf(typeof(RootFilter), expressions.Path[0]); - Assert.IsInstanceOf(typeof(FieldFilter), expressions.Path[1]); + List paths = (List)expressions.Left; + Assert.AreEqual(2, paths.Count); + Assert.IsInstanceOf(typeof(RootFilter), paths[0]); + Assert.IsInstanceOf(typeof(FieldFilter), paths[1]); } [Test] @@ -478,7 +521,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath { JPath path = new JPath("[?(@.name>=5.56789e+0)]"); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression; - Assert.AreEqual(5.56789e+0, (double)expressions.Value); + Assert.AreEqual(5.56789e+0, (double)(JToken)expressions.Right); } [Test] diff --git a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs index 10dc218b..1ca720b0 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs @@ -53,7 +53,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath new BooleanQueryExpression { Operator = QueryOperator.Exists, - Path = new List + Left = new List { new FieldFilter { @@ -64,7 +64,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath new BooleanQueryExpression { Operator = QueryOperator.Exists, - Path = new List + Left = new List { new FieldFilter { @@ -111,7 +111,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath new BooleanQueryExpression { Operator = QueryOperator.Exists, - Path = new List + Left = new List { new FieldFilter { @@ -122,7 +122,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath new BooleanQueryExpression { Operator = QueryOperator.Exists, - Path = new List + Left = new List { new FieldFilter { @@ -164,8 +164,8 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath BooleanQueryExpression e1 = new BooleanQueryExpression { Operator = QueryOperator.LessThan, - Value = new JValue(3), - Path = new List + Right = new JValue(3), + Left = new List { new ArrayIndexFilter() } @@ -179,8 +179,8 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath BooleanQueryExpression e2 = new BooleanQueryExpression { Operator = QueryOperator.LessThanOrEquals, - Value = new JValue(3), - Path = new List + Right = new JValue(3), + Left = new List { new ArrayIndexFilter() } diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs b/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs index abae9e8d..58f66737 100644 --- a/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs +++ b/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs @@ -423,6 +423,63 @@ namespace Newtonsoft.Json.Linq.JsonPath }; } + private bool TryParseExpression(out List expressionPath) + { + if (_expression[_currentIndex] == '$') + { + expressionPath = new List(); + expressionPath.Add(RootFilter.Instance); + } + else if (_expression[_currentIndex] == '@') + { + expressionPath = new List(); + } + else + { + expressionPath = null; + return false; + } + + _currentIndex++; + + if (ParsePath(expressionPath, _currentIndex, true)) + { + throw new JsonException("Path ended with open query."); + } + + return true; + } + + private JsonException ThrowUnexpectedCharacterException() + { + return new JsonException("Unexpected character while parsing path query: " + _expression[_currentIndex]); + } + + private object ParseSide() + { + EatWhitespace(); + + List expressionPath; + if (TryParseExpression(out expressionPath)) + { + EatWhitespace(); + EnsureLength("Path ended with open query."); + + return expressionPath; + } + + object value; + if (TryParseValue(out value)) + { + EatWhitespace(); + EnsureLength("Path ended with open query."); + + return new JValue(value); + } + + throw ThrowUnexpectedCharacterException(); + } + private QueryExpression ParseExpression() { QueryExpression rootExpression = null; @@ -430,31 +487,10 @@ namespace Newtonsoft.Json.Linq.JsonPath while (_currentIndex < _expression.Length) { - EatWhitespace(); - - List expressionPath = new List(); - - if (_expression[_currentIndex] == '$') - { - expressionPath.Add(RootFilter.Instance); - } - else if (_expression[_currentIndex] != '@') - { - throw new JsonException("Unexpected character while parsing path query: " + _expression[_currentIndex]); - } - - _currentIndex++; - - if (ParsePath(expressionPath, _currentIndex, true)) - { - throw new JsonException("Path ended with open query."); - } - - EatWhitespace(); - EnsureLength("Path ended with open query."); + object left = ParseSide(); + object right = null; QueryOperator op; - object value = null; if (_expression[_currentIndex] == ')' || _expression[_currentIndex] == '|' || _expression[_currentIndex] == '&') @@ -465,20 +501,14 @@ namespace Newtonsoft.Json.Linq.JsonPath { op = ParseOperator(); - EatWhitespace(); - EnsureLength("Path ended with open query."); - - value = ParseValue(); - - EatWhitespace(); - EnsureLength("Path ended with open query."); + right = ParseSide(); } BooleanQueryExpression booleanExpression = new BooleanQueryExpression { - Path = expressionPath, + Left = left, Operator = op, - Value = (op != QueryOperator.Exists) ? new JValue(value) : null + Right = right }; if (_expression[_currentIndex] == ')') @@ -491,8 +521,13 @@ namespace Newtonsoft.Json.Linq.JsonPath return booleanExpression; } - if (_expression[_currentIndex] == '&' && Match("&&")) + if (_expression[_currentIndex] == '&') { + if (!Match("&&")) + { + throw ThrowUnexpectedCharacterException(); + } + if (parentExpression == null || parentExpression.Operator != QueryOperator.And) { CompositeExpression andExpression = new CompositeExpression { Operator = QueryOperator.And }; @@ -512,8 +547,13 @@ namespace Newtonsoft.Json.Linq.JsonPath parentExpression.Expressions.Add(booleanExpression); } - if (_expression[_currentIndex] == '|' && Match("||")) + if (_expression[_currentIndex] == '|') { + if (!Match("||")) + { + throw ThrowUnexpectedCharacterException(); + } + if (parentExpression == null || parentExpression.Operator != QueryOperator.Or) { CompositeExpression orExpression = new CompositeExpression { Operator = QueryOperator.Or }; @@ -538,12 +578,13 @@ namespace Newtonsoft.Json.Linq.JsonPath throw new JsonException("Path ended with open query."); } - private object ParseValue() + private bool TryParseValue(out object value) { char currentChar = _expression[_currentIndex]; if (currentChar == '\'') { - return ReadQuotedString(); + value = ReadQuotedString(); + return true; } else if (char.IsDigit(currentChar) || currentChar == '-') { @@ -561,26 +602,16 @@ namespace Newtonsoft.Json.Linq.JsonPath if (numberText.IndexOfAny(new char[] { '.', 'E', 'e' }) != -1) { double d; - if (double.TryParse(numberText, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out d)) - { - return d; - } - else - { - throw new JsonException("Could not read query value."); - } + bool result = double.TryParse(numberText, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out d); + value = d; + return result; } else { long l; - if (long.TryParse(numberText, NumberStyles.Integer, CultureInfo.InvariantCulture, out l)) - { - return l; - } - else - { - throw new JsonException("Could not read query value."); - } + bool result = long.TryParse(numberText, NumberStyles.Integer, CultureInfo.InvariantCulture, out l); + value = l; + return result; } } else @@ -594,6 +625,7 @@ namespace Newtonsoft.Json.Linq.JsonPath { if (Match("true")) { + value = true; return true; } } @@ -601,18 +633,21 @@ namespace Newtonsoft.Json.Linq.JsonPath { if (Match("false")) { - return false; + value = false; + return true; } } else if (currentChar == 'n') { if (Match("null")) { - return null; + value = null; + return true; } } - throw new JsonException("Could not read query value."); + value = null; + return false; } private string ReadQuotedString() diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs b/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs index bf6515bd..cbeafda9 100644 --- a/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs +++ b/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs @@ -66,69 +66,45 @@ namespace Newtonsoft.Json.Linq.JsonPath internal class BooleanQueryExpression : QueryExpression { - public List Path { get; set; } - public JValue Value { get; set; } + public object Left { get; set; } + public object Right { get; set; } + + private IEnumerable GetResult(JToken root, JToken t, object o) + { + JToken resultToken = o as JToken; + if (resultToken != null) + { + return new[] { resultToken }; + } + + List pathFilters = o as List; + if (pathFilters != null) + { + return JPath.Evaluate(pathFilters, root, t, false); + } + + return new JToken[0]; + } public override bool IsMatch(JToken root, JToken t) { - IEnumerable pathResult = JPath.Evaluate(Path, root, t, false); + IEnumerable rightResults = GetResult(root, t, Right); + IEnumerable leftResults = GetResult(root, t, Left); - foreach (JToken r in pathResult) + foreach (JToken leftResult in leftResults) { - JValue v = r as JValue; - if (v != null) + if (Operator == QueryOperator.Exists) { - switch (Operator) - { - case QueryOperator.Equals: - if (EqualsWithStringCoercion(v, Value)) - { - return true; - } - break; - case QueryOperator.NotEquals: - if (!EqualsWithStringCoercion(v, Value)) - { - return true; - } - break; - case QueryOperator.GreaterThan: - if (v.CompareTo(Value) > 0) - { - return true; - } - break; - case QueryOperator.GreaterThanOrEquals: - if (v.CompareTo(Value) >= 0) - { - return true; - } - break; - case QueryOperator.LessThan: - if (v.CompareTo(Value) < 0) - { - return true; - } - break; - case QueryOperator.LessThanOrEquals: - if (v.CompareTo(Value) <= 0) - { - return true; - } - break; - case QueryOperator.Exists: - return true; - } + return true; } else { - switch (Operator) + foreach (JToken rightResult in rightResults) { - case QueryOperator.Exists: - // you can only specify primative types in a comparison - // notequals will always be true - case QueryOperator.NotEquals: + if (MatchTokens(leftResult, rightResult)) + { return true; + } } } } @@ -136,6 +112,70 @@ namespace Newtonsoft.Json.Linq.JsonPath return false; } + private bool MatchTokens(JToken leftResult, JToken rightResult) + { + JValue leftValue = leftResult as JValue; + JValue rightValue = rightResult as JValue; + + if (leftValue != null && rightValue != null) + { + switch (Operator) + { + case QueryOperator.Equals: + if (EqualsWithStringCoercion(leftValue, rightValue)) + { + return true; + } + break; + case QueryOperator.NotEquals: + if (!EqualsWithStringCoercion(leftValue, rightValue)) + { + return true; + } + break; + case QueryOperator.GreaterThan: + if (leftValue.CompareTo(rightValue) > 0) + { + return true; + } + break; + case QueryOperator.GreaterThanOrEquals: + if (leftValue.CompareTo(rightValue) >= 0) + { + return true; + } + break; + case QueryOperator.LessThan: + if (leftValue.CompareTo(rightValue) < 0) + { + return true; + } + break; + case QueryOperator.LessThanOrEquals: + if (leftValue.CompareTo(rightValue) <= 0) + { + return true; + } + break; + case QueryOperator.Exists: + return true; + } + } + else + { + switch (Operator) + { + case QueryOperator.Exists: + // you can only specify primative types in a comparison + // notequals will always be true + case QueryOperator.NotEquals: + return true; + } + } + + return false; + } + private bool EqualsWithStringCoercion(JValue value, JValue queryValue) { if (value.Equals(queryValue))