-JsonPath++
This commit is contained in:
@@ -25,7 +25,9 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Newtonsoft.Json.Linq.JsonPath;
|
||||
using Newtonsoft.Json.Tests.Bson;
|
||||
#if !NETFX_CORE
|
||||
using NUnit.Framework;
|
||||
#else
|
||||
@@ -404,6 +406,19 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
Assert.AreEqual(3, (int)t[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultipleQueries()
|
||||
{
|
||||
JArray a = new JArray(1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||
|
||||
// json path does item based evaluation - http://www.sitepen.com/blog/2008/03/17/jsonpath-support/
|
||||
// first query resolves array to ints
|
||||
// int has no children to query
|
||||
IList<JToken> t = a.SelectTokens("[?(@ <> 1)][?(@ <> 4)][?(@ < 7)]").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(0, t.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GreaterQuery()
|
||||
{
|
||||
@@ -419,20 +434,37 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("hi", 3)), t[1]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GreaterQueryBigInteger()
|
||||
{
|
||||
JArray a = new JArray(
|
||||
new JObject(new JProperty("hi", new BigInteger(1))),
|
||||
new JObject(new JProperty("hi", new BigInteger(2))),
|
||||
new JObject(new JProperty("hi", new BigInteger(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", 2.0)),
|
||||
new JObject(new JProperty("hi", 3)));
|
||||
|
||||
IList<JToken> t = a.SelectTokens("[ ?( @.hi >= 1 ) ]").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(3, t.Count);
|
||||
Assert.AreEqual(4, 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]));
|
||||
Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("hi", 2.0)), t[2]));
|
||||
Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("hi", 3)), t[3]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -109,6 +109,14 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
() => { new JPath("$ .Blah"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoFieldNameAfterDot()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
@"Unexpected end while parsing path.",
|
||||
() => { new JPath("$.Blah."); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RootWithBadWhitespace2()
|
||||
{
|
||||
@@ -215,11 +223,10 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
JPath path = new JPath("Blah[ ?( @..name ) ]");
|
||||
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.Exists, expressions[0].Operator);
|
||||
Assert.AreEqual(1, expressions[0].Path.Count);
|
||||
Assert.AreEqual("name", ((ScanFilter)expressions[0].Path[0]).Name);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(QueryOperator.Exists, expressions.Operator);
|
||||
Assert.AreEqual(1, expressions.Path.Count);
|
||||
Assert.AreEqual("name", ((ScanFilter)expressions.Path[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -228,10 +235,9 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions.Operator);
|
||||
Assert.AreEqual("hi", (string)expressions.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -240,10 +246,9 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions.Operator);
|
||||
Assert.AreEqual("h'i", (string)expressions.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -252,10 +257,9 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions.Operator);
|
||||
Assert.AreEqual("h\\i", (string)expressions.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -272,10 +276,9 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions.Operator);
|
||||
Assert.AreEqual(false, (bool)expressions.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -284,10 +287,9 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions.Operator);
|
||||
Assert.AreEqual(true, (bool)expressions.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -296,98 +298,184 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(QueryOperator.Equals, expressions.Operator);
|
||||
Assert.AreEqual(null, expressions.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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual("name", ((ScanFilter)expressions.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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.NotEquals, expressions.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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.NotEquals, expressions.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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.LessThan, expressions.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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.LessThanOrEquals, expressions.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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.GreaterThan, expressions.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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.GreaterThanOrEquals, expressions.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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(12, (int)expressions.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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(-12, (int)expressions.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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(12.1d, (double)expressions.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterExistWithAnd()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name&&@.title)]");
|
||||
CompositeExpression expressions = (CompositeExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.And, expressions.Operator);
|
||||
Assert.AreEqual(2, expressions.Expressions.Count);
|
||||
Assert.AreEqual("name", ((FieldFilter)((BooleanQueryExpression)expressions.Expressions[0]).Path[0]).Name);
|
||||
Assert.AreEqual(QueryOperator.Exists, expressions.Expressions[0].Operator);
|
||||
Assert.AreEqual("title", ((FieldFilter)((BooleanQueryExpression)expressions.Expressions[1]).Path[0]).Name);
|
||||
Assert.AreEqual(QueryOperator.Exists, expressions.Expressions[1].Operator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FilterExistWithAndOr()
|
||||
{
|
||||
JPath path = new JPath("[?(@.name&&@.title||@.pie)]");
|
||||
CompositeExpression andExpression = (CompositeExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(QueryOperator.And, andExpression.Operator);
|
||||
Assert.AreEqual(2, andExpression.Expressions.Count);
|
||||
Assert.AreEqual("name", ((FieldFilter)((BooleanQueryExpression)andExpression.Expressions[0]).Path[0]).Name);
|
||||
Assert.AreEqual(QueryOperator.Exists, andExpression.Expressions[0].Operator);
|
||||
|
||||
CompositeExpression orExpression = (CompositeExpression)andExpression.Expressions[1];
|
||||
Assert.AreEqual(2, orExpression.Expressions.Count);
|
||||
Assert.AreEqual("title", ((FieldFilter)((BooleanQueryExpression)orExpression.Expressions[0]).Path[0]).Name);
|
||||
Assert.AreEqual(QueryOperator.Exists, orExpression.Expressions[0].Operator);
|
||||
Assert.AreEqual("pie", ((FieldFilter)((BooleanQueryExpression)orExpression.Expressions[1]).Path[0]).Name);
|
||||
Assert.AreEqual(QueryOperator.Exists, orExpression.Expressions[1].Operator);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BadOr1()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>("Unexpected character while parsing path query: )",
|
||||
() => new JPath("[?(@.name||)]"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BaddOr2()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>("Unexpected character while parsing path query: |",
|
||||
() => new JPath("[?(@.name|)]"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BaddOr3()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>("Unexpected character while parsing path query: |",
|
||||
() => new JPath("[?(@.name|"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BaddOr4()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>("Path ended with open query.",
|
||||
() => new JPath("[?(@.name||"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoAtAfterOr()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>("Unexpected character while parsing path query: s",
|
||||
() => new JPath("[?(@.name||s"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoPathAfterAt()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(@"Path ended with open query.",
|
||||
() => new JPath("[?(@.name||@"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoPathAfterDot()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(@"Unexpected end while parsing path.",
|
||||
() => new JPath("[?(@.name||@."));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoPathAfterDot2()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(@"Unexpected end while parsing path.",
|
||||
() => new JPath("[?(@.name||@.)]"));
|
||||
}
|
||||
|
||||
[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);
|
||||
BooleanQueryExpression expressions = (BooleanQueryExpression)((QueryFilter)path.Filters[0]).Expression;
|
||||
Assert.AreEqual(5.56789e+0, (double)expressions.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Linq.JsonPath;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
{
|
||||
[TestFixture]
|
||||
public class QueryExpressionTests : TestFixtureBase
|
||||
{
|
||||
[Test]
|
||||
public void AndExpressionTest()
|
||||
{
|
||||
CompositeExpression compositeExpression = new CompositeExpression
|
||||
{
|
||||
Operator = QueryOperator.And,
|
||||
Expressions = new List<QueryExpression>
|
||||
{
|
||||
new BooleanQueryExpression
|
||||
{
|
||||
Operator = QueryOperator.Exists,
|
||||
Path = new List<PathFilter>
|
||||
{
|
||||
new FieldFilter
|
||||
{
|
||||
Name = "FirstName"
|
||||
}
|
||||
}
|
||||
},
|
||||
new BooleanQueryExpression
|
||||
{
|
||||
Operator = QueryOperator.Exists,
|
||||
Path = new List<PathFilter>
|
||||
{
|
||||
new FieldFilter
|
||||
{
|
||||
Name = "LastName"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JObject o1 = new JObject
|
||||
{
|
||||
{"Title","Title!"},
|
||||
{"FirstName", "FirstName!"},
|
||||
{"LastName", "LastName!"}
|
||||
};
|
||||
|
||||
Assert.IsTrue(compositeExpression.IsMatch(o1));
|
||||
|
||||
JObject o2 = new JObject
|
||||
{
|
||||
{"Title","Title!"},
|
||||
{"FirstName", "FirstName!"}
|
||||
};
|
||||
|
||||
Assert.IsFalse(compositeExpression.IsMatch(o2));
|
||||
|
||||
JObject o3 = new JObject
|
||||
{
|
||||
{"Title","Title!"}
|
||||
};
|
||||
|
||||
Assert.IsFalse(compositeExpression.IsMatch(o3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OrExpressionTest()
|
||||
{
|
||||
CompositeExpression compositeExpression = new CompositeExpression
|
||||
{
|
||||
Operator = QueryOperator.Or,
|
||||
Expressions = new List<QueryExpression>
|
||||
{
|
||||
new BooleanQueryExpression
|
||||
{
|
||||
Operator = QueryOperator.Exists,
|
||||
Path = new List<PathFilter>
|
||||
{
|
||||
new FieldFilter
|
||||
{
|
||||
Name = "FirstName"
|
||||
}
|
||||
}
|
||||
},
|
||||
new BooleanQueryExpression
|
||||
{
|
||||
Operator = QueryOperator.Exists,
|
||||
Path = new List<PathFilter>
|
||||
{
|
||||
new FieldFilter
|
||||
{
|
||||
Name = "LastName"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JObject o1 = new JObject
|
||||
{
|
||||
{"Title","Title!"},
|
||||
{"FirstName", "FirstName!"},
|
||||
{"LastName", "LastName!"}
|
||||
};
|
||||
|
||||
Assert.IsTrue(compositeExpression.IsMatch(o1));
|
||||
|
||||
JObject o2 = new JObject
|
||||
{
|
||||
{"Title","Title!"},
|
||||
{"FirstName", "FirstName!"}
|
||||
};
|
||||
|
||||
Assert.IsTrue(compositeExpression.IsMatch(o2));
|
||||
|
||||
JObject o3 = new JObject
|
||||
{
|
||||
{"Title","Title!"}
|
||||
};
|
||||
|
||||
Assert.IsFalse(compositeExpression.IsMatch(o3));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,6 +211,7 @@
|
||||
<Compile Include="Linq\JsonPath\JPathParseTests.cs" />
|
||||
<Compile Include="Linq\JPropertyTests.cs" />
|
||||
<Compile Include="Linq\JRawTests.cs" />
|
||||
<Compile Include="Linq\JsonPath\QueryExpressionTests.cs" />
|
||||
<Compile Include="Linq\JTokenEqualityComparerTests.cs" />
|
||||
<Compile Include="Linq\JTokenReaderTest.cs" />
|
||||
<Compile Include="Linq\JTokenTests.cs" />
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
internal class AndFilter : PathFilter
|
||||
{
|
||||
public List<PathFilter> Filters { get; set; }
|
||||
|
||||
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch)
|
||||
{
|
||||
foreach (PathFilter f in Filters)
|
||||
{
|
||||
current = f.ExecuteFilter(current, errorWhenNoMatch).ToList();
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
bool scan = false;
|
||||
bool followingIndexer = false;
|
||||
bool followingDot = false;
|
||||
|
||||
bool ended = false;
|
||||
while (_currentIndex < _expression.Length && !ended)
|
||||
@@ -107,6 +108,7 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
_currentIndex++;
|
||||
currentPartStartIndex = _currentIndex;
|
||||
followingIndexer = true;
|
||||
followingDot = false;
|
||||
break;
|
||||
case ']':
|
||||
case ')':
|
||||
@@ -135,9 +137,10 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
_currentIndex++;
|
||||
currentPartStartIndex = _currentIndex;
|
||||
followingIndexer = false;
|
||||
followingDot = true;
|
||||
break;
|
||||
default:
|
||||
if (query && (currentChar == '=' || currentChar == '<' || currentChar == '!' || currentChar == '>'))
|
||||
if (query && (currentChar == '=' || currentChar == '<' || currentChar == '!' || currentChar == '>' || currentChar == '|' || currentChar == '&'))
|
||||
{
|
||||
ended = true;
|
||||
}
|
||||
@@ -152,6 +155,8 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
}
|
||||
}
|
||||
|
||||
bool atPathEnd = (_currentIndex == _expression.Length);
|
||||
|
||||
if (_currentIndex > currentPartStartIndex)
|
||||
{
|
||||
string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex).TrimEnd();
|
||||
@@ -160,8 +165,14 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
filters.Add(filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no field name following dot in path and at end of base path/query
|
||||
if (followingDot && (atPathEnd || query))
|
||||
throw new JsonException("Unexpected end while parsing path.");
|
||||
}
|
||||
|
||||
return (_currentIndex == _expression.Length);
|
||||
return atPathEnd;
|
||||
}
|
||||
|
||||
private PathFilter ParseIndexer(char indexerOpenChar)
|
||||
@@ -344,75 +355,118 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
|
||||
_currentIndex++;
|
||||
|
||||
List<QueryExpression> expressions = new List<QueryExpression>();
|
||||
QueryExpression expression = ParseExpression();
|
||||
|
||||
while (true)
|
||||
_currentIndex++;
|
||||
EnsureLength("Path ended with open indexer.");
|
||||
EatWhitespace();
|
||||
|
||||
if (_expression[_currentIndex] != indexerCloseChar)
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]);
|
||||
|
||||
return new QueryFilter
|
||||
{
|
||||
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]);
|
||||
|
||||
return new QueryFilter
|
||||
{
|
||||
Expression = expressions
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentIndex++;
|
||||
}
|
||||
}
|
||||
Expression = expression
|
||||
};
|
||||
}
|
||||
|
||||
private QueryExpression ParseExpression()
|
||||
{
|
||||
EatWhitespace();
|
||||
QueryExpression rootExpression = null;
|
||||
CompositeExpression parentExpression = null;
|
||||
|
||||
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] == ')')
|
||||
while (_currentIndex < _expression.Length)
|
||||
{
|
||||
op = QueryOperator.Exists;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = ParseOperator();
|
||||
EatWhitespace();
|
||||
|
||||
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.");
|
||||
|
||||
value = ParseValue();
|
||||
QueryOperator op;
|
||||
object value = null;
|
||||
if (_expression[_currentIndex] == ')'
|
||||
|| _expression[_currentIndex] == '|'
|
||||
|| _expression[_currentIndex] == '&')
|
||||
{
|
||||
op = QueryOperator.Exists;
|
||||
}
|
||||
else
|
||||
{
|
||||
op = ParseOperator();
|
||||
|
||||
EatWhitespace();
|
||||
EnsureLength("Path ended with open query.");
|
||||
EatWhitespace();
|
||||
EnsureLength("Path ended with open query.");
|
||||
|
||||
value = ParseValue();
|
||||
|
||||
EatWhitespace();
|
||||
EnsureLength("Path ended with open query.");
|
||||
}
|
||||
|
||||
BooleanQueryExpression booleanExpression = new BooleanQueryExpression
|
||||
{
|
||||
Path = expressionPath,
|
||||
Operator = op,
|
||||
Value = (op != QueryOperator.Exists) ? new JValue(value) : null
|
||||
};
|
||||
|
||||
if (_expression[_currentIndex] == ')')
|
||||
{
|
||||
if (parentExpression != null)
|
||||
{
|
||||
parentExpression.Expressions.Add(booleanExpression);
|
||||
return rootExpression;
|
||||
}
|
||||
|
||||
return booleanExpression;
|
||||
}
|
||||
if (_expression[_currentIndex] == '&' && Match("&&"))
|
||||
{
|
||||
if (parentExpression == null || parentExpression.Operator != QueryOperator.And)
|
||||
{
|
||||
CompositeExpression andExpression = new CompositeExpression { Operator = QueryOperator.And };
|
||||
|
||||
if (parentExpression != null)
|
||||
parentExpression.Expressions.Add(andExpression);
|
||||
|
||||
parentExpression = andExpression;
|
||||
|
||||
if (rootExpression == null)
|
||||
rootExpression = parentExpression;
|
||||
}
|
||||
|
||||
parentExpression.Expressions.Add(booleanExpression);
|
||||
}
|
||||
if (_expression[_currentIndex] == '|' && Match("||"))
|
||||
{
|
||||
if (parentExpression == null || parentExpression.Operator != QueryOperator.Or)
|
||||
{
|
||||
CompositeExpression orExpression = new CompositeExpression { Operator = QueryOperator.Or };
|
||||
|
||||
if (parentExpression != null)
|
||||
parentExpression.Expressions.Add(orExpression);
|
||||
|
||||
parentExpression = orExpression;
|
||||
|
||||
if (rootExpression == null)
|
||||
rootExpression = parentExpression;
|
||||
}
|
||||
|
||||
parentExpression.Expressions.Add(booleanExpression);
|
||||
}
|
||||
}
|
||||
|
||||
return new QueryExpression
|
||||
{
|
||||
Path = expressionPath,
|
||||
Operator = op,
|
||||
Value = (op != QueryOperator.Exists) ? new JValue(value) : null
|
||||
};
|
||||
throw new JsonException("Path ended with open query.");
|
||||
}
|
||||
|
||||
private object ParseValue()
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
internal class OrFilter : PathFilter
|
||||
{
|
||||
public List<PathFilter> Filters { get; set; }
|
||||
|
||||
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch)
|
||||
{
|
||||
List<JToken> cachedCurrent = current.ToList();
|
||||
List<JToken> remaining = cachedCurrent.ToList();
|
||||
List<JToken> matches = new List<JToken>();
|
||||
int previousResultCount = 0;
|
||||
|
||||
foreach (PathFilter f in Filters)
|
||||
{
|
||||
matches.AddRange(f.ExecuteFilter(remaining, errorWhenNoMatch));
|
||||
|
||||
for (int i = previousResultCount; i < matches.Count; i++)
|
||||
{
|
||||
remaining.Remove(matches[i]);
|
||||
}
|
||||
previousResultCount = matches.Count;
|
||||
}
|
||||
|
||||
return cachedCurrent.Where(matches.Contains);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,19 +13,103 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
LessThan,
|
||||
LessThanOrEquals,
|
||||
GreaterThan,
|
||||
GreaterThanOrEquals
|
||||
GreaterThanOrEquals,
|
||||
And,
|
||||
Or
|
||||
}
|
||||
|
||||
internal class QueryExpression
|
||||
internal abstract class QueryExpression
|
||||
{
|
||||
public QueryOperator Operator { get; set; }
|
||||
|
||||
public abstract bool IsMatch(JToken t);
|
||||
}
|
||||
|
||||
internal class CompositeExpression : QueryExpression
|
||||
{
|
||||
public List<QueryExpression> Expressions { get; set; }
|
||||
|
||||
public CompositeExpression()
|
||||
{
|
||||
Expressions = new List<QueryExpression>();
|
||||
}
|
||||
|
||||
public override bool IsMatch(JToken t)
|
||||
{
|
||||
switch (Operator)
|
||||
{
|
||||
case QueryOperator.And:
|
||||
foreach (QueryExpression e in Expressions)
|
||||
{
|
||||
if (!e.IsMatch(t))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case QueryOperator.Or:
|
||||
foreach (QueryExpression e in Expressions)
|
||||
{
|
||||
if (e.IsMatch(t))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class BooleanQueryExpression : QueryExpression
|
||||
{
|
||||
public List<PathFilter> Path { get; set; }
|
||||
public QueryOperator Operator { get; set; }
|
||||
public JValue Value { get; set; }
|
||||
|
||||
public override bool IsMatch(JToken t)
|
||||
{
|
||||
IEnumerable<JToken> pathResult = JPath.Evaluate(Path, t, false);
|
||||
|
||||
foreach (JToken r in pathResult)
|
||||
{
|
||||
JValue v = r as JValue;
|
||||
switch (Operator)
|
||||
{
|
||||
case QueryOperator.Equals:
|
||||
if (v != null && v.Equals(Value))
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.NotEquals:
|
||||
if (v != null && !v.Equals(Value))
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.GreaterThan:
|
||||
if (v != null && v.CompareTo(Value) > 0)
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.GreaterThanOrEquals:
|
||||
if (v != null && v.CompareTo(Value) >= 0)
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.LessThan:
|
||||
if (v != null && v.CompareTo(Value) < 0)
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.LessThanOrEquals:
|
||||
if (v != null && v.CompareTo(Value) <= 0)
|
||||
return true;
|
||||
break;
|
||||
case QueryOperator.Exists:
|
||||
return true;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal class QueryFilter : PathFilter
|
||||
{
|
||||
public List<QueryExpression> Expression { get; set; }
|
||||
public QueryExpression Expression { get; set; }
|
||||
|
||||
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch)
|
||||
{
|
||||
@@ -33,56 +117,10 @@ namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
foreach (JToken v in t)
|
||||
{
|
||||
if (Evaluate(errorWhenNoMatch, v))
|
||||
if (Expression.IsMatch(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,6 +111,7 @@
|
||||
<Compile Include="Linq\JContainer.cs" />
|
||||
<Compile Include="Linq\JEnumerable.cs" />
|
||||
<Compile Include="Linq\JObject.cs" />
|
||||
<Compile Include="Linq\JsonPath\AndFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\ArrayIndexFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\ArrayMultipleIndexFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\ArraySliceFilter.cs" />
|
||||
@@ -120,6 +121,7 @@
|
||||
<Compile Include="Linq\JPropertyDescriptor.cs" />
|
||||
<Compile Include="Linq\JPropertyKeyedCollection.cs" />
|
||||
<Compile Include="Linq\JRaw.cs" />
|
||||
<Compile Include="Linq\JsonPath\OrFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\PathFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\QueryFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\ScanFilter.cs" />
|
||||
|
||||
Reference in New Issue
Block a user