-Added support for root object in JSONPath filters

This commit is contained in:
James Newton-King
2016-09-10 16:54:29 +12:00
parent 59618df631
commit ac2d619474
21 changed files with 128 additions and 44 deletions
@@ -949,5 +949,57 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
result = a.SelectTokens("$.[?(@.value)]").ToList(); result = a.SelectTokens("$.[?(@.value)]").ToList();
Assert.AreEqual(4, result.Count); Assert.AreEqual(4, result.Count);
} }
[Test]
public void RootInFilter()
{
string json = @"[
{
""store"" : {
""book"" : [
{
""category"" : ""reference"",
""author"" : ""Nigel Rees"",
""title"" : ""Sayings of the Century"",
""price"" : 8.95
},
{
""category"" : ""fiction"",
""author"" : ""Evelyn Waugh"",
""title"" : ""Sword of Honour"",
""price"" : 12.99
},
{
""category"" : ""fiction"",
""author"" : ""Herman Melville"",
""title"" : ""Moby Dick"",
""isbn"" : ""0-553-21311-3"",
""price"" : 8.99
},
{
""category"" : ""fiction"",
""author"" : ""J. R. R. Tolkien"",
""title"" : ""The Lord of the Rings"",
""isbn"" : ""0-395-19395-8"",
""price"" : 22.99
}
],
""bicycle"" : {
""color"" : ""red"",
""price"" : 19.95
}
},
""expensive"" : 10
}
]";
JArray a = JArray.Parse(json);
List<JToken> result = a.SelectTokens("$.[?($.store.bicycle.price < 20)]").ToList();
Assert.AreEqual(1, result.Count);
result = a.SelectTokens("$.[?($.store.bicycle.price < 10)]").ToList();
Assert.AreEqual(0, result.Count);
}
} }
} }
@@ -419,6 +419,16 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
Assert.AreEqual(QueryOperator.Exists, orExpression.Expressions[1].Operator); Assert.AreEqual(QueryOperator.Exists, orExpression.Expressions[1].Operator);
} }
[Test]
public void FilterWithRoot()
{
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]);
}
[Test] [Test]
public void BadOr1() public void BadOr1()
{ {
@@ -86,7 +86,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
{ "LastName", "LastName!" } { "LastName", "LastName!" }
}; };
Assert.IsTrue(compositeExpression.IsMatch(o1)); Assert.IsTrue(compositeExpression.IsMatch(o1, o1));
JObject o2 = new JObject JObject o2 = new JObject
{ {
@@ -94,14 +94,14 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
{ "FirstName", "FirstName!" } { "FirstName", "FirstName!" }
}; };
Assert.IsFalse(compositeExpression.IsMatch(o2)); Assert.IsFalse(compositeExpression.IsMatch(o2, o2));
JObject o3 = new JObject JObject o3 = new JObject
{ {
{ "Title", "Title!" } { "Title", "Title!" }
}; };
Assert.IsFalse(compositeExpression.IsMatch(o3)); Assert.IsFalse(compositeExpression.IsMatch(o3, o3));
} }
[Test] [Test]
@@ -144,7 +144,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
{ "LastName", "LastName!" } { "LastName", "LastName!" }
}; };
Assert.IsTrue(compositeExpression.IsMatch(o1)); Assert.IsTrue(compositeExpression.IsMatch(o1, o1));
JObject o2 = new JObject JObject o2 = new JObject
{ {
@@ -152,14 +152,14 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
{ "FirstName", "FirstName!" } { "FirstName", "FirstName!" }
}; };
Assert.IsTrue(compositeExpression.IsMatch(o2)); Assert.IsTrue(compositeExpression.IsMatch(o2, o2));
JObject o3 = new JObject JObject o3 = new JObject
{ {
{ "Title", "Title!" } { "Title", "Title!" }
}; };
Assert.IsFalse(compositeExpression.IsMatch(o3)); Assert.IsFalse(compositeExpression.IsMatch(o3, o3));
} }
[Test] [Test]
@@ -175,10 +175,10 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
} }
}; };
Assert.IsTrue(e1.IsMatch(new JArray(1, 2, 3, 4, 5))); Assert.IsTrue(e1.IsMatch(null, new JArray(1, 2, 3, 4, 5)));
Assert.IsTrue(e1.IsMatch(new JArray(2, 3, 4, 5))); Assert.IsTrue(e1.IsMatch(null, new JArray(2, 3, 4, 5)));
Assert.IsFalse(e1.IsMatch(new JArray(3, 4, 5))); Assert.IsFalse(e1.IsMatch(null, new JArray(3, 4, 5)));
Assert.IsFalse(e1.IsMatch(new JArray(4, 5))); Assert.IsFalse(e1.IsMatch(null, new JArray(4, 5)));
BooleanQueryExpression e2 = new BooleanQueryExpression BooleanQueryExpression e2 = new BooleanQueryExpression
{ {
@@ -190,10 +190,10 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
} }
}; };
Assert.IsTrue(e2.IsMatch(new JArray(1, 2, 3, 4, 5))); Assert.IsTrue(e2.IsMatch(null, new JArray(1, 2, 3, 4, 5)));
Assert.IsTrue(e2.IsMatch(new JArray(2, 3, 4, 5))); Assert.IsTrue(e2.IsMatch(null, new JArray(2, 3, 4, 5)));
Assert.IsTrue(e2.IsMatch(new JArray(3, 4, 5))); Assert.IsTrue(e2.IsMatch(null, new JArray(3, 4, 5)));
Assert.IsFalse(e2.IsMatch(new JArray(4, 5))); Assert.IsFalse(e2.IsMatch(null, new JArray(4, 5)));
} }
} }
} }
+2 -2
View File
@@ -2305,7 +2305,7 @@ namespace Newtonsoft.Json.Linq
JPath p = new JPath(path); JPath p = new JPath(path);
JToken token = null; JToken token = null;
foreach (JToken t in p.Evaluate(this, errorWhenNoMatch)) foreach (JToken t in p.Evaluate(this, this, errorWhenNoMatch))
{ {
if (token != null) if (token != null)
{ {
@@ -2341,7 +2341,7 @@ namespace Newtonsoft.Json.Linq
public IEnumerable<JToken> SelectTokens(string path, bool errorWhenNoMatch) public IEnumerable<JToken> SelectTokens(string path, bool errorWhenNoMatch)
{ {
JPath p = new JPath(path); JPath p = new JPath(path);
return p.Evaluate(this, errorWhenNoMatch); return p.Evaluate(this, this, errorWhenNoMatch);
} }
#if !(NET35 || NET20 || PORTABLE40) #if !(NET35 || NET20 || PORTABLE40)
@@ -8,7 +8,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{ {
public int? Index { get; set; } public int? Index { get; set; }
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch) public override IEnumerable<JToken> ExecuteFilter(JToken root, IEnumerable<JToken> current, bool errorWhenNoMatch)
{ {
foreach (JToken t in current) foreach (JToken t in current)
{ {
@@ -6,7 +6,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{ {
public List<int> Indexes { get; set; } public List<int> Indexes { get; set; }
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch) public override IEnumerable<JToken> ExecuteFilter(JToken root, IEnumerable<JToken> current, bool errorWhenNoMatch)
{ {
foreach (JToken t in current) foreach (JToken t in current)
{ {
@@ -11,7 +11,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
public int? End { get; set; } public int? End { get; set; }
public int? Step { get; set; } public int? Step { get; set; }
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch) public override IEnumerable<JToken> ExecuteFilter(JToken root, IEnumerable<JToken> current, bool errorWhenNoMatch)
{ {
if (Step == 0) if (Step == 0)
{ {
@@ -8,7 +8,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{ {
public string Name { get; set; } public string Name { get; set; }
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch) public override IEnumerable<JToken> ExecuteFilter(JToken root, IEnumerable<JToken> current, bool errorWhenNoMatch)
{ {
foreach (JToken t in current) foreach (JToken t in current)
{ {
@@ -13,7 +13,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{ {
public List<string> Names { get; set; } public List<string> Names { get; set; }
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch) public override IEnumerable<JToken> ExecuteFilter(JToken root, IEnumerable<JToken> current, bool errorWhenNoMatch)
{ {
foreach (JToken t in current) foreach (JToken t in current)
{ {
+11 -7
View File
@@ -429,15 +429,19 @@ namespace Newtonsoft.Json.Linq.JsonPath
{ {
EatWhitespace(); EatWhitespace();
if (_expression[_currentIndex] != '@') List<PathFilter> expressionPath = new List<PathFilter>();
if (_expression[_currentIndex] == '$')
{
expressionPath.Add(new RootFilter());
}
else if (_expression[_currentIndex] != '@')
{ {
throw new JsonException("Unexpected character while parsing path query: " + _expression[_currentIndex]); throw new JsonException("Unexpected character while parsing path query: " + _expression[_currentIndex]);
} }
_currentIndex++; _currentIndex++;
List<PathFilter> expressionPath = new List<PathFilter>();
if (ParsePath(expressionPath, _currentIndex, true)) if (ParsePath(expressionPath, _currentIndex, true))
{ {
throw new JsonException("Path ended with open query."); throw new JsonException("Path ended with open query.");
@@ -758,17 +762,17 @@ namespace Newtonsoft.Json.Linq.JsonPath
} }
} }
internal IEnumerable<JToken> Evaluate(JToken t, bool errorWhenNoMatch) internal IEnumerable<JToken> Evaluate(JToken root, JToken t, bool errorWhenNoMatch)
{ {
return Evaluate(Filters, t, errorWhenNoMatch); return Evaluate(Filters, root, t, errorWhenNoMatch);
} }
internal static IEnumerable<JToken> Evaluate(List<PathFilter> filters, JToken t, bool errorWhenNoMatch) internal static IEnumerable<JToken> Evaluate(List<PathFilter> filters, JToken root, JToken t, bool errorWhenNoMatch)
{ {
IEnumerable<JToken> current = new[] { t }; IEnumerable<JToken> current = new[] { t };
foreach (PathFilter filter in filters) foreach (PathFilter filter in filters)
{ {
current = filter.ExecuteFilter(current, errorWhenNoMatch); current = filter.ExecuteFilter(root, current, errorWhenNoMatch);
} }
return current; return current;
@@ -6,7 +6,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{ {
internal abstract class PathFilter internal abstract class PathFilter
{ {
public abstract IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch); public abstract IEnumerable<JToken> ExecuteFilter(JToken root, IEnumerable<JToken> current, bool errorWhenNoMatch);
protected static JToken GetTokenIndex(JToken t, bool errorWhenNoMatch, int index) protected static JToken GetTokenIndex(JToken t, bool errorWhenNoMatch, int index)
{ {
@@ -24,7 +24,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{ {
public QueryOperator Operator { get; set; } public QueryOperator Operator { get; set; }
public abstract bool IsMatch(JToken t); public abstract bool IsMatch(JToken root, JToken t);
} }
internal class CompositeExpression : QueryExpression internal class CompositeExpression : QueryExpression
@@ -36,14 +36,14 @@ namespace Newtonsoft.Json.Linq.JsonPath
Expressions = new List<QueryExpression>(); Expressions = new List<QueryExpression>();
} }
public override bool IsMatch(JToken t) public override bool IsMatch(JToken root, JToken t)
{ {
switch (Operator) switch (Operator)
{ {
case QueryOperator.And: case QueryOperator.And:
foreach (QueryExpression e in Expressions) foreach (QueryExpression e in Expressions)
{ {
if (!e.IsMatch(t)) if (!e.IsMatch(root, t))
{ {
return false; return false;
} }
@@ -52,7 +52,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
case QueryOperator.Or: case QueryOperator.Or:
foreach (QueryExpression e in Expressions) foreach (QueryExpression e in Expressions)
{ {
if (e.IsMatch(t)) if (e.IsMatch(root, t))
{ {
return true; return true;
} }
@@ -69,9 +69,9 @@ namespace Newtonsoft.Json.Linq.JsonPath
public List<PathFilter> Path { get; set; } public List<PathFilter> Path { get; set; }
public JValue Value { get; set; } public JValue Value { get; set; }
public override bool IsMatch(JToken t) public override bool IsMatch(JToken root, JToken t)
{ {
IEnumerable<JToken> pathResult = JPath.Evaluate(Path, t, false); IEnumerable<JToken> pathResult = JPath.Evaluate(Path, root, t, false);
foreach (JToken r in pathResult) foreach (JToken r in pathResult)
{ {
@@ -7,13 +7,13 @@ namespace Newtonsoft.Json.Linq.JsonPath
{ {
public QueryExpression Expression { get; set; } public QueryExpression Expression { get; set; }
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch) public override IEnumerable<JToken> ExecuteFilter(JToken root, IEnumerable<JToken> current, bool errorWhenNoMatch)
{ {
foreach (JToken t in current) foreach (JToken t in current)
{ {
foreach (JToken v in t) foreach (JToken v in t)
{ {
if (Expression.IsMatch(v)) if (Expression.IsMatch(root, v))
{ {
yield return v; yield return v;
} }
@@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace Newtonsoft.Json.Linq.JsonPath
{
internal class RootFilter : PathFilter
{
public override IEnumerable<JToken> ExecuteFilter(JToken root, IEnumerable<JToken> current, bool errorWhenNoMatch)
{
return root;
}
}
}
@@ -6,17 +6,17 @@ namespace Newtonsoft.Json.Linq.JsonPath
{ {
public string Name { get; set; } public string Name { get; set; }
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch) public override IEnumerable<JToken> ExecuteFilter(JToken root, IEnumerable<JToken> current, bool errorWhenNoMatch)
{ {
foreach (JToken root in current) foreach (JToken c in current)
{ {
if (Name == null) if (Name == null)
{ {
yield return root; yield return c;
} }
JToken value = root; JToken value = c;
JToken container = root; JToken container = c;
while (true) while (true)
{ {
@@ -26,12 +26,12 @@ namespace Newtonsoft.Json.Linq.JsonPath
} }
else else
{ {
while (value != null && value != root && value == value.Parent.Last) while (value != null && value != c && value == value.Parent.Last)
{ {
value = value.Parent; value = value.Parent;
} }
if (value == null || value == root) if (value == null || value == c)
{ {
break; break;
} }
@@ -123,6 +123,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" /> <Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" /> <Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" /> <Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" /> <Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\MergeArrayHandling.cs" /> <Compile Include="Linq\MergeArrayHandling.cs" />
<Compile Include="Linq\MergeNullValueHandling.cs" /> <Compile Include="Linq\MergeNullValueHandling.cs" />
@@ -137,6 +137,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" /> <Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" /> <Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" /> <Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" /> <Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\MergeArrayHandling.cs" /> <Compile Include="Linq\MergeArrayHandling.cs" />
<Compile Include="Linq\MergeNullValueHandling.cs" /> <Compile Include="Linq\MergeNullValueHandling.cs" />
@@ -137,6 +137,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" /> <Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" /> <Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" /> <Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" /> <Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\MergeArrayHandling.cs" /> <Compile Include="Linq\MergeArrayHandling.cs" />
<Compile Include="Linq\MergeNullValueHandling.cs" /> <Compile Include="Linq\MergeNullValueHandling.cs" />
@@ -128,6 +128,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" /> <Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" /> <Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" /> <Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" /> <Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\JToken.cs" /> <Compile Include="Linq\JToken.cs" />
<Compile Include="Linq\JTokenEqualityComparer.cs" /> <Compile Include="Linq\JTokenEqualityComparer.cs" />
@@ -133,6 +133,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" /> <Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" /> <Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" /> <Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" /> <Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\JToken.cs" /> <Compile Include="Linq\JToken.cs" />
<Compile Include="Linq\JTokenEqualityComparer.cs" /> <Compile Include="Linq\JTokenEqualityComparer.cs" />
@@ -131,6 +131,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" /> <Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" /> <Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" /> <Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" /> <Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\JToken.cs" /> <Compile Include="Linq\JToken.cs" />
<Compile Include="Linq\JTokenEqualityComparer.cs" /> <Compile Include="Linq\JTokenEqualityComparer.cs" />