-Refactor regex in JSON Path

This commit is contained in:
James Newton-King
2018-02-11 14:42:13 +13:00
parent 7f3292f6cb
commit 6d3b5077e2
5 changed files with 60 additions and 67 deletions
@@ -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<JsonException>(() => { new JPath(@"Blah[?(@.title =~ /[\"); }, "Path ended with an open regex.");
}
[Test]
public void SinglePropertyAndFilterWithUnknownEscape()
{
@@ -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<PathFilter>
{
@@ -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<PathFilter>
{
@@ -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<PathFilter>
{
@@ -210,7 +210,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
BooleanQueryExpression e2 = new BooleanQueryExpression
{
Operator = QueryOperator.RegExEquals,
Operator = QueryOperator.RegexEquals,
Right = new JValue("/<tag>.*</tag>/i"),
Left = new List<PathFilter>
{
+20 -54
View File
@@ -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("<>"))
@@ -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);
@@ -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;
}
}
}