diff --git a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs index 22ead06f..25957122 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathParseTests.cs @@ -315,16 +315,44 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath } [Test] - public void SinglePropertyAndFilterWithRegex() + public void SinglePropertyAndFilterWithRegexAndOptions() { JPath path = new JPath("Blah[ ?( @.name=~/hi/i ) ]"); Assert.AreEqual(2, path.Filters.Count); Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name); BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression; - Assert.AreEqual(QueryOperator.RegExEquals, expressions.Operator); + Assert.AreEqual(QueryOperator.RegexEquals, expressions.Operator); Assert.AreEqual("/hi/i", (string)(JToken)expressions.Right); } + [Test] + public void SinglePropertyAndFilterWithRegex() + { + JPath path = new JPath("Blah[?(@.title =~ /^.*Sword.*$/)]"); + Assert.AreEqual(2, path.Filters.Count); + Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name); + BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression; + Assert.AreEqual(QueryOperator.RegexEquals, expressions.Operator); + Assert.AreEqual("/^.*Sword.*$/", (string)(JToken)expressions.Right); + } + + [Test] + public void SinglePropertyAndFilterWithEscapedRegex() + { + JPath path = new JPath(@"Blah[?(@.title =~ /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g)]"); + Assert.AreEqual(2, path.Filters.Count); + Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name); + BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression; + Assert.AreEqual(QueryOperator.RegexEquals, expressions.Operator); + Assert.AreEqual(@"/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g", (string)(JToken)expressions.Right); + } + + [Test] + public void SinglePropertyAndFilterWithOpenRegex() + { + ExceptionAssert.Throws(() => { new JPath(@"Blah[?(@.title =~ /[\"); }, "Path ended with an open regex."); + } + [Test] public void SinglePropertyAndFilterWithUnknownEscape() { diff --git a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs index dc45b595..cdb3db07 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs @@ -159,11 +159,11 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath } [Test] - public void BooleanExpressionTest_RegExEqualsOperator() + public void BooleanExpressionTest_RegexEqualsOperator() { BooleanQueryExpression e1 = new BooleanQueryExpression { - Operator = QueryOperator.RegExEquals, + Operator = QueryOperator.RegexEquals, Right = new JValue("/foo.*d/"), Left = new List { @@ -178,7 +178,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath BooleanQueryExpression e2 = new BooleanQueryExpression { - Operator = QueryOperator.RegExEquals, + Operator = QueryOperator.RegexEquals, Right = new JValue("/Foo.*d/i"), Left = new List { @@ -193,11 +193,11 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath } [Test] - public void BooleanExpressionTest_RegExEqualsOperator_CornerCase() + public void BooleanExpressionTest_RegexEqualsOperator_CornerCase() { BooleanQueryExpression e1 = new BooleanQueryExpression { - Operator = QueryOperator.RegExEquals, + Operator = QueryOperator.RegexEquals, Right = new JValue("/// comment/"), Left = new List { @@ -210,7 +210,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath BooleanQueryExpression e2 = new BooleanQueryExpression { - Operator = QueryOperator.RegExEquals, + Operator = QueryOperator.RegexEquals, Right = new JValue("/.*/i"), Left = new List { diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs b/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs index 5c563d06..792aa62c 100644 --- a/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs +++ b/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs @@ -719,79 +719,45 @@ namespace Newtonsoft.Json.Linq.JsonPath private string ReadRegexString() { - var sb = new StringBuilder("/"); + int startIndex = _currentIndex; _currentIndex++; while (_currentIndex < _expression.Length) { char currentChar = _expression[_currentIndex]; + + // handle escaped / character if (currentChar == '\\' && _currentIndex + 1 < _expression.Length) { - _currentIndex++; - - if (_expression[_currentIndex] == '\'') - { - sb.Append('\''); - } - else if (_expression[_currentIndex] == '\\') - { - sb.Append('\\'); - } - else if (_expression[_currentIndex] == '/') - { - sb.Append('/'); - } - else - { - throw new JsonException(@"Unknown escape character: \" + _expression[_currentIndex]); - } - - _currentIndex++; + _currentIndex += 2; } else if (currentChar == '/') { - sb.Append(currentChar); _currentIndex++; - string options = ParseRegexOptions(); - sb.Append(options); + while (_currentIndex < _expression.Length) + { + currentChar = _expression[_currentIndex]; - return sb.ToString(); + if (char.IsLetter(currentChar)) + { + _currentIndex++; + } + else + { + break; + } + } + + return _expression.Substring(startIndex, _currentIndex - startIndex); } else { _currentIndex++; - sb.Append(currentChar); } } - throw new JsonException("Path ended with an open string."); - } - - private string ParseRegexOptions() - { - var optionsSb = new StringBuilder(); - - while (_currentIndex < _expression.Length) - { - char currentChar = _expression[_currentIndex]; - - if (currentChar == 'i' - || currentChar == 'm' - || currentChar == 's' - || currentChar == 'u' - || currentChar == 'x') - { - _currentIndex++; - optionsSb.Append(currentChar); - } - else - { - break; - } - } - - return optionsSb.ToString(); + throw new JsonException("Path ended with an open regex."); } private bool Match(string s) @@ -827,7 +793,7 @@ namespace Newtonsoft.Json.Linq.JsonPath if (Match("=~")) { - return QueryOperator.RegExEquals; + return QueryOperator.RegexEquals; } if (Match("!=") || Match("<>")) diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs b/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs index 83344fae..7cfcf3f3 100644 --- a/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs +++ b/Src/Newtonsoft.Json/Linq/JsonPath/QueryExpression.cs @@ -24,7 +24,7 @@ namespace Newtonsoft.Json.Linq.JsonPath GreaterThanOrEquals = 7, And = 8, Or = 9, - RegExEquals = 10 + RegexEquals = 10 } internal abstract class QueryExpression @@ -128,7 +128,7 @@ namespace Newtonsoft.Json.Linq.JsonPath { switch (Operator) { - case QueryOperator.RegExEquals: + case QueryOperator.RegexEquals: if (RegexEquals(leftValue, rightValue)) { return true; @@ -196,7 +196,7 @@ namespace Newtonsoft.Json.Linq.JsonPath return false; } - var regexText = (string)pattern.Value; + string regexText = (string)pattern.Value; int patternOptionDelimiterIndex = regexText.LastIndexOf('/'); string patternText = regexText.Substring(1, patternOptionDelimiterIndex - 1); diff --git a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs index 38877631..5c40ae8a 100644 --- a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs @@ -154,7 +154,7 @@ namespace Newtonsoft.Json.Utilities internal static RegexOptions GetRegexOptions(string optionsText) { - var options = RegexOptions.None; + RegexOptions options = RegexOptions.None; foreach (char c in optionsText) { switch (c) @@ -176,6 +176,5 @@ namespace Newtonsoft.Json.Utilities return options; } - } } \ No newline at end of file