-JsonPath++
This commit is contained in:
@@ -355,6 +355,111 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
Assert.AreEqual("Jeff", a2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExistsQuery()
|
||||
{
|
||||
JArray a = new JArray(new JObject(new JProperty("hi", "ho")), new JObject(new JProperty("hi2", "ha")));
|
||||
|
||||
IList<JToken> t = a.SelectTokens("[ ?( @.hi ) ]").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(1, t.Count);
|
||||
Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("hi", "ho")), t[0]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EqualsQuery()
|
||||
{
|
||||
JArray a = new JArray(
|
||||
new JObject(new JProperty("hi", "ho")),
|
||||
new JObject(new JProperty("hi", "ha")));
|
||||
|
||||
IList<JToken> t = a.SelectTokens("[ ?( @.['hi'] == 'ha' ) ]").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(1, t.Count);
|
||||
Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("hi", "ha")), t[0]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NotEqualsQuery()
|
||||
{
|
||||
JArray a = new JArray(
|
||||
new JArray(new JObject(new JProperty("hi", "ho"))),
|
||||
new JArray(new JObject(new JProperty("hi", "ha"))));
|
||||
|
||||
IList<JToken> t = a.SelectTokens("[ ?( @..hi <> 'ha' ) ]").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(1, t.Count);
|
||||
Assert.IsTrue(JToken.DeepEquals(new JArray(new JObject(new JProperty("hi", "ho"))), t[0]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoPathQuery()
|
||||
{
|
||||
JArray a = new JArray(1, 2, 3);
|
||||
|
||||
IList<JToken> t = a.SelectTokens("[ ?( @ > 1 ) ]").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(2, t.Count);
|
||||
Assert.AreEqual(2, (int)t[0]);
|
||||
Assert.AreEqual(3, (int)t[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GreaterQuery()
|
||||
{
|
||||
JArray a = new JArray(
|
||||
new JObject(new JProperty("hi", 1)),
|
||||
new JObject(new JProperty("hi", 2)),
|
||||
new JObject(new JProperty("hi", 3)));
|
||||
|
||||
IList<JToken> t = a.SelectTokens("[ ?( @.hi > 1 ) ]").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]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GreaterOrEqualQuery()
|
||||
{
|
||||
JArray a = new JArray(
|
||||
new JObject(new JProperty("hi", 1)),
|
||||
new JObject(new JProperty("hi", 2)),
|
||||
new JObject(new JProperty("hi", 3)));
|
||||
|
||||
IList<JToken> t = a.SelectTokens("[ ?( @.hi >= 1 ) ]").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(3, t.Count);
|
||||
Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("hi", 1)), t[0]));
|
||||
Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("hi", 2)), t[1]));
|
||||
Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("hi", 3)), t[2]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NestedQuery()
|
||||
{
|
||||
JArray a = new JArray(
|
||||
new JObject(
|
||||
new JProperty("name", "Bad Boys"),
|
||||
new JProperty("cast", new JArray(
|
||||
new JObject(new JProperty("name", "Will Smith"))))),
|
||||
new JObject(
|
||||
new JProperty("name", "Independence Day"),
|
||||
new JProperty("cast", new JArray(
|
||||
new JObject(new JProperty("name", "Will Smith"))))),
|
||||
new JObject(
|
||||
new JProperty("name", "The Rock"),
|
||||
new JProperty("cast", new JArray(
|
||||
new JObject(new JProperty("name", "Nick Cage")))))
|
||||
);
|
||||
|
||||
IList<JToken> t = a.SelectTokens("[?(@.cast[?(@.name=='Will Smith')])].name").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(2, t.Count);
|
||||
Assert.AreEqual("Bad Boys", (string)t[0]);
|
||||
Assert.AreEqual("Independence Day", (string)t[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PathWithConstructor()
|
||||
{
|
||||
|
||||
@@ -38,7 +38,6 @@ using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Utilities.LinqBridge;
|
||||
#else
|
||||
using System.Linq;
|
||||
|
||||
#endif
|
||||
|
||||
namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
@@ -211,13 +210,184 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndFilter()
|
||||
public void SinglePropertyAndExistsQuery()
|
||||
{
|
||||
JPath path = new JPath("Blah[?(@.name=='hi')]");
|
||||
JPath path = new JPath("Blah[ ?( @..name ) ]");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
List<object> expressions = ((QueryFilter)path.Filters[1]).Expression;
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(1, expressions.Count);
|
||||
Assert.AreEqual(QueryOperator.Exists, expressions[0].Operator);
|
||||
Assert.AreEqual(1, expressions[0].Path.Count);
|
||||
Assert.AreEqual("name", ((ScanFilter)expressions[0].Path[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndFilterWithWhitespace()
|
||||
{
|
||||
JPath path = new JPath("Blah[ ?( @.name=='hi' ) ]");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(1, expressions.Count);
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions[0].Operator);
|
||||
Assert.AreEqual("hi", (string)expressions[0].Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndFilterWithEscapeQuote()
|
||||
{
|
||||
JPath path = new JPath(@"Blah[ ?( @.name=='h\'i' ) ]");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(1, expressions.Count);
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions[0].Operator);
|
||||
Assert.AreEqual("h'i", (string)expressions[0].Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndFilterWithDoubleEscape()
|
||||
{
|
||||
JPath path = new JPath(@"Blah[ ?( @.name=='h\\i' ) ]");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(1, expressions.Count);
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions[0].Operator);
|
||||
Assert.AreEqual("h\\i", (string)expressions[0].Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndFilterWithUnknownEscape()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
@"Unknown escape chracter: \i",
|
||||
() => { new JPath(@"Blah[ ?( @.name=='h\i' ) ]"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndFilterWithFalse()
|
||||
{
|
||||
JPath path = new JPath("Blah[ ?( @.name==false ) ]");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(1, expressions.Count);
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions[0].Operator);
|
||||
Assert.AreEqual(false, (bool)expressions[0].Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndFilterWithTrue()
|
||||
{
|
||||
JPath path = new JPath("Blah[ ?( @.name==true ) ]");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(1, expressions.Count);
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions[0].Operator);
|
||||
Assert.AreEqual(true, (bool)expressions[0].Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndFilterWithNull()
|
||||
{
|
||||
JPath path = new JPath("Blah[ ?( @.name==null ) ]");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(1, expressions.Count);
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions[0].Operator);
|
||||
Assert.AreEqual(null, expressions[0].Value.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterWithScan()
|
||||
{
|
||||
JPath path = new JPath("[?(@..name<>null)]");
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual("name", ((ScanFilter)expressions[0].Path[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterWithNotEquals()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name<>null)]");
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.NotEquals, expressions[0].Operator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterWithNotEquals2()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name!=null)]");
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.NotEquals, expressions[0].Operator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterWithLessThan()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name<null)]");
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.LessThan, expressions[0].Operator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterWithLessThanOrEquals()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name<=null)]");
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.LessThanOrEquals, expressions[0].Operator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterWithGreaterThan()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name>null)]");
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.GreaterThan, expressions[0].Operator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterWithGreaterThanOrEquals()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name>=null)]");
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.GreaterThanOrEquals, expressions[0].Operator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterWithInteger()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name>=12)]");
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(12, (int)expressions[0].Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterWithNegativeInteger()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name>=-12)]");
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(-12, (int)expressions[0].Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterWithFloat()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name>=12.1)]");
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(12.1d, (double)expressions[0].Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterWithFloatExp()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name>=5.56789e+0)]");
|
||||
List<QueryExpression> expressions = ((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(5.56789e+0, (double)expressions[0].Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -14,14 +14,11 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
if (Step == 0)
|
||||
throw new JsonException("Step cannot be zero.");
|
||||
|
||||
// todo error when no match
|
||||
foreach (JToken t in current)
|
||||
{
|
||||
IList<JToken> sourceCollection = t as IList<JToken> ?? new List<JToken>(t);
|
||||
|
||||
// nothing to slice
|
||||
if (sourceCollection.Count == 0)
|
||||
yield break;
|
||||
|
||||
// set defaults for null arguments
|
||||
int stepCount = Step ?? 1;
|
||||
int startIndex = Start ?? ((stepCount > 0) ? 0 : sourceCollection.Count - 1);
|
||||
|
||||
@@ -19,10 +19,11 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
JToken v = o[Name];
|
||||
|
||||
if (v == null && errorWhenNoMatch)
|
||||
throw new JsonException("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, Name));
|
||||
if (v != null)
|
||||
yield return v;
|
||||
|
||||
yield return v;
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, Name));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -27,6 +27,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
|
||||
@@ -51,8 +52,6 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
private void ParseMain()
|
||||
{
|
||||
int currentPartStartIndex = _currentIndex;
|
||||
bool followingIndexer = false;
|
||||
bool scan = false;
|
||||
|
||||
if (_expression.Length == 0)
|
||||
return;
|
||||
@@ -68,20 +67,27 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
if (_expression[_currentIndex] != '.')
|
||||
throw new JsonException("Unexpected character while parsing path: " + _expression[_currentIndex]);
|
||||
|
||||
_currentIndex++;
|
||||
|
||||
EnsureLength("Unexpected end while parsing path.");
|
||||
|
||||
if (_expression[_currentIndex] == '.')
|
||||
{
|
||||
scan = true;
|
||||
_currentIndex++;
|
||||
}
|
||||
|
||||
currentPartStartIndex = _currentIndex;
|
||||
}
|
||||
|
||||
while (_currentIndex < _expression.Length)
|
||||
if (!ParsePath(Filters, currentPartStartIndex, false))
|
||||
{
|
||||
int lastCharacterIndex = _currentIndex;
|
||||
|
||||
EatWhitespace();
|
||||
|
||||
if (_currentIndex < _expression.Length)
|
||||
throw new JsonException("Unexpected character while parsing path: " + _expression[lastCharacterIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ParsePath(List<PathFilter> filters, int currentPartStartIndex, bool query)
|
||||
{
|
||||
bool scan = false;
|
||||
bool followingIndexer = false;
|
||||
|
||||
bool ended = false;
|
||||
while (_currentIndex < _expression.Length && !ended)
|
||||
{
|
||||
char currentChar = _expression[_currentIndex];
|
||||
|
||||
@@ -93,22 +99,23 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
Filters.Add(filter);
|
||||
filters.Add(filter);
|
||||
scan = false;
|
||||
}
|
||||
|
||||
ParseIndexer(currentChar);
|
||||
filters.Add(ParseIndexer(currentChar));
|
||||
_currentIndex++;
|
||||
currentPartStartIndex = _currentIndex;
|
||||
followingIndexer = true;
|
||||
break;
|
||||
case ']':
|
||||
case ')':
|
||||
throw new JsonException("Unexpected character while parsing path: " + currentChar);
|
||||
ended = true;
|
||||
break;
|
||||
case ' ':
|
||||
EatWhitespace();
|
||||
//EatWhitespace();
|
||||
if (_currentIndex < _expression.Length)
|
||||
throw new JsonException("Unexpected character while parsing path: " + currentChar);
|
||||
ended = true;
|
||||
break;
|
||||
case '.':
|
||||
if (_currentIndex > currentPartStartIndex)
|
||||
@@ -117,7 +124,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
if (member == "*")
|
||||
member = null;
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
Filters.Add(filter);
|
||||
filters.Add(filter);
|
||||
scan = false;
|
||||
}
|
||||
if (_currentIndex + 1 < _expression.Length && _expression[_currentIndex + 1] == '.')
|
||||
@@ -130,10 +137,17 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
followingIndexer = false;
|
||||
break;
|
||||
default:
|
||||
if (followingIndexer)
|
||||
throw new JsonException("Unexpected character following indexer: " + currentChar);
|
||||
if (query && (currentChar == '=' || currentChar == '<' || currentChar == '!' || currentChar == '>'))
|
||||
{
|
||||
ended = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (followingIndexer)
|
||||
throw new JsonException("Unexpected character following indexer: " + currentChar);
|
||||
|
||||
_currentIndex++;
|
||||
_currentIndex++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -144,11 +158,13 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
if (member == "*")
|
||||
member = null;
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
Filters.Add(filter);
|
||||
filters.Add(filter);
|
||||
}
|
||||
|
||||
return (_currentIndex == _expression.Length);
|
||||
}
|
||||
|
||||
private void ParseIndexer(char indexerOpenChar)
|
||||
private PathFilter ParseIndexer(char indexerOpenChar)
|
||||
{
|
||||
_currentIndex++;
|
||||
|
||||
@@ -160,19 +176,19 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
|
||||
if (_expression[_currentIndex] == '\'')
|
||||
{
|
||||
ParseQuotedField(indexerCloseChar);
|
||||
return ParseQuotedField(indexerCloseChar);
|
||||
}
|
||||
else if (_expression[_currentIndex] == '?')
|
||||
{
|
||||
ParseQuery(indexerCloseChar);
|
||||
return ParseQuery(indexerCloseChar);
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseArrayIndexer(indexerCloseChar);
|
||||
return ParseArrayIndexer(indexerCloseChar);
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseArrayIndexer(char indexerCloseChar)
|
||||
private PathFilter ParseArrayIndexer(char indexerCloseChar)
|
||||
{
|
||||
int start = _currentIndex;
|
||||
int? end = null;
|
||||
@@ -206,7 +222,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
int index = Convert.ToInt32(indexer, CultureInfo.InvariantCulture);
|
||||
|
||||
indexes.Add(index);
|
||||
Filters.Add(new ArrayMultipleIndexFilter { Indexes = indexes });
|
||||
return new ArrayMultipleIndexFilter { Indexes = indexes };
|
||||
}
|
||||
else if (colonCount > 0)
|
||||
{
|
||||
@@ -221,7 +237,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
step = index;
|
||||
}
|
||||
|
||||
Filters.Add(new ArraySliceFilter { Start = startIndex, End = endIndex, Step = step });
|
||||
return new ArraySliceFilter { Start = startIndex, End = endIndex, Step = step };
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -231,10 +247,8 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
string indexer = _expression.Substring(start, length);
|
||||
int index = Convert.ToInt32(indexer, CultureInfo.InvariantCulture);
|
||||
|
||||
Filters.Add(new ArrayIndexFilter { Index = index });
|
||||
return new ArrayIndexFilter { Index = index };
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (currentCharacter == ',')
|
||||
{
|
||||
int length = (end ?? _currentIndex) - start;
|
||||
@@ -264,8 +278,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
if (_expression[_currentIndex] != indexerCloseChar)
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter);
|
||||
|
||||
Filters.Add(new ArrayIndexFilter());
|
||||
return;
|
||||
return new ArrayIndexFilter();
|
||||
}
|
||||
else if (currentCharacter == ':')
|
||||
{
|
||||
@@ -321,7 +334,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseQuery(char indexerCloseChar)
|
||||
private PathFilter ParseQuery(char indexerCloseChar)
|
||||
{
|
||||
_currentIndex++;
|
||||
EnsureLength("Path ended with open indexer.");
|
||||
@@ -329,25 +342,27 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
if (_expression[_currentIndex] != '(')
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]);
|
||||
|
||||
List<object> expressions = new List<object>();
|
||||
_currentIndex++;
|
||||
|
||||
List<QueryExpression> expressions = new List<QueryExpression>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
ParseExpression();
|
||||
expressions.Add(string.Empty);
|
||||
expressions.Add(ParseExpression());
|
||||
|
||||
if (_expression[_currentIndex] == ')')
|
||||
{
|
||||
_currentIndex++;
|
||||
EnsureLength("Path ended with open indexer.");
|
||||
EatWhitespace();
|
||||
|
||||
if (_expression[_currentIndex] != indexerCloseChar)
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]);
|
||||
|
||||
Filters.Add(new QueryFilter
|
||||
return new QueryFilter
|
||||
{
|
||||
Expression = expressions
|
||||
});
|
||||
return;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -356,25 +371,179 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseExpression()
|
||||
private QueryExpression ParseExpression()
|
||||
{
|
||||
_currentIndex++;
|
||||
while (_currentIndex < _expression.Length)
|
||||
{
|
||||
char currentCharacter = _expression[_currentIndex];
|
||||
EatWhitespace();
|
||||
|
||||
if (currentCharacter == ')')
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentIndex++;
|
||||
}
|
||||
if (_expression[_currentIndex] != '@')
|
||||
throw new JsonException("Unexpected character while parsing path query: " + _expression[_currentIndex]);
|
||||
|
||||
_currentIndex++;
|
||||
|
||||
List<PathFilter> expressionPath = new List<PathFilter>();
|
||||
|
||||
if (ParsePath(expressionPath, _currentIndex, true))
|
||||
throw new JsonException("Path ended with open query.");
|
||||
|
||||
EatWhitespace();
|
||||
EnsureLength("Path ended with open query.");
|
||||
|
||||
QueryOperator op;
|
||||
object value = null;
|
||||
if (_expression[_currentIndex] == ')')
|
||||
{
|
||||
op = QueryOperator.Exists;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = ParseOperator();
|
||||
|
||||
EatWhitespace();
|
||||
EnsureLength("Path ended with open query.");
|
||||
|
||||
value = ParseValue();
|
||||
|
||||
EatWhitespace();
|
||||
EnsureLength("Path ended with open query.");
|
||||
}
|
||||
|
||||
return new QueryExpression
|
||||
{
|
||||
Path = expressionPath,
|
||||
Operator = op,
|
||||
Value = (op != QueryOperator.Exists) ? new JValue(value) : null
|
||||
};
|
||||
}
|
||||
|
||||
private void ParseQuotedField(char indexerCloseChar)
|
||||
private object ParseValue()
|
||||
{
|
||||
char currentChar = _expression[_currentIndex];
|
||||
if (currentChar == '\'')
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
_currentIndex++;
|
||||
while (_currentIndex < _expression.Length)
|
||||
{
|
||||
currentChar = _expression[_currentIndex];
|
||||
if (currentChar == '\\' && _currentIndex + 1 < _expression.Length)
|
||||
{
|
||||
_currentIndex++;
|
||||
|
||||
if (_expression[_currentIndex] == '\'')
|
||||
sb.Append('\'');
|
||||
else if (_expression[_currentIndex] == '\\')
|
||||
sb.Append('\\');
|
||||
else
|
||||
throw new JsonException(@"Unknown escape chracter: \" + _expression[_currentIndex]);
|
||||
|
||||
_currentIndex++;
|
||||
}
|
||||
else if (currentChar == '\'')
|
||||
{
|
||||
_currentIndex++;
|
||||
return sb.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentIndex++;
|
||||
sb.Append(currentChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (char.IsDigit(currentChar) || currentChar == '-')
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(currentChar);
|
||||
|
||||
_currentIndex++;
|
||||
while (_currentIndex < _expression.Length)
|
||||
{
|
||||
currentChar = _expression[_currentIndex];
|
||||
if (currentChar == ' ' || currentChar == ')')
|
||||
{
|
||||
string numberText = sb.ToString();
|
||||
|
||||
if (numberText.IndexOfAny(new char[] { '.', 'E', 'e' }) != -1)
|
||||
{
|
||||
double d;
|
||||
if (double.TryParse(numberText, out d))
|
||||
return d;
|
||||
else
|
||||
throw new JsonException("Could not read query value.");
|
||||
}
|
||||
else
|
||||
{
|
||||
long l;
|
||||
if (long.TryParse(numberText, out l))
|
||||
return l;
|
||||
else
|
||||
throw new JsonException("Could not read query value.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(currentChar);
|
||||
_currentIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (currentChar == 't')
|
||||
{
|
||||
if (Match("true"))
|
||||
return true;
|
||||
}
|
||||
else if (currentChar == 'f')
|
||||
{
|
||||
if (Match("false"))
|
||||
return false;
|
||||
}
|
||||
else if (currentChar == 'n')
|
||||
{
|
||||
if (Match("null"))
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new JsonException("Could not read query value.");
|
||||
}
|
||||
|
||||
private bool Match(string s)
|
||||
{
|
||||
int currentPosition = _currentIndex;
|
||||
foreach (char c in s)
|
||||
{
|
||||
if (currentPosition < _expression.Length && _expression[currentPosition] == c)
|
||||
currentPosition++;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
_currentIndex = currentPosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
private QueryOperator ParseOperator()
|
||||
{
|
||||
if (_currentIndex + 1 >= _expression.Length)
|
||||
throw new JsonException("Path ended with open query.");
|
||||
|
||||
if (Match("=="))
|
||||
return QueryOperator.Equals;
|
||||
if (Match("!=") || Match("<>"))
|
||||
return QueryOperator.NotEquals;
|
||||
if (Match("<="))
|
||||
return QueryOperator.LessThanOrEquals;
|
||||
if (Match("<"))
|
||||
return QueryOperator.LessThan;
|
||||
if (Match(">="))
|
||||
return QueryOperator.GreaterThanOrEquals;
|
||||
if (Match(">"))
|
||||
return QueryOperator.GreaterThan;
|
||||
|
||||
throw new JsonException("Could not read query operator.");
|
||||
}
|
||||
|
||||
private FieldFilter ParseQuotedField(char indexerCloseChar)
|
||||
{
|
||||
_currentIndex++;
|
||||
int start = _currentIndex;
|
||||
@@ -398,9 +567,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter);
|
||||
|
||||
string indexer = _expression.Substring(start, length);
|
||||
Filters.Add(new FieldFilter { Name = indexer });
|
||||
|
||||
return;
|
||||
return new FieldFilter { Name = indexer };
|
||||
}
|
||||
|
||||
_currentIndex++;
|
||||
@@ -415,11 +582,15 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
throw new JsonException(message);
|
||||
}
|
||||
|
||||
internal IEnumerable<JToken> Evaluate(JToken root, bool errorWhenNoMatch)
|
||||
internal IEnumerable<JToken> Evaluate(JToken t, bool errorWhenNoMatch)
|
||||
{
|
||||
IEnumerable<JToken> current = new [] { root };
|
||||
return Evaluate(Filters, t, errorWhenNoMatch);
|
||||
}
|
||||
|
||||
foreach (PathFilter filter in Filters)
|
||||
internal static IEnumerable<JToken> Evaluate(List<PathFilter> filters, JToken t, bool errorWhenNoMatch)
|
||||
{
|
||||
IEnumerable<JToken> current = new[] { t };
|
||||
foreach (PathFilter filter in filters)
|
||||
{
|
||||
current = filter.ExecuteFilter(current, errorWhenNoMatch);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
internal enum QueryOperator
|
||||
{
|
||||
None,
|
||||
Equals,
|
||||
NotEquals,
|
||||
Exists,
|
||||
LessThan,
|
||||
LessThanOrEquals,
|
||||
GreaterThan,
|
||||
GreaterThanOrEquals
|
||||
}
|
||||
|
||||
internal class QueryExpression
|
||||
{
|
||||
public List<PathFilter> Path { get; set; }
|
||||
public QueryOperator Operator { get; set; }
|
||||
public JValue Value { get; set; }
|
||||
}
|
||||
|
||||
internal class QueryFilter : PathFilter
|
||||
{
|
||||
public List<object> Expression { get; set; }
|
||||
public List<QueryExpression> Expression { get; set; }
|
||||
|
||||
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
foreach (JToken t in current)
|
||||
{
|
||||
foreach (JToken v in t)
|
||||
{
|
||||
if (Evaluate(errorWhenNoMatch, v))
|
||||
yield return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool Evaluate(bool errorWhenNoMatch, JToken value)
|
||||
{
|
||||
foreach (QueryExpression expression in Expression)
|
||||
{
|
||||
IEnumerable<JToken> pathResult = JPath.Evaluate(expression.Path, value, errorWhenNoMatch);
|
||||
|
||||
foreach (JToken r in pathResult)
|
||||
{
|
||||
JValue v = r as JValue;
|
||||
switch (expression.Operator)
|
||||
{
|
||||
case QueryOperator.Equals:
|
||||
if (v != null && v.Equals(expression.Value))
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.NotEquals:
|
||||
if (v != null && !v.Equals(expression.Value))
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.GreaterThan:
|
||||
if (v != null && v.CompareTo(expression.Value) > 0)
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.GreaterThanOrEquals:
|
||||
if (v != null && v.CompareTo(expression.Value) >= 0)
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.LessThan:
|
||||
if (v != null && v.CompareTo(expression.Value) < 0)
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.LessThanOrEquals:
|
||||
if (v != null && v.CompareTo(expression.Value) <= 0)
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.Exists:
|
||||
return true;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user