-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();
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);
}
[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]
public void BadOr1()
{
@@ -86,7 +86,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
{ "LastName", "LastName!" }
};
Assert.IsTrue(compositeExpression.IsMatch(o1));
Assert.IsTrue(compositeExpression.IsMatch(o1, o1));
JObject o2 = new JObject
{
@@ -94,14 +94,14 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
{ "FirstName", "FirstName!" }
};
Assert.IsFalse(compositeExpression.IsMatch(o2));
Assert.IsFalse(compositeExpression.IsMatch(o2, o2));
JObject o3 = new JObject
{
{ "Title", "Title!" }
};
Assert.IsFalse(compositeExpression.IsMatch(o3));
Assert.IsFalse(compositeExpression.IsMatch(o3, o3));
}
[Test]
@@ -144,7 +144,7 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
{ "LastName", "LastName!" }
};
Assert.IsTrue(compositeExpression.IsMatch(o1));
Assert.IsTrue(compositeExpression.IsMatch(o1, o1));
JObject o2 = new JObject
{
@@ -152,14 +152,14 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
{ "FirstName", "FirstName!" }
};
Assert.IsTrue(compositeExpression.IsMatch(o2));
Assert.IsTrue(compositeExpression.IsMatch(o2, o2));
JObject o3 = new JObject
{
{ "Title", "Title!" }
};
Assert.IsFalse(compositeExpression.IsMatch(o3));
Assert.IsFalse(compositeExpression.IsMatch(o3, o3));
}
[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(new JArray(2, 3, 4, 5)));
Assert.IsFalse(e1.IsMatch(new JArray(3, 4, 5)));
Assert.IsFalse(e1.IsMatch(new JArray(4, 5)));
Assert.IsTrue(e1.IsMatch(null, new JArray(1, 2, 3, 4, 5)));
Assert.IsTrue(e1.IsMatch(null, new JArray(2, 3, 4, 5)));
Assert.IsFalse(e1.IsMatch(null, new JArray(3, 4, 5)));
Assert.IsFalse(e1.IsMatch(null, new JArray(4, 5)));
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(new JArray(2, 3, 4, 5)));
Assert.IsTrue(e2.IsMatch(new JArray(3, 4, 5)));
Assert.IsFalse(e2.IsMatch(new JArray(4, 5)));
Assert.IsTrue(e2.IsMatch(null, new JArray(1, 2, 3, 4, 5)));
Assert.IsTrue(e2.IsMatch(null, new JArray(2, 3, 4, 5)));
Assert.IsTrue(e2.IsMatch(null, new JArray(3, 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);
JToken token = null;
foreach (JToken t in p.Evaluate(this, errorWhenNoMatch))
foreach (JToken t in p.Evaluate(this, this, errorWhenNoMatch))
{
if (token != null)
{
@@ -2341,7 +2341,7 @@ namespace Newtonsoft.Json.Linq
public IEnumerable<JToken> SelectTokens(string path, bool errorWhenNoMatch)
{
JPath p = new JPath(path);
return p.Evaluate(this, errorWhenNoMatch);
return p.Evaluate(this, this, errorWhenNoMatch);
}
#if !(NET35 || NET20 || PORTABLE40)
@@ -8,7 +8,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{
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)
{
@@ -6,7 +6,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{
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)
{
@@ -11,7 +11,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
public int? End { 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)
{
@@ -8,7 +8,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{
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)
{
@@ -13,7 +13,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{
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)
{
+11 -7
View File
@@ -429,15 +429,19 @@ namespace Newtonsoft.Json.Linq.JsonPath
{
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]);
}
_currentIndex++;
List<PathFilter> expressionPath = new List<PathFilter>();
if (ParsePath(expressionPath, _currentIndex, true))
{
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 };
foreach (PathFilter filter in filters)
{
current = filter.ExecuteFilter(current, errorWhenNoMatch);
current = filter.ExecuteFilter(root, current, errorWhenNoMatch);
}
return current;
@@ -6,7 +6,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{
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)
{
@@ -24,7 +24,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
{
public QueryOperator Operator { get; set; }
public abstract bool IsMatch(JToken t);
public abstract bool IsMatch(JToken root, JToken t);
}
internal class CompositeExpression : QueryExpression
@@ -36,14 +36,14 @@ namespace Newtonsoft.Json.Linq.JsonPath
Expressions = new List<QueryExpression>();
}
public override bool IsMatch(JToken t)
public override bool IsMatch(JToken root, JToken t)
{
switch (Operator)
{
case QueryOperator.And:
foreach (QueryExpression e in Expressions)
{
if (!e.IsMatch(t))
if (!e.IsMatch(root, t))
{
return false;
}
@@ -52,7 +52,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
case QueryOperator.Or:
foreach (QueryExpression e in Expressions)
{
if (e.IsMatch(t))
if (e.IsMatch(root, t))
{
return true;
}
@@ -69,9 +69,9 @@ namespace Newtonsoft.Json.Linq.JsonPath
public List<PathFilter> Path { 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)
{
@@ -7,13 +7,13 @@ namespace Newtonsoft.Json.Linq.JsonPath
{
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 v in t)
{
if (Expression.IsMatch(v))
if (Expression.IsMatch(root, 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 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)
{
yield return root;
yield return c;
}
JToken value = root;
JToken container = root;
JToken value = c;
JToken container = c;
while (true)
{
@@ -26,12 +26,12 @@ namespace Newtonsoft.Json.Linq.JsonPath
}
else
{
while (value != null && value != root && value == value.Parent.Last)
while (value != null && value != c && value == value.Parent.Last)
{
value = value.Parent;
}
if (value == null || value == root)
if (value == null || value == c)
{
break;
}
@@ -123,6 +123,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\MergeArrayHandling.cs" />
<Compile Include="Linq\MergeNullValueHandling.cs" />
@@ -137,6 +137,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\MergeArrayHandling.cs" />
<Compile Include="Linq\MergeNullValueHandling.cs" />
@@ -137,6 +137,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\MergeArrayHandling.cs" />
<Compile Include="Linq\MergeNullValueHandling.cs" />
@@ -128,6 +128,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\JToken.cs" />
<Compile Include="Linq\JTokenEqualityComparer.cs" />
@@ -133,6 +133,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\JToken.cs" />
<Compile Include="Linq\JTokenEqualityComparer.cs" />
@@ -131,6 +131,7 @@
<Compile Include="Linq\JsonPath\PathFilter.cs" />
<Compile Include="Linq\JsonPath\QueryExpression.cs" />
<Compile Include="Linq\JsonPath\QueryFilter.cs" />
<Compile Include="Linq\JsonPath\RootFilter.cs" />
<Compile Include="Linq\JsonPath\ScanFilter.cs" />
<Compile Include="Linq\JToken.cs" />
<Compile Include="Linq\JTokenEqualityComparer.cs" />