diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..2fb6147
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "Json-Path-Test-Suite"]
+ path = Json-Path-Test-Suite
+ url = https://github.com/gregsdennis/JSON-Path-Test-Suite.git
+[submodule "Json-Schema-Test-Suite"]
+ path = Json-Schema-Test-Suite
+ url = https://github.com/json-schema-org/JSON-Schema-Test-Suite.git
diff --git a/JSON-Schema-Test-Suite/README.md b/JSON-Schema-Test-Suite/README.md
index 2958ee6..5adf569 100644
--- a/JSON-Schema-Test-Suite/README.md
+++ b/JSON-Schema-Test-Suite/README.md
@@ -118,6 +118,7 @@ for more information.
### .NET ###
* [Newtonsoft.Json.Schema](https://github.com/JamesNK/Newtonsoft.Json.Schema)
+* [Manatee.Json](https://github.com/gregsdennis/Manatee.Json)
### PHP ###
diff --git a/JSON-Schema-Test-Suite/tests/draft4/ref.json b/JSON-Schema-Test-Suite/tests/draft4/ref.json
index 7e80552..fe5aa90 100644
--- a/JSON-Schema-Test-Suite/tests/draft4/ref.json
+++ b/JSON-Schema-Test-Suite/tests/draft4/ref.json
@@ -155,5 +155,25 @@
"valid": false
}
]
+ },
+ {
+ "description": "property named $ref that is not a reference",
+ "schema": {
+ "properties": {
+ "$ref": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
}
]
diff --git a/Json-Path-Test-Suite b/Json-Path-Test-Suite
new file mode 160000
index 0000000..c5afa49
--- /dev/null
+++ b/Json-Path-Test-Suite
@@ -0,0 +1 @@
+Subproject commit c5afa49488b5a659ab7f5569745d9840ee4a12e1
diff --git a/Json-Schema-Test-Suite/.gitignore b/Json-Schema-Test-Suite/.gitignore
new file mode 100644
index 0000000..1333ed7
--- /dev/null
+++ b/Json-Schema-Test-Suite/.gitignore
@@ -0,0 +1 @@
+TODO
diff --git a/Manatee.Json.Tests/DevTest.cs b/Manatee.Json.Tests/DevTest.cs
index 74781e5..2b81b5f 100644
--- a/Manatee.Json.Tests/DevTest.cs
+++ b/Manatee.Json.Tests/DevTest.cs
@@ -28,7 +28,7 @@
namespace Manatee.Json.Tests
{
[TestClass]
- [Ignore]
+ [Ignore]
public class DevTest
{
[TestMethod]
diff --git a/Manatee.Json.Tests/Manatee.Json.Tests.csproj b/Manatee.Json.Tests/Manatee.Json.Tests.csproj
index e19db5f..74ad343 100644
--- a/Manatee.Json.Tests/Manatee.Json.Tests.csproj
+++ b/Manatee.Json.Tests/Manatee.Json.Tests.csproj
@@ -139,10 +139,6 @@
Manatee.Json.snk
-
- ..\packages\Manatee.StateMachine.1.1.2\lib\net40\Manatee.StateMachine.dll
- True
-
False
@@ -174,6 +170,14 @@
+
+
+
+
+
+
+
+
@@ -223,7 +227,6 @@
-
Always
diff --git a/Manatee.Json.Tests/Path/EvaluationTest.cs b/Manatee.Json.Tests/Path/EvaluationTest.cs
new file mode 100644
index 0000000..586481a
--- /dev/null
+++ b/Manatee.Json.Tests/Path/EvaluationTest.cs
@@ -0,0 +1,37 @@
+using Manatee.Json.Path;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Manatee.Json.Tests.Path
+{
+ [TestClass]
+ public class EvaluationTest
+ {
+ [TestMethod]
+ public void ArrayLengthExpression_LastItem()
+ {
+ var json = new JsonArray {1, 2, 3};
+ var path = JsonPathWith.Array(jv => jv.Length() - 1);
+ var expected = new JsonArray {3};
+
+ var actual = path.Evaluate(json);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ public void ObjectNameLengthExpression_LastItem()
+ {
+ var json = new JsonObject
+ {
+ {"name", new JsonArray {1, 2, 3}},
+ {"name2", new JsonArray {1, 2, 3}},
+ {"test", new JsonArray {1, 2}},
+ };
+ var path = JsonPathWith.Array(jv => JsonPathRoot.Name("test").Length());
+ var expected = new JsonArray {new JsonArray {1, 2}};
+
+ var actual = path.Evaluate(json);
+
+ Assert.AreEqual(expected, actual);
+ }
+ }
+}
diff --git a/Manatee.Json.Tests/Path/FilterExpressionTest.cs b/Manatee.Json.Tests/Path/FilterExpressionTest.cs
new file mode 100644
index 0000000..a9fb9c1
--- /dev/null
+++ b/Manatee.Json.Tests/Path/FilterExpressionTest.cs
@@ -0,0 +1,217 @@
+using Manatee.Json.Path;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Manatee.Json.Tests.Path
+{
+ [TestClass]
+ public class FilterExpressionTest
+ {
+ private void Run(JsonPath expected, string text)
+ {
+ var actual = JsonPath.Parse(text);
+ Assert.AreEqual(expected, actual);
+ }
+
+ private void CompareEval(JsonPath expected, string text)
+ {
+ var data = new JsonArray
+ {
+ 1,
+ new JsonObject
+ {
+ ["bool"] = false,
+ ["int"] = 20,
+ ["string"] = "value",
+ },
+ "hello",
+ true,
+ 5,
+ new JsonArray {1,2,3 }
+ };
+
+ var actual = JsonPath.Parse(text);
+
+ var expectedResult = expected.Evaluate(data);
+ var actualResult = actual.Evaluate(data);
+
+ Assert.AreEqual(expectedResult, actualResult);
+ }
+
+ [TestMethod]
+ public void PropertyEqualsValue()
+ {
+ Run(JsonPathWith.Array(jv => jv.Name("test") == 5), "$[?(@.test == 5)]");
+ }
+ [TestMethod]
+ public void PropertyNotEqualToValue()
+ {
+ Run(JsonPathWith.Array(jv => jv.Name("test") != 5), "$[?(@.test != 5)]");
+ }
+ [TestMethod]
+ public void PropertyGreaterThanValue()
+ {
+ Run(JsonPathWith.Array(jv => jv.Name("test") > 5), "$[?(@.test > 5)]");
+ }
+ [TestMethod]
+ public void PropertyGreaterThanEqualToValue()
+ {
+ Run(JsonPathWith.Array(jv => jv.Name("test") >= 5), "$[?(@.test >= 5)]");
+ }
+ [TestMethod]
+ public void PropertyLessThanValue()
+ {
+ Run(JsonPathWith.Array(jv => jv.Name("test") < 5), "$[?(@.test < 5)]");
+ }
+ [TestMethod]
+ public void PropertyLessThanEqualToValue()
+ {
+ Run(JsonPathWith.Array(jv => jv.Name("test") <= 5), "$[?(@.test <= 5)]");
+ }
+ [TestMethod]
+ public void ArrayIndexEqualsValue()
+ {
+ Run(JsonPathWith.Array(jv => jv.ArrayIndex(1) == 5), "$[?(@[1] == 5)]");
+ }
+ [TestMethod]
+ [ExpectedException(typeof(JsonPathSyntaxException))]
+ public void ArrayMultiIndex()
+ {
+ var text = "$[?(@[1,3] == 5)]";
+
+ try
+ {
+ var actual = JsonPath.Parse(text);
+ }
+ catch (JsonPathSyntaxException e)
+ {
+ Assert.IsTrue(e.Message.StartsWith("JSON Path expression indexers only support single constant values."));
+ throw;
+ }
+ }
+ [TestMethod]
+ [ExpectedException(typeof(JsonPathSyntaxException))]
+ public void ArraySliceEqualsValue()
+ {
+ var text = "$[?(@[1:3] == 5)]";
+
+ try
+ {
+ var actual = JsonPath.Parse(text);
+ }
+ catch (JsonPathSyntaxException e)
+ {
+ Assert.IsTrue(e.Message.StartsWith("JSON Path expression indexers only support single constant values."));
+ throw;
+ }
+ }
+ [TestMethod]
+ [ExpectedException(typeof(JsonPathSyntaxException))]
+ public void ArrayIndexExpressionEqualsValue()
+ {
+ var text = "$[?(@[(@.length-1))]";
+
+ try
+ {
+ var actual = JsonPath.Parse(text);
+ }
+ catch (JsonPathSyntaxException e)
+ {
+ Assert.IsTrue(e.Message.StartsWith("JSON Path expression indexers only support single constant values."));
+ throw;
+ }
+ }
+ [TestMethod]
+ [ExpectedException(typeof(JsonPathSyntaxException))]
+ public void ArrayFilterExpressionEqualsValue()
+ {
+ var text = "$[?(@[?(@.name == 5))]";
+
+ try
+ {
+ var actual = JsonPath.Parse(text);
+ }
+ catch (JsonPathSyntaxException e)
+ {
+ Assert.IsTrue(e.Message.StartsWith("JSON Path expression indexers only support single constant values."));
+ throw;
+ }
+ }
+ [TestMethod]
+ public void HasProperty()
+ {
+ Run(JsonPathWith.Array(jv => jv.HasProperty("test")), "$[?(@.test)]");
+ }
+ [TestMethod]
+ public void DoesNotHaveProperty()
+ {
+ Run(JsonPathWith.Array(jv => !jv.HasProperty("test")), "$[?(!@.test)]");
+ }
+ [TestMethod]
+ public void GroupedNot()
+ {
+ Run(JsonPathWith.Array(jv => !(jv.HasProperty("test") && jv.Name("name") == 5)), "$[?(!(@.test && @.name == 5))]");
+ }
+ [TestMethod]
+ public void And()
+ {
+ Run(JsonPathWith.Array(jv => jv.HasProperty("test") && jv.Name("name") == 5), "$[?(@.test && @.name == 5)]");
+ }
+ [TestMethod]
+ public void Or()
+ {
+ Run(JsonPathWith.Array(jv => jv.HasProperty("test") || jv.Name("name") == 5),"$[?(@.test || @.name == 5)]");
+ }
+ [TestMethod]
+ // This won't work the same. Parsing generates an IndexOfExpression with a distinct parameter,
+ // but when constructing the path, the parameter goes through several castings generating a
+ // parameter expression. The parsed path would be different in structure but should still
+ // represent the same thing. We have to test by evaluation.
+ public void IndexOfNumber()
+ {
+ CompareEval(JsonPathWith.Array(jv => jv.IndexOf(5) == 4), "$[?(@.indexOf(5) == 4)]");
+ }
+ [TestMethod]
+ // This won't work the same. Parsing generates an IndexOfExpression with a distinct parameter,
+ // but when constructing the path, the parameter goes through several castings generating a
+ // parameter expression. The parsed path would be different in structure but should still
+ // represent the same thing. We have to test by evaluation.
+ public void IndexOfBoolean()
+ {
+ CompareEval(JsonPathWith.Array(jv => jv.IndexOf(true) == 3), "$[?(@.indexOf(true) == 3)]");
+ }
+ [TestMethod]
+ // This won't work the same. Parsing generates an IndexOfExpression with a distinct parameter,
+ // but when constructing the path, the parameter goes through several castings generating a
+ // parameter expression. The parsed path would be different in structure but should still
+ // represent the same thing. We have to test by evaluation.
+ public void IndexOfString()
+ {
+ CompareEval(JsonPathWith.Array(jv => jv.IndexOf("string") == 2), "$[?(@.indexOf(\"hello\") == 2)]");
+ }
+ [TestMethod]
+ // This won't work the same. Parsing generates a ValueExpression, but the only way to
+ // construct the path is to pass the field, which generates a FieldExpression. The parsed
+ // path would be different in structure but should still represent the same thing.
+ // We have to test by evaluation.
+ public void IndexOfArray()
+ {
+ var arr = new JsonArray {1, 2, 3};
+ CompareEval(JsonPathWith.Array(jv => jv.IndexOf(arr) == 6), "$[?(@.indexOf([1,2,3]) == 6)]");
+ }
+ [TestMethod]
+ // This won't work the same. Parsing generates a ValueExpression, but the only way to
+ // construct the path is to pass the field, which generates a FieldExpression. The parsed
+ // path would be different in structure but should still represent the same thing.
+ // We have to test by evaluation.
+ public void IndexOfObject()
+ {
+ var obj = new JsonObject
+ {
+ ["bool"] = false,
+ ["int"] = 20,
+ ["string"] = "value",
+ };
+ CompareEval(JsonPathWith.Array(jv => jv.IndexOf(obj) == 1), "$[?(@.indexOf({\"key\":\"value\"}) == 1)]");
+ }
+ }
+}
diff --git a/Manatee.Json.Tests/Path/GoessnerExamplesTest.cs b/Manatee.Json.Tests/Path/GoessnerExamplesTest.cs
index 2fe2f0c..ccb75ee 100644
--- a/Manatee.Json.Tests/Path/GoessnerExamplesTest.cs
+++ b/Manatee.Json.Tests/Path/GoessnerExamplesTest.cs
@@ -332,7 +332,7 @@ public void GoessnerExample6BParsed()
[TestMethod]
public void GoessnerExample6BConstructed()
{
- var path = JsonPathWith.Search("book").ArraySlice(-1, null);
+ var path = JsonPathWith.Search("book").Array(new Slice(-1, null));
var expected = new JsonArray
{
new JsonObject
@@ -426,7 +426,7 @@ public void GoessnerExample7BParsed()
[TestMethod]
public void GoessnerExample7BConstructed()
{
- var path = JsonPathWith.Search("book").ArraySlice(null, 2);
+ var path = JsonPathWith.Search("book").Array(new Slice(null, 2));
var expected = new JsonArray
{
new JsonObject
diff --git a/Manatee.Json.Tests/Path/IndexExpressionParseTest.cs b/Manatee.Json.Tests/Path/IndexExpressionParseTest.cs
new file mode 100644
index 0000000..03aff96
--- /dev/null
+++ b/Manatee.Json.Tests/Path/IndexExpressionParseTest.cs
@@ -0,0 +1,124 @@
+using Manatee.Json.Path;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Manatee.Json.Tests.Path
+{
+ [TestClass]
+ public class IndexExpressionParseTest
+ {
+ [TestMethod]
+ public void Length()
+ {
+ var text = "$[(@.length)]";
+ var expected = JsonPathWith.Array(jv => jv.Length());
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ public void Length_Root()
+ {
+ var text = "$[($.length)]";
+ var expected = JsonPathWith.Array(jv => JsonPathRoot.Length());
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ public void ExtendedLength()
+ {
+ var text = "$[(@.name.length)]";
+ var expected = JsonPathWith.Array(jv => jv.Name("name").Length());
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ public void Addition()
+ {
+ var text = "$[(@.length+1)]";
+ var expected = JsonPathWith.Array(jv => jv.Length() + 1);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ public void Subtraction()
+ {
+ var text = "$[(@.length-1)]";
+ var expected = JsonPathWith.Array(jv => jv.Length() - 1);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ public void Multiplication()
+ {
+ var text = "$[(@.length*1)]";
+ var expected = JsonPathWith.Array(jv => jv.Length()*1);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ public void Division()
+ {
+ var text = "$[(@.length/1)]";
+ var expected = JsonPathWith.Array(jv => jv.Length()/1);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ public void Modulus()
+ {
+ var text = "$[(@.length%1)]";
+ var expected = JsonPathWith.Array(jv => jv.Length()%1);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ [Ignore]
+ // The C# ^ operator doesn't have the required exponentiation priority, so
+ // constructing expressions with this operator results in a strange structure.
+ // Also not really sure JS supports it as an exponentiation operator, either.
+ public void Exponent()
+ {
+ var text = "$[(@.length^1)]";
+ var expected = JsonPathWith.Array(jv => jv.Length() ^ 1);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ public void Add3()
+ {
+ var text = "$[(3+@.length+3)]";
+ var expected = JsonPathWith.Array(jv => 3 + jv.Length() + 3);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ public void WhyOnGodsGreenEarthWouldAnyoneDoThis()
+ {
+ var text = "$[(4+@.length*($.name.length-2)+5)]";
+ var expected = JsonPathWith.Array(jv => 4 + jv.Length()*(JsonPathRoot.Name("name").Length() - 2) + 5);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ }
+}
diff --git a/Manatee.Json.Tests/Path/ParsingTest.cs b/Manatee.Json.Tests/Path/ParsingTest.cs
new file mode 100644
index 0000000..06ab062
--- /dev/null
+++ b/Manatee.Json.Tests/Path/ParsingTest.cs
@@ -0,0 +1,283 @@
+using Manatee.Json.Path;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Manatee.Json.Tests.Path
+{
+ [TestClass]
+ public class ParsingTest
+ {
+ private void Run(JsonPath expected, string text)
+ {
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void SingleNamedObject()
+ {
+ Run(JsonPathWith.Name("name"), "$.name");
+ }
+
+ [TestMethod]
+ public void SingleQuotedNamedObject()
+ {
+ var text = "$.'quoted name'";
+ var expected = JsonPathWith.Name("quoted name");
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void DoubleQuotedNamedObject()
+ {
+ var text = "$.\"quoted name\"";
+ var expected = JsonPathWith.Name("quoted name");
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void SingleWildcardObject()
+ {
+ var text = "$.*";
+ var expected = JsonPathWith.Wildcard();
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void NamedObjectWithWildcardObject()
+ {
+ var text = "$.name.*";
+ var expected = JsonPathWith.Name("name").Wildcard();
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void WildcardObjectWithNamedObject()
+ {
+ var text = "$.*.name";
+ var expected = JsonPathWith.Wildcard().Name("name");
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+ [TestMethod]
+ public void SingleNamedSearch()
+ {
+ var text = "$..name";
+ var expected = JsonPathWith.Search("name");
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void SingleQuotedNamedSearch()
+ {
+ var text = "$..'quoted name'";
+ var expected = JsonPathWith.Search("quoted name");
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void DoubleQuotedNamedSearch()
+ {
+ var text = "$..\"quoted name\"";
+ var expected = JsonPathWith.Search("quoted name");
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void SingleWildcardSearch()
+ {
+ var text = "$..*";
+ var expected = JsonPathWith.Search();
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void NamedObjectWithWildcardSearch()
+ {
+ var text = "$.name..*";
+ var expected = JsonPathWith.Name("name").Search();
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void WildcardObjectWithNamedSearch()
+ {
+ var text = "$.*..name";
+ var expected = JsonPathWith.Wildcard().Search("name");
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void SingleIndexedArray()
+ {
+ var text = "$[1]";
+ var expected = JsonPathWith.Array(1);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void SingleSlicedArray()
+ {
+ var text = "$[1:5]";
+ var expected = JsonPathWith.Array(new Slice(1, 5));
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void SteppedSlicedArray()
+ {
+ var text = "$[1:5:2]";
+ var expected = JsonPathWith.Array(new Slice(1, 5, 2));
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void IndexedSlicedArray()
+ {
+ var text = "$[1,5:7]";
+ var expected = JsonPathWith.Array(1, new Slice(5, 7));
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void SlicedIndexedArray()
+ {
+ var text = "$[1:5,7]";
+ var expected = JsonPathWith.Array(new Slice(1, 5), 7);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void MultiSlicedArray()
+ {
+ var text = "$[1:5,7:11:2]";
+ var expected = JsonPathWith.Array(new Slice(1, 5), new Slice(7, 11, 2));
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void MultiIndexedArray()
+ {
+ var text = "$[1,3]";
+ var expected = JsonPathWith.Array(1, 3);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(JsonPathSyntaxException))]
+ public void EmptyIndexedArray()
+ {
+ var text = "$[]";
+
+ JsonPath.Parse(text);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(JsonPathSyntaxException))]
+ public void EmptyObject()
+ {
+ var text = "$.";
+
+ JsonPath.Parse(text);
+ }
+
+ [TestMethod]
+ public void WildcardArray()
+ {
+ var text = "$[*]";
+ var expected = JsonPathWith.Array();
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void SearchIndexedArray()
+ {
+ var text = "$..[1]";
+ var expected = JsonPathWith.SearchArray(1);
+
+ var actual = JsonPath.Parse(text);
+
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void ChainedNameIndexedArray()
+ {
+ Run(JsonPathWith.Name("name").Array(4), "$.name[4]");
+ }
+
+ [TestMethod]
+ public void ChainedIndexedArrayName()
+ {
+ Run(JsonPathWith.Array(4).Name("name"), "$[4].name");
+ }
+
+ [TestMethod]
+ public void ChainedNameName()
+ {
+ Run(JsonPathWith.Name("name").Name("test"), "$.name.test");
+ }
+
+ [TestMethod]
+ public void ChainedIndexedArrayIndexedArray()
+ {
+ Run(JsonPathWith.Array(2).Array(4), "$[2][4]");
+ }
+ }
+}
diff --git a/Manatee.Json.Tests/Path/TestSuite/JsonPathTestSuite.cs b/Manatee.Json.Tests/Path/TestSuite/JsonPathTestSuite.cs
new file mode 100644
index 0000000..9837781
--- /dev/null
+++ b/Manatee.Json.Tests/Path/TestSuite/JsonPathTestSuite.cs
@@ -0,0 +1,120 @@
+/***************************************************************************************
+
+ Copyright 2016 Greg Dennis
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ File Name: JsonPathTestSuite.cs
+ Namespace: Manatee.Json.Tests.Schema.TestSuite
+ Class Name: JsonPathTestSuite
+ Purpose: Runs the series of schema tests defined at
+ https://github.com/json-schema-org/JSON-Schema-Test-Suite.
+
+***************************************************************************************/
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Manatee.Json.Serialization;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Manatee.Json.Tests.Path.TestSuite
+{
+ [TestClass]
+ public class JsonPathTestSuite
+ {
+ private const string TestFolder = @"..\..\..\Json-Path-Test-Suite\tests\";
+#pragma warning disable 649
+ private const string FileNameForDebugging = "";
+ private const string TestPathForDebugging = "";
+#pragma warning restore 649
+ private static readonly JsonSerializer Serializer;
+ private int _failures;
+ private int _passes;
+
+ static JsonPathTestSuite()
+ {
+ Serializer = new JsonSerializer();
+ JsonSerializationAbstractionMap.MapGeneric(typeof(IEnumerable<>), typeof(List<>));
+ }
+
+ [TestMethod]
+ public void RunSuite()
+ {
+ // uncomment and paste the filename of a test suite to debug it.
+ //_fileNameForDebugging = "";
+ // uncomment and paste the description of a test to debug it.
+ //_testNameForDebugging = "ref within ref valid";
+
+ try
+ {
+ var fileNames = Directory.GetFiles(TestFolder);
+
+ foreach (var fileName in fileNames)
+ {
+ _RunFile(fileName);
+ }
+
+ Assert.AreEqual(0, _failures);
+ }
+ finally
+ {
+ Console.WriteLine();
+ Console.WriteLine($"Passes: {_passes}");
+ Console.WriteLine($"Failures: {_failures}");
+ }
+ }
+
+ private void _RunFile(string fileName)
+ {
+ if (fileName == FileNameForDebugging)
+ {
+ System.Diagnostics.Debugger.Break();
+ }
+
+ var contents = File.ReadAllText(fileName);
+ var json = JsonValue.Parse(contents);
+
+ var testSet = Serializer.Deserialize(json);
+
+ Console.WriteLine($"{testSet.Title} ({fileName})");
+ _RunTestSet(testSet);
+ }
+
+ private void _RunTestSet(PathTestSet testSet)
+ {
+ foreach (var test in testSet.Tests)
+ {
+ var pathString = test.Path.ToString();
+ if (pathString == TestPathForDebugging)
+ {
+ System.Diagnostics.Debugger.Break();
+ }
+
+ var results = test.Path.Evaluate(testSet.Data);
+
+ if (Equals(results, test.Result))
+ {
+ // It's difficult to see the failures when everything shows.
+ // The Stack Trace Explorer needs to show color output. :/
+ //Console.WriteLine($" {test.Description} - Passed");
+ _passes++;
+ }
+ else
+ {
+ Console.WriteLine($" {pathString} - Failed");
+ _failures++;
+ }
+ }
+ }
+ }
+}
diff --git a/Manatee.Json.Tests/Path/TestSuite/PathTest.cs b/Manatee.Json.Tests/Path/TestSuite/PathTest.cs
new file mode 100644
index 0000000..7c85a33
--- /dev/null
+++ b/Manatee.Json.Tests/Path/TestSuite/PathTest.cs
@@ -0,0 +1,26 @@
+using System;
+using Manatee.Json.Path;
+using Manatee.Json.Serialization;
+
+namespace Manatee.Json.Tests.Path.TestSuite
+{
+ internal class PathTest : IJsonSerializable
+ {
+ public JsonPath Path { get; private set; }
+ public JsonArray Result { get; private set; }
+
+ public void FromJson(JsonValue json, JsonSerializer serializer)
+ {
+ Path = JsonPath.Parse(json.Object["path"].String);
+ var result = json.Object["result"];
+ if (result.Type == JsonValueType.Boolean && !result.Boolean)
+ Result = new JsonArray();
+ else
+ Result = result.Array;
+ }
+ public JsonValue ToJson(JsonSerializer serializer)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Manatee.Json.Tests/Path/TestSuite/PathTestSet.cs b/Manatee.Json.Tests/Path/TestSuite/PathTestSet.cs
new file mode 100644
index 0000000..3ce6623
--- /dev/null
+++ b/Manatee.Json.Tests/Path/TestSuite/PathTestSet.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using Manatee.Json.Serialization;
+
+namespace Manatee.Json.Tests.Path.TestSuite
+{
+ internal class PathTestSet : IJsonSerializable
+ {
+ public string Title { get; private set; }
+ public JsonValue Data { get; private set; }
+ public IEnumerable Tests { get; private set; }
+
+ public void FromJson(JsonValue json, JsonSerializer serializer)
+ {
+ Title = json.Object["title"].String;
+ Data = json.Object["data"];
+ Tests = serializer.Deserialize>(json.Object["tests"]);
+ }
+ public JsonValue ToJson(JsonSerializer serializer)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/Manatee.Json.Tests/Path/ToStringTest.cs b/Manatee.Json.Tests/Path/ToStringTest.cs
new file mode 100644
index 0000000..742e8b5
--- /dev/null
+++ b/Manatee.Json.Tests/Path/ToStringTest.cs
@@ -0,0 +1,223 @@
+using Manatee.Json.Path;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Manatee.Json.Tests.Path
+{
+ [TestClass]
+ public class ToStringTest
+ {
+ private void Run(string expected, JsonPath path)
+ {
+ var actual = path.ToString();
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestMethod]
+ public void JustRoot()
+ {
+ Run("$", new JsonPath());
+ }
+
+ #region Dot Operator
+
+ [TestMethod]
+ public void ObjectKey()
+ {
+ Run("$.name", JsonPathWith.Name("name"));
+ }
+ [TestMethod]
+ public void ObjectChain()
+ {
+ Run("$.name.test", JsonPathWith.Name("name").Name("test"));
+ }
+ [TestMethod]
+ public void Length()
+ {
+ Run("$.length", JsonPathWith.Length());
+ }
+
+ #endregion
+
+ #region Indexed Arrays
+
+ [TestMethod]
+ public void ArrayIndex_SingleConstant()
+ {
+ Run("$[0]", JsonPathWith.Array(0));
+ }
+ [TestMethod]
+ public void ArrayIndex_SingleSlice()
+ {
+ Run("$[0:6]", JsonPathWith.Array(new Slice(0, 6)));
+ }
+ [TestMethod]
+ public void ArrayIndex_SingleSliceStartEndStep()
+ {
+ Run("$[0:6:2]", JsonPathWith.Array(new Slice(0, 6, 2)));
+ }
+ [TestMethod]
+ public void ArrayIndex_SingleSliceStart()
+ {
+ Run("$[0:]", JsonPathWith.Array(new Slice(0, null)));
+ }
+ [TestMethod]
+ public void ArrayIndex_SingleSliceEnd()
+ {
+ Run("$[:6]", JsonPathWith.Array(new Slice(null, 6)));
+ }
+ [TestMethod]
+ public void ArrayIndex_SingleSliceStartStep()
+ {
+ Run("$[0::2]", JsonPathWith.Array(new Slice(0, null, 2)));
+ }
+ [TestMethod]
+ public void ArrayIndex_SingleEndStep()
+ {
+ Run("$[:6:2]", JsonPathWith.Array(new Slice(null, 6, 2)));
+ }
+ [TestMethod]
+ public void ArrayIndex_SingleSliceStep()
+ {
+ Run("$[::2]", JsonPathWith.Array(new Slice(null, null, 2)));
+ }
+ [TestMethod]
+ public void ArrayIndex_ConstantSlice()
+ {
+ Run("$[1,4:6]", JsonPathWith.Array(1, new Slice(4, 6)));
+ }
+
+ #endregion
+
+ #region Index Expression Arrays
+
+ [TestMethod]
+ public void ArrayIndexExpression_LocalLength()
+ {
+ Run("$[(@.length)]", JsonPathWith.Array(jv => jv.Length()));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_RootLength()
+ {
+ Run("$[($.length)]", JsonPathWith.Array(jv => JsonPathRoot.Length()));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_NameLength()
+ {
+ Run("$[(@.name.length)]", JsonPathWith.Array(jv => jv.Name("name").Length()));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_Addition()
+ {
+ Run("$[(@.length+1)]", JsonPathWith.Array(jv => jv.Length() + 1));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_Subtraction()
+ {
+ Run("$[(@.length-1)]", JsonPathWith.Array(jv => jv.Length() - 1));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_Multiplication()
+ {
+ Run("$[(@.length*1)]", JsonPathWith.Array(jv => jv.Length() * 1));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_Division()
+ {
+ Run("$[(@.length/1)]", JsonPathWith.Array(jv => jv.Length() / 1));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_Modulus()
+ {
+ Run("$[(@.length%1)]", JsonPathWith.Array(jv => jv.Length() % 1));
+ }
+
+ #endregion
+
+ #region Filter Expression Arrays
+
+ [TestMethod]
+ public void ArrayFilterExpression_LocalNameExists()
+ {
+ Run("$[?(@.test)]", JsonPathWith.Array(jv => jv.HasProperty("test")));
+ }
+ [TestMethod]
+ public void ArrayFilterExpression_LocalLengthEqualsInt()
+ {
+ Run("$[?(@.length == 1)]", JsonPathWith.Array(jv => jv.Length() == 1));
+ }
+ [TestMethod]
+ public void ArrayFilterExpression_LocalNameEqualsString()
+ {
+ Run("$[?(@.test == \"string\")]", JsonPathWith.Array(jv => jv.Name("test") == "string"));
+ }
+ [TestMethod]
+ public void ArrayFilterExpression_LocalNameChainEqualsBoolean()
+ {
+ Run("$[?(@.name.test == false)]", JsonPathWith.Array(jv => jv.Name("name").Name("test") == false));
+ }
+ [TestMethod]
+ public void ArrayFilterExpression_RootLengthNotEqualsValue()
+ {
+ Run("$[?($.length != 1)]", JsonPathWith.Array(jv => JsonPathRoot.Length() != 1));
+ }
+ [TestMethod]
+ public void ArrayFilterExpression_NameLengthLessThanValue()
+ {
+ Run("$[?(@.name.length < 1)]", JsonPathWith.Array(jv => jv.Name("name").Length() < 1));
+ }
+ [TestMethod]
+ public void ArrayFilterExpression_AdditionLessThanEqualValue()
+ {
+ Run("$[?(@.length <= 1)]", JsonPathWith.Array(jv => jv.Length() <= 1));
+ }
+ [TestMethod]
+ public void ArrayFilterExpression_SubtractionGreaterThanValue()
+ {
+ Run("$[?(@.length-1 > 2)]", JsonPathWith.Array(jv => jv.Length() - 1 > 2));
+ }
+ [TestMethod]
+ public void ArrayFilterExpression_MultiplicationGreaterThanEqualValue()
+ {
+ Run("$[?(@.length*1 >= 2)]", JsonPathWith.Array(jv => jv.Length() * 1 >= 2));
+ }
+ [TestMethod]
+ public void ArrayFilterExpression_DivisionEqualValue()
+ {
+ Run("$[?(@.length/1 == 2)]", JsonPathWith.Array(jv => jv.Length() / 1 == 2));
+ }
+ [TestMethod]
+ public void ArrayFilterExpression_ModulusEqualValue()
+ {
+ Run("$[?(@.length%1 == 2)]", JsonPathWith.Array(jv => jv.Length() % 1 == 2));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_IndexOfNumber()
+ {
+ Run("$[?(@.indexOf(5) == 4)]", JsonPathWith.Array(jv => jv.IndexOf(5) == 4));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_IndexOfBoolean()
+ {
+ Run("$[?(@.indexOf(false) == 4)]", JsonPathWith.Array(jv => jv.IndexOf(false) == 4));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_IndexOfString()
+ {
+ Run("$[?(@.indexOf(\"string\") == 4)]", JsonPathWith.Array(jv => jv.IndexOf("string") == 4));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_IndexOfArray()
+ {
+ var arr = new JsonArray {1, 2, 3};
+ Run("$[?(@.indexOf([1,2,3]) == 4)]", JsonPathWith.Array(jv => jv.IndexOf(arr) == 4));
+ }
+ [TestMethod]
+ public void ArrayIndexExpression_IndexOfObject()
+ {
+ var obj = new JsonObject {{"key", "value"}};
+ Run("$[?(@.indexOf({\"key\":\"value\"}) == 4)]", JsonPathWith.Array(jv => jv.IndexOf(obj) == 4));
+ }
+
+ #endregion
+ }
+}
diff --git a/Manatee.Json.Tests/Serialization/JsonDeserializerTest.cs b/Manatee.Json.Tests/Serialization/JsonDeserializerTest.cs
index f08d92d..e60233f 100644
--- a/Manatee.Json.Tests/Serialization/JsonDeserializerTest.cs
+++ b/Manatee.Json.Tests/Serialization/JsonDeserializerTest.cs
@@ -286,6 +286,7 @@ public void InterfaceWithoutMap_Successful()
{
{"RequiredProp", "test"}
};
+ JsonSerializationAbstractionMap.RemoveMap();
IInterface expected = new ImplementationClass {RequiredProp = "test"};
var actual = serializer.Deserialize(json);
@@ -562,6 +563,7 @@ public void UnimplementedInterface_ReturnsRunTimeImplementation()
{
var serializer = new JsonSerializer();
var json = new JsonObject {{"RequiredProp", "test"}};
+ JsonSerializationAbstractionMap.RemoveMap();
IInterface expected = new ImplementationClass {RequiredProp = "test"};
var actual = serializer.Deserialize(json);
diff --git a/Manatee.Json.Tests/packages.config b/Manatee.Json.Tests/packages.config
deleted file mode 100644
index 5916821..0000000
--- a/Manatee.Json.Tests/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/Manatee.Json.sln.DotSettings b/Manatee.Json.sln.DotSettings
index 627c71a..f70fddb 100644
--- a/Manatee.Json.sln.DotSettings
+++ b/Manatee.Json.sln.DotSettings
@@ -17,9 +17,11 @@
TOGETHER_SAME_LINE
NEXT_LINE_SHIFTED_2
200
- <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Private methods, properties, and events"><ElementKinds><Kind Name="METHOD" /><Kind Name="EVENT" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
True
True
- True
\ No newline at end of file
+ True
+ <data><IncludeFilters /><ExcludeFilters><Filter ModuleMask="Manatee.Json" ModuleVersionMask="*" ClassMask="Manatee.Json.Path.JsonPathRoot" FunctionMask="*" IsEnabled="True" /></ExcludeFilters></data>
\ No newline at end of file
diff --git a/Manatee.Json.sln.DotSettings.user b/Manatee.Json.sln.DotSettings.user
index 0e7f399..646f45c 100644
--- a/Manatee.Json.sln.DotSettings.user
+++ b/Manatee.Json.sln.DotSettings.user
@@ -1,5 +1,8 @@
SOLUTION
+ True
+ 2016.2
+ C:\Users\Gregd\AppData\Local\Temp\ssm.Qageqif.tmp
diff --git a/Manatee.Json/Internal/GeneralExtensions.cs b/Manatee.Json/Internal/GeneralExtensions.cs
index 8311b84..588b51b 100644
--- a/Manatee.Json/Internal/GeneralExtensions.cs
+++ b/Manatee.Json/Internal/GeneralExtensions.cs
@@ -267,5 +267,16 @@ public static bool ContentsEqual(this IEnumerable a, IEnumerable b)
var listB = b.ToList();
return listA.Count == listB.Count && listA.All(item => listB.Contains(item));
}
+ public static JsonValue AsJsonValue(this object value)
+ {
+ if (value is JsonValue) return (JsonValue) value;
+ if (value is JsonArray) return (JsonArray) value;
+ if (value is JsonObject) return (JsonObject) value;
+ if (value is string) return (string) value;
+ if (value is bool) return (bool) value;
+ if (value is IConvertible) return Convert.ToDouble(value);
+
+ return null;
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/JsonArray.cs b/Manatee.Json/JsonArray.cs
index f3fab9c..bf7334c 100644
--- a/Manatee.Json/JsonArray.cs
+++ b/Manatee.Json/JsonArray.cs
@@ -21,8 +21,8 @@
***************************************************************************************/
+using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
using Manatee.Json.Internal;
@@ -72,7 +72,26 @@ public string GetIndentedString(int indentLevel = 0)
s += $"{tab1}{this[i].GetIndentedString(indentLevel + 1)}\n{tab0}]";
return s;
}
-
+ ///
+ /// Adds an object to the end of the .
+ ///
+ /// The object to be added to the end of the .
+ /// If the value is null, it will be replaced by .
+ public new void Add(JsonValue item)
+ {
+ base.Add(item ?? JsonValue.Null);
+ }
+ ///
+ /// Adds the elements of the specified collection to the end of the .
+ ///
+ /// The collection whose elements should be added to the end of the
+ /// . The collection itself cannot be null, but it can contain elements
+ /// that are null. These elements will be replaced by
+ /// is null.
+ public new void AddRange(IEnumerable collection)
+ {
+ base.AddRange(collection.Select(v => v ?? JsonValue.Null));
+ }
///
/// Creates a string representation of the JSON data.
///
diff --git a/Manatee.Json/JsonObject.cs b/Manatee.Json/JsonObject.cs
index be43c75..3a7b22a 100644
--- a/Manatee.Json/JsonObject.cs
+++ b/Manatee.Json/JsonObject.cs
@@ -88,7 +88,8 @@ public string GetIndentedString(int indentLevel = 0)
/// Adds the specified key and value to the dictionary.
///
/// The key of the element to add.
- /// The value of the element to add. The value can be null for reference types.
+ /// The value of the element to add. If the value is null,
+ /// it will be replaced by .
public new void Add(string key, JsonValue value)
{
base.Add(key, value ?? JsonValue.Null);
diff --git a/Manatee.Json/Manatee.Json.csproj b/Manatee.Json/Manatee.Json.csproj
index 3992d46..196a586 100644
--- a/Manatee.Json/Manatee.Json.csproj
+++ b/Manatee.Json/Manatee.Json.csproj
@@ -167,36 +167,6 @@
prompt
MinimumRecommendedRules.ruleset
-
-
- ..\packages\Manatee.StateMachine.1.1.2\lib\net35\Manatee.StateMachine.dll
- True
-
-
-
-
- ..\packages\Manatee.StateMachine.1.1.2\lib\net35-client\Manatee.StateMachine.dll
- True
-
-
-
-
- ..\packages\Manatee.StateMachine.1.1.2\lib\net40\Manatee.StateMachine.dll
- True
-
-
-
-
- ..\packages\Manatee.StateMachine.1.1.2\lib\net40-client\Manatee.StateMachine.dll
- True
-
-
-
-
- ..\packages\Manatee.StateMachine.1.1.2\lib\net45\Manatee.StateMachine.dll
- True
-
-
@@ -233,7 +203,34 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -246,7 +243,6 @@
-
@@ -311,6 +307,14 @@
+
+
+
+
+
+
+
+
@@ -535,7 +539,6 @@
-
diff --git a/Manatee.Json/Parsing/ArrayParser.cs b/Manatee.Json/Parsing/ArrayParser.cs
index ecda5a2..9798f66 100644
--- a/Manatee.Json/Parsing/ArrayParser.cs
+++ b/Manatee.Json/Parsing/ArrayParser.cs
@@ -32,7 +32,7 @@ public bool Handles(char c)
{
return c == '[';
}
- public string TryParse(string source, ref int index, out JsonValue value)
+ public string TryParse(string source, ref int index, out JsonValue value, bool allowExtraChars)
{
var array = new JsonArray();
value = array;
diff --git a/Manatee.Json/Parsing/BoolParser.cs b/Manatee.Json/Parsing/BoolParser.cs
index df9132a..29cb83b 100644
--- a/Manatee.Json/Parsing/BoolParser.cs
+++ b/Manatee.Json/Parsing/BoolParser.cs
@@ -33,7 +33,7 @@ public bool Handles(char c)
{
return c.In('t', 'T', 'f', 'F');
}
- public string TryParse(string source, ref int index, out JsonValue value)
+ public string TryParse(string source, ref int index, out JsonValue value, bool allowExtraChars)
{
char[] buffer;
int count;
diff --git a/Manatee.Json/Parsing/IJsonParser.cs b/Manatee.Json/Parsing/IJsonParser.cs
index 2bb06bd..838e6b1 100644
--- a/Manatee.Json/Parsing/IJsonParser.cs
+++ b/Manatee.Json/Parsing/IJsonParser.cs
@@ -29,7 +29,7 @@ internal interface IJsonParser
{
bool Handles(char c);
// returns error message, if any. Null return implies success.
- string TryParse(string source, ref int index, out JsonValue value);
+ string TryParse(string source, ref int index, out JsonValue value, bool allowExtraChars);
string TryParse(StreamReader stream, out JsonValue value);
}
}
diff --git a/Manatee.Json/Parsing/JsonParser.cs b/Manatee.Json/Parsing/JsonParser.cs
index d2ac7f0..5eeecf4 100644
--- a/Manatee.Json/Parsing/JsonParser.cs
+++ b/Manatee.Json/Parsing/JsonParser.cs
@@ -58,7 +58,7 @@ public static JsonValue Parse(StreamReader stream)
throw new JsonSyntaxException(errorMessage, value);
return value;
}
- public static string Parse(string source, ref int index, out JsonValue value)
+ public static string Parse(string source, ref int index, out JsonValue value, bool allowExtraChars = false)
{
var length = source.Length;
char c;
@@ -74,7 +74,7 @@ public static string Parse(string source, ref int index, out JsonValue value)
value = null;
return "Cannot determine type.";
}
- errorMessage = parser.TryParse(source, ref index, out value);
+ errorMessage = parser.TryParse(source, ref index, out value, allowExtraChars);
return errorMessage;
}
public static string Parse(StreamReader stream, out JsonValue value)
diff --git a/Manatee.Json/Parsing/NullParser.cs b/Manatee.Json/Parsing/NullParser.cs
index 775f26e..038d02c 100644
--- a/Manatee.Json/Parsing/NullParser.cs
+++ b/Manatee.Json/Parsing/NullParser.cs
@@ -32,7 +32,7 @@ public bool Handles(char c)
{
return c.In('n', 'N');
}
- public string TryParse(string source, ref int index, out JsonValue value)
+ public string TryParse(string source, ref int index, out JsonValue value, bool allowExtraChars)
{
var buffer = new char[4];
for (int i = 0; i < 4 && index + i < source.Length; i++)
diff --git a/Manatee.Json/Parsing/NumberParser.cs b/Manatee.Json/Parsing/NumberParser.cs
index 0d96081..0a2de7b 100644
--- a/Manatee.Json/Parsing/NumberParser.cs
+++ b/Manatee.Json/Parsing/NumberParser.cs
@@ -37,7 +37,7 @@ public bool Handles(char c)
{
return c.In('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-');
}
- public string TryParse(string source, ref int index, out JsonValue value)
+ public string TryParse(string source, ref int index, out JsonValue value, bool allowExtraChars)
{
var bufferSize = 0;
var bufferLength = FibSequence[bufferSize];
@@ -57,7 +57,9 @@ public string TryParse(string source, ref int index, out JsonValue value)
}
var c = source[index];
if (char.IsWhiteSpace(c) || c.In(',', ']', '}')) break;
- if (!NumberChars.Contains(c))
+ var isNumber = NumberChars.Contains(c);
+ if (!isNumber && allowExtraChars) break;
+ if (!isNumber)
{
value = null;
return "Expected \',\', \']\', or \'}\'.";
diff --git a/Manatee.Json/Parsing/ObjectParser.cs b/Manatee.Json/Parsing/ObjectParser.cs
index d6c6aaf..e412412 100644
--- a/Manatee.Json/Parsing/ObjectParser.cs
+++ b/Manatee.Json/Parsing/ObjectParser.cs
@@ -32,7 +32,7 @@ public bool Handles(char c)
{
return c == '{';
}
- public string TryParse(string source, ref int index, out JsonValue value)
+ public string TryParse(string source, ref int index, out JsonValue value, bool allowExtraChars)
{
var obj = new JsonObject();
value = obj;
diff --git a/Manatee.Json/Parsing/StringParser.cs b/Manatee.Json/Parsing/StringParser.cs
index f37e920..16dc195 100644
--- a/Manatee.Json/Parsing/StringParser.cs
+++ b/Manatee.Json/Parsing/StringParser.cs
@@ -34,7 +34,7 @@ public bool Handles(char c)
{
return c == '\"';
}
- public string TryParse(string source, ref int index, out JsonValue value)
+ public string TryParse(string source, ref int index, out JsonValue value, bool allowExtraChars)
{
var bufferSize = 0;
var bufferLength = FibSequence[bufferSize];
diff --git a/Manatee.Json/Path/ArrayParameters/FilterExpressionQuery.cs b/Manatee.Json/Path/ArrayParameters/FilterExpressionQuery.cs
index 37da1ca..554b04f 100644
--- a/Manatee.Json/Path/ArrayParameters/FilterExpressionQuery.cs
+++ b/Manatee.Json/Path/ArrayParameters/FilterExpressionQuery.cs
@@ -21,13 +21,14 @@
***************************************************************************************/
+using System;
using System.Collections.Generic;
using System.Linq;
using Manatee.Json.Path.Expressions;
namespace Manatee.Json.Path.ArrayParameters
{
- internal class FilterExpressionQuery : IJsonPathArrayQuery
+ internal class FilterExpressionQuery : IJsonPathArrayQuery, IEquatable
{
private readonly Expression _expression;
@@ -44,5 +45,19 @@ public override string ToString()
{
return $"?({_expression})";
}
+ public bool Equals(FilterExpressionQuery other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Equals(_expression, other._expression);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as FilterExpressionQuery);
+ }
+ public override int GetHashCode()
+ {
+ return _expression?.GetHashCode() ?? 0;
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/ArrayParameters/IndexExpressionQuery.cs b/Manatee.Json/Path/ArrayParameters/IndexExpressionQuery.cs
index 592abaf..9ac1316 100644
--- a/Manatee.Json/Path/ArrayParameters/IndexExpressionQuery.cs
+++ b/Manatee.Json/Path/ArrayParameters/IndexExpressionQuery.cs
@@ -20,13 +20,14 @@
Purpose: Provides expression-based indexing for array queries.
***************************************************************************************/
+using System;
using System.Collections.Generic;
using System.Linq;
using Manatee.Json.Path.Expressions;
namespace Manatee.Json.Path.ArrayParameters
{
- internal class IndexExpressionQuery : IJsonPathArrayQuery
+ internal class IndexExpressionQuery : IJsonPathArrayQuery, IEquatable
{
private readonly Expression _expression;
@@ -42,7 +43,21 @@ public IEnumerable Find(JsonArray json, JsonValue root)
}
public override string ToString()
{
- return string.Format("({0})", _expression);
+ return $"({_expression})";
+ }
+ public bool Equals(IndexExpressionQuery other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Equals(_expression, other._expression);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as IndexExpressionQuery);
+ }
+ public override int GetHashCode()
+ {
+ return _expression?.GetHashCode() ?? 0;
}
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/ArrayParameters/IndexQuery.cs b/Manatee.Json/Path/ArrayParameters/IndexQuery.cs
deleted file mode 100644
index 1e85170..0000000
--- a/Manatee.Json/Path/ArrayParameters/IndexQuery.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-/***************************************************************************************
-
- Copyright 2016 Greg Dennis
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
- File Name: IndexQuery.cs
- Namespace: Manatee.Json.Path.ArrayParameters
- Class Name: IndexQuery
- Purpose: Provides indexed array queries.
-
-***************************************************************************************/
-using System.Collections.Generic;
-using System.Linq;
-using Manatee.Json.Internal;
-
-namespace Manatee.Json.Path.ArrayParameters
-{
- internal class IndexQuery : IJsonPathArrayQuery
- {
- private readonly IEnumerable _indices;
-
- public IndexQuery(params int[] indices)
- {
- _indices = indices;
- }
-
- public IEnumerable Find(JsonArray json, JsonValue root)
- {
- var index = 0;
- while (index < json.Count)
- {
- if (_indices.Contains(index))
- yield return json[index];
- index++;
- }
- }
- public override string ToString()
- {
- return _indices.Join(",");
- }
- }
-}
\ No newline at end of file
diff --git a/Manatee.Json/Path/ArrayParameters/SliceQuery.cs b/Manatee.Json/Path/ArrayParameters/SliceQuery.cs
index 8c35186..b536732 100644
--- a/Manatee.Json/Path/ArrayParameters/SliceQuery.cs
+++ b/Manatee.Json/Path/ArrayParameters/SliceQuery.cs
@@ -21,52 +21,43 @@
***************************************************************************************/
using System;
+using Manatee.Json.Internal;
using System.Collections.Generic;
+using System.Linq;
namespace Manatee.Json.Path.ArrayParameters
{
- internal class SliceQuery : IJsonPathArrayQuery
+ internal class SliceQuery : IJsonPathArrayQuery, IEquatable
{
- private int? _start;
- private int? _end;
- private int? _step;
+ internal IEnumerable Slices { get; }
- public SliceQuery(int? start, int? end, int? step = null)
+ public SliceQuery(params Slice[] slices)
+ : this((IEnumerable) slices) {}
+ public SliceQuery(IEnumerable slices)
{
- _start = start;
- _end = end;
- _step = step;
+ Slices = slices.ToList();
}
-
public IEnumerable Find(JsonArray json, JsonValue root)
{
- var start = ResolveIndex(_start ?? 0, json.Count);
- var end = ResolveIndex(_end ?? json.Count, json.Count);
- var step = Math.Max(_step ?? 1, 1);
-
- var index = start;
- while (index < end)
- {
- yield return json[index];
- index += step;
- }
+ return Slices.SelectMany(s => s.Find(json, root)).Distinct();
}
public override string ToString()
{
- return _step.HasValue
- ? string.Format("{0}:{1}:{2}",
- _start.HasValue ? _start.ToString() : string.Empty,
- _end.HasValue ? _end.ToString() : string.Empty,
- _step)
- : string.Format("{0}:{1}",
- _start.HasValue ? _start.ToString() : string.Empty,
- _end.HasValue ? _end.ToString() : string.Empty);
-
+ return Slices.Join(",");
}
-
- private static int ResolveIndex(int index, int count)
+ public bool Equals(SliceQuery other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Slices.ContentsEqual(other.Slices);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as SliceQuery);
+ }
+ public override int GetHashCode()
{
- return index < 0 ? count + index : index;
+ return Slices?.GetCollectionHashCode() ?? 0;
}
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/ArrayParameters/WildCardQuery.cs b/Manatee.Json/Path/ArrayParameters/WildCardQuery.cs
index da3220a..fc0fe3d 100644
--- a/Manatee.Json/Path/ArrayParameters/WildCardQuery.cs
+++ b/Manatee.Json/Path/ArrayParameters/WildCardQuery.cs
@@ -20,11 +20,12 @@
Purpose: Provides a wildcard for array queries.
***************************************************************************************/
+using System;
using System.Collections.Generic;
namespace Manatee.Json.Path.ArrayParameters
{
- internal class WildCardQuery : IJsonPathArrayQuery
+ internal class WildCardQuery : IJsonPathArrayQuery, IEquatable
{
public static WildCardQuery Instance { get; }
@@ -32,6 +33,7 @@ static WildCardQuery()
{
Instance = new WildCardQuery();
}
+ private WildCardQuery() {}
public IEnumerable Find(JsonArray json, JsonValue root)
{
@@ -41,5 +43,18 @@ public override string ToString()
{
return "*";
}
+ public bool Equals(WildCardQuery other)
+ {
+ return !ReferenceEquals(null, other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as WildCardQuery);
+ }
+ public override int GetHashCode()
+ {
+ // ReSharper disable once BaseObjectGetHashCodeCallInGetHashCode
+ return base.GetHashCode();
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/AddExpression.cs b/Manatee.Json/Path/Expressions/AddExpression.cs
index 1fcf53c..87bdf29 100644
--- a/Manatee.Json/Path/Expressions/AddExpression.cs
+++ b/Manatee.Json/Path/Expressions/AddExpression.cs
@@ -21,11 +21,13 @@
***************************************************************************************/
+using System;
+
namespace Manatee.Json.Path.Expressions
{
- internal class AddExpression : ExpressionTreeBranch
+ internal class AddExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 2;
+ protected override int BasePriority => 2;
public override object Evaluate(T json, JsonValue root)
{
@@ -44,5 +46,27 @@ public override string ToString()
{
return $"{Left}+{Right}";
}
+ public bool Equals(AddExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((AddExpression) obj);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/AndExpression.cs b/Manatee.Json/Path/Expressions/AndExpression.cs
index fde7413..ebf61e2 100644
--- a/Manatee.Json/Path/Expressions/AndExpression.cs
+++ b/Manatee.Json/Path/Expressions/AndExpression.cs
@@ -20,11 +20,13 @@
Purpose: Expresses the intent to perform a boolean AND.
***************************************************************************************/
+using System;
+
namespace Manatee.Json.Path.Expressions
{
- internal class AndExpression : ExpressionTreeBranch
+ internal class AndExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 0;
+ protected override int BasePriority => 0;
public override object Evaluate(T json, JsonValue root)
{
@@ -34,5 +36,24 @@ public override string ToString()
{
return $"{Left} && {Right}";
}
+ public bool Equals(AndExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as AndExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/ArrayIndexExpression.cs b/Manatee.Json/Path/Expressions/ArrayIndexExpression.cs
index 7822287..c4a7f16 100644
--- a/Manatee.Json/Path/Expressions/ArrayIndexExpression.cs
+++ b/Manatee.Json/Path/Expressions/ArrayIndexExpression.cs
@@ -23,18 +23,20 @@
***************************************************************************************/
using System;
using System.Linq;
+using Manatee.Json.Internal;
namespace Manatee.Json.Path.Expressions
{
- internal class ArrayIndexExpression : PathExpression
+ internal class ArrayIndexExpression : PathExpression, IEquatable>
{
- public override int Priority => 6;
public int Index { get; set; }
public ExpressionTreeNode IndexExpression { get; set; }
+ protected override int BasePriority => 6;
+
public override object Evaluate(T json, JsonValue root)
{
- var value = IsLocal ? json as JsonValue : root;
+ var value = IsLocal ? json.AsJsonValue() : root;
if (value == null)
throw new NotSupportedException("ArrayIndex requires a JsonValue to evaluate.");
var results = Path.Evaluate(value);
@@ -56,6 +58,28 @@ public override string ToString()
var path = Path == null ? string.Empty : Path.GetRawString();
return string.Format(IsLocal ? "@{0}[{1}]" : "${0}[{1}]", path, GetIndex());
}
+ public bool Equals(ArrayIndexExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other) &&
+ Index == other.Index &&
+ Equals(IndexExpression, other.IndexExpression);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as ArrayIndexExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode*397) ^ Index;
+ hashCode = (hashCode*397) ^ (IndexExpression?.GetHashCode() ?? 0);
+ return hashCode;
+ }
+ }
private int GetIndex()
{
diff --git a/Manatee.Json/Path/Expressions/ConversionExpression.cs b/Manatee.Json/Path/Expressions/ConversionExpression.cs
index 63e2e4e..45c4369 100644
--- a/Manatee.Json/Path/Expressions/ConversionExpression.cs
+++ b/Manatee.Json/Path/Expressions/ConversionExpression.cs
@@ -25,12 +25,13 @@
namespace Manatee.Json.Path.Expressions
{
- internal class ConversionExpression : ExpressionTreeNode
+ internal class ConversionExpression : ExpressionTreeNode, IEquatable>
{
- public override int Priority => 6;
public ExpressionTreeNode Root { get; set; }
public Type TargetType { get; set; }
+ protected override int BasePriority => 6;
+
public override object Evaluate(T json, JsonValue root)
{
var value = Root.Evaluate(json, root);
@@ -41,6 +42,20 @@ public override string ToString()
{
return Root.ToString();
}
+ public bool Equals(ConversionExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Equals(Root, other.Root) && TargetType == other.TargetType;
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as ConversionExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked { return ((Root?.GetHashCode() ?? 0)*397) ^ (TargetType != null ? TargetType.GetHashCode() : 0); }
+ }
private object CastValue(object value)
{
@@ -49,7 +64,9 @@ private object CastValue(object value)
return new JsonValue((bool)value);
if (value is string)
return new JsonValue((string)value);
- return new JsonValue(Convert.ToDouble(value));
+ if (value is IConvertible)
+ return new JsonValue(Convert.ToDouble(value));
+ return value;
}
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/DivideExpression.cs b/Manatee.Json/Path/Expressions/DivideExpression.cs
index 7c90c10..e875e2c 100644
--- a/Manatee.Json/Path/Expressions/DivideExpression.cs
+++ b/Manatee.Json/Path/Expressions/DivideExpression.cs
@@ -21,11 +21,13 @@
***************************************************************************************/
+using System;
+
namespace Manatee.Json.Path.Expressions
{
- internal class DivideExpression : ExpressionTreeBranch
+ internal class DivideExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 3;
+ protected override int BasePriority => 3;
public override object Evaluate(T json, JsonValue root)
{
@@ -40,5 +42,24 @@ public override string ToString()
var right = Right.Priority <= Priority ? $"({Right})" : Right.ToString();
return $"{left}/{right}";
}
+ public bool Equals(DivideExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as DivideExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/ExponentExpression.cs b/Manatee.Json/Path/Expressions/ExponentExpression.cs
index 9c3c5d5..defc193 100644
--- a/Manatee.Json/Path/Expressions/ExponentExpression.cs
+++ b/Manatee.Json/Path/Expressions/ExponentExpression.cs
@@ -24,9 +24,9 @@
namespace Manatee.Json.Path.Expressions
{
- internal class ExponentExpression : ExpressionTreeBranch
+ internal class ExponentExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 4;
+ protected override int BasePriority => 4;
public override object Evaluate(T json, JsonValue root)
{
@@ -40,5 +40,24 @@ public override string ToString()
var right = Right.Priority <= Priority ? $"({Right})" : Right.ToString();
return $"{left}^{right}";
}
+ public bool Equals(ExponentExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as ExponentExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/Expression.cs b/Manatee.Json/Path/Expressions/Expression.cs
index 2c3d0ff..cfa8dbb 100644
--- a/Manatee.Json/Path/Expressions/Expression.cs
+++ b/Manatee.Json/Path/Expressions/Expression.cs
@@ -21,96 +21,33 @@
***************************************************************************************/
using System;
-using System.Collections.Generic;
-using System.Linq;
-using Manatee.Json.Internal;
-using Manatee.Json.Path.Operators;
-using Manatee.StateMachine;
-using Manatee.StateMachine.Exceptions;
namespace Manatee.Json.Path.Expressions
{
- internal class Expression
+ internal class Expression : IEquatable>
{
- private enum State
- {
- Start,
- Value,
- NumberOrPath,
- Operator,
- Comparison,
- ValueOrOperator,
- BooleanOrComparison,
- End,
- }
-
- private static readonly StateMachine _stateMachine = new StateMachine();
-
- private List> _nodeList;
- private readonly InputStream _stream = new InputStream();
- private bool _done;
- private string _source;
- private int _index;
- private JsonPathExpressionInput? _previousInput;
- private int _startIndex;
+ private readonly ExpressionTreeNode _root;
- internal ExpressionTreeNode Root { get; set; }
-
- static Expression()
+ public Expression(ExpressionTreeNode root)
{
- _stateMachine[State.Start, JsonPathExpressionInput.OpenParenth] = GotStart;
- _stateMachine[State.Value, JsonPathExpressionInput.OpenParenth] = GotGroup;
- _stateMachine[State.Value, JsonPathExpressionInput.Number] = GotNumber;
- _stateMachine[State.Value, JsonPathExpressionInput.Minus] = GotNumber;
- _stateMachine[State.Value, JsonPathExpressionInput.Root] = GotRoot;
- _stateMachine[State.Value, JsonPathExpressionInput.Current] = GotCurrent;
- _stateMachine[State.Value, JsonPathExpressionInput.Bang] = GotNot;
- _stateMachine[State.Value, JsonPathExpressionInput.Quote] = GotString;
- _stateMachine[State.Value, JsonPathExpressionInput.Letter] = GotNamedConstant;
- _stateMachine[State.NumberOrPath, JsonPathExpressionInput.OpenParenth] = GotGroup;
- _stateMachine[State.NumberOrPath, JsonPathExpressionInput.Number] = GotNumber;
- _stateMachine[State.NumberOrPath, JsonPathExpressionInput.Root] = GotRoot;
- _stateMachine[State.NumberOrPath, JsonPathExpressionInput.Current] = GotCurrent;
- _stateMachine[State.Operator, JsonPathExpressionInput.Plus] = GotPlus;
- _stateMachine[State.Operator, JsonPathExpressionInput.Minus] = GotMinus;
- _stateMachine[State.Operator, JsonPathExpressionInput.Star] = GotMultiply;
- _stateMachine[State.Operator, JsonPathExpressionInput.Slash] = GotDivide;
- _stateMachine[State.Operator, JsonPathExpressionInput.Caret] = GotExponent;
- _stateMachine[State.Operator, JsonPathExpressionInput.LessThan] = GotLessThan;
- _stateMachine[State.Operator, JsonPathExpressionInput.Equal] = GotEqual;
- _stateMachine[State.Operator, JsonPathExpressionInput.And] = GotAnd;
- _stateMachine[State.Operator, JsonPathExpressionInput.Or] = GotOr;
- _stateMachine[State.Operator, JsonPathExpressionInput.GreaterThan] = GotGreaterThan;
- _stateMachine[State.Operator, JsonPathExpressionInput.Bang] = GotNot;
- _stateMachine[State.Operator, JsonPathExpressionInput.CloseParenth] = CompleteExpression;
- _stateMachine[State.Comparison, JsonPathExpressionInput.Equal] = GotEqual;
- _stateMachine[State.Comparison, JsonPathExpressionInput.And] = GotAnd;
- _stateMachine[State.Comparison, JsonPathExpressionInput.Or] = GotOr;
- _stateMachine[State.ValueOrOperator, JsonPathExpressionInput.OpenParenth] = GotGroup;
- _stateMachine[State.ValueOrOperator, JsonPathExpressionInput.Number] = GotNumber;
- _stateMachine[State.ValueOrOperator, JsonPathExpressionInput.Root] = GotRoot;
- _stateMachine[State.ValueOrOperator, JsonPathExpressionInput.Current] = GotCurrent;
- _stateMachine[State.ValueOrOperator, JsonPathExpressionInput.Equal] = FinalizeComparison;
- _stateMachine[State.ValueOrOperator, JsonPathExpressionInput.Letter] = GotNamedConstant;
- _stateMachine[State.BooleanOrComparison, JsonPathExpressionInput.OpenParenth] = GotGroup;
- _stateMachine[State.BooleanOrComparison, JsonPathExpressionInput.Root] = GotRoot;
- _stateMachine[State.BooleanOrComparison, JsonPathExpressionInput.Current] = GotCurrent;
- _stateMachine[State.BooleanOrComparison, JsonPathExpressionInput.Equal] = GotEqual;
- _stateMachine[State.BooleanOrComparison, JsonPathExpressionInput.Letter] = GotNamedConstant;
- _stateMachine.UpdateFunction = GetNextInput;
+ _root = root;
}
public T Evaluate(TIn json, JsonValue root)
{
- var result = Root.Evaluate(json, root);
- if (typeof (T) == typeof (bool) && result == null)
+ var result = _root.Evaluate(json, root);
+ if (typeof(T) == typeof(bool) && result == null)
return (T) (object) false;
- if (typeof (T) == typeof (bool) && result != null && !(result is bool))
+ if (typeof(T) == typeof(bool) && result != null && !(result is bool))
return (T) (object) true;
- if (typeof (T) == typeof (int) && result == null)
+ if (typeof(T) == typeof(int) && result == null)
return (T) (object) -1;
- if (typeof (T) == typeof (JsonValue))
+ var resultAsJsonValue = result as JsonValue;
+ if (typeof(T) == typeof(int) && resultAsJsonValue?.Type == JsonValueType.Number)
+ return (T) Convert.ChangeType(resultAsJsonValue.Number, typeof(T));
+ if (typeof(T) == typeof(JsonValue))
{
+ if (result is JsonValue) return (T) result;
if (result is double)
return (T) (object) new JsonValue((double) result);
if (result is bool)
@@ -122,386 +59,25 @@ public T Evaluate(TIn json, JsonValue root)
if (result is JsonObject)
return (T) (object) new JsonValue((JsonObject) result);
}
- return (T)Convert.ChangeType(result, typeof(T));
+ return (T) Convert.ChangeType(result, typeof(T));
}
public override string ToString()
{
- return Root.ToString();
- }
-
- internal int Parse(string source, int i)
- {
- _source = source;
- Parse(i);
- return _index;
- }
-
- private void Parse(int i)
- {
- _stream.Clear();
- _startIndex = i + 1;
- _index = i;
- _done = false;
- try
- {
- _stateMachine.Run(this, State.Start, _stream);
- if (!_done)
- throw new JsonPathSyntaxException(GetErrorLocation(), "Found incomplete expression.");
- }
- catch (InputNotValidForStateException e)
- {
- switch (e.State)
- {
- case State.Start:
- throw new JsonPathSyntaxException(GetErrorLocation(), "Expression must start with '('.");
- case State.Value:
- throw new JsonPathSyntaxException(GetErrorLocation(), "Expected an expression, a value, or a path which evaluates to a value.");
- case State.NumberOrPath:
- throw new JsonPathSyntaxException(GetErrorLocation(), "Expected an expression, a value, or a path which evaluates to a value.");
- case State.Operator:
- throw new JsonPathSyntaxException(GetErrorLocation(), "Expected an operator.");
- case State.Comparison:
- throw new JsonPathSyntaxException(GetErrorLocation(), "Invalid comparison operator.");
- case State.ValueOrOperator:
- if (e.Input == JsonPathExpressionInput.CloseParenth)
- throw new JsonPathSyntaxException(GetErrorLocation(), "Found incomplete expression.");
- throw new JsonPathSyntaxException(GetErrorLocation(), "Invalid comparison operator.");
- case State.BooleanOrComparison:
- throw new JsonPathSyntaxException(GetErrorLocation(), "Expected '=', a boolean value, or an expression which evaluates to a boolean.");
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- catch (StateNotValidException)
- {
- throw new JsonPathSyntaxException(GetErrorLocation(), "An unrecoverable error occurred while parsing a JSON path expression. Please report to littlecrabsolutions@yahoo.com.");
- }
- catch (ActionNotDefinedForStateAndInputException)
- {
- throw new JsonPathSyntaxException(GetErrorLocation(), "An unrecoverable error occurred while parsing a JSON path expression. Please report to littlecrabsolutions@yahoo.com.");
- }
- }
- private string GetErrorLocation()
- {
- var length = _index - _startIndex - 1;
- return length < 1 ? string.Empty : _source.Substring(_startIndex, length);
- }
- private static void GetNextInput(object owner)
- {
- var obj = owner as Expression;
- if (obj == null) return;
- if (obj._done || (obj._index == obj._source.Length)) return;
- var c = default(char);
- try
- {
- c = obj._source[obj._index++];
- var next = CharacterConverter.ExpressionItem(c);
- obj._stream.Add(next);
- }
- catch (KeyNotFoundException)
- {
- throw new JsonPathSyntaxException(obj.GetErrorLocation(), "Unrecognized character '{0}' in input string.", c);
- }
- }
- private static State GotStart(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- exp._nodeList = new List>();
- return State.Value;
- }
- private static State GotGroup(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- CheckComparison(exp);
- var group = new Expression();
- exp._index = group.Parse(exp._source, exp._index - 1);
- var last = exp._nodeList.LastOrDefault() as IndexOfExpression;
- if (last != null)
- last.ParameterExpression = group.Root as ExpressionTreeNode;
- else
- exp._nodeList.Add(new GroupExpression {Group = group.Root});
- return State.Operator;
- }
- private static State CompleteExpression(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- exp.Root = BuildTree(exp._nodeList);
- exp._nodeList = null;
- exp._done = true;
- return State.End;
- }
- private static State GotNumber(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- CheckComparison(exp);
- var numString = new string(exp._source.Skip(exp._index - 1).TakeWhile(char.IsNumber).ToArray());
- double num;
- if (!double.TryParse(numString, out num))
- throw new JsonPathSyntaxException("Attempt to parse '{0}' as a number failed.", numString);
- exp._index += numString.Length - 1;
- exp._nodeList.Add(new ValueExpression {Value = num});
- return State.Operator;
- }
- private static State GotRoot(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- CheckComparison(exp);
- var path = new JsonPath();
- exp._index = path.Parse(exp._source, exp._index - 1) - 1;
- var name = path.Operators.Last() as NameOperator;
- var indexOf = path.Operators.Last() as IndexOfOperator;
- if (name != null && name.Name == "length")
- {
- path.Operators.Remove(name);
- exp._nodeList.Add(new LengthExpression {Path = path});
- }
- else if (indexOf != null)
- {
- path.Operators.Remove(indexOf);
- exp._nodeList.Add(new IndexOfExpression { Path = path, IsLocal = true, ParameterExpression = indexOf.Parameter.Root });
- }
- else
- exp._nodeList.Add(new PathExpression {Path = path});
- return State.Operator;
- }
- private static State GotCurrent(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- CheckComparison(exp);
- var path = new JsonPath();
- exp._index = path.Parse(exp._source, exp._index - 1) - 1;
- var name = path.Operators.Last() as NameOperator;
- var indexOf = path.Operators.Last() as IndexOfOperator;
- if (name != null && name.Name == "length")
- {
- path.Operators.Remove(name);
- exp._nodeList.Add(new LengthExpression {Path = path, IsLocal = true});
- }
- else if (indexOf != null)
- {
- path.Operators.Remove(indexOf);
- exp._nodeList.Add(new IndexOfExpression {Path = path, IsLocal = true, ParameterExpression = indexOf.Parameter.Root});
- }
- else
- exp._nodeList.Add(new PathExpression {Path = path, IsLocal = true});
- return State.Operator;
- }
- private static State GotString(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- CheckComparison(exp);
- var value = new string(exp._source.Skip(exp._index).TakeWhile(c => c != '"').ToArray());
- exp._index += value.Length+1;
- exp._nodeList.Add(new ValueExpression {Value = value});
- return State.Operator;
+ return _root.ToString();
}
- private static State GotPlus(object owner, JsonPathExpressionInput input)
+ public bool Equals(Expression other)
{
- var exp = owner as Expression;
- exp._nodeList.Add(new AddExpression());
- return State.Value;
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Equals(_root, other._root);
}
- private static State GotMinus(object owner, JsonPathExpressionInput input)
+ public override bool Equals(object obj)
{
- var exp = owner as Expression;
- exp._nodeList.Add(new SubtractExpression());
- return State.Value;
+ return Equals(obj as Expression);
}
- private static State GotMultiply(object owner, JsonPathExpressionInput input)
+ public override int GetHashCode()
{
- var exp = owner as Expression;
- exp._nodeList.Add(new MultiplyExpression());
- return State.Value;
- }
- private static State GotDivide(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- exp._nodeList.Add(new DivideExpression());
- return State.Value;
- }
- private static State GotExponent(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- exp._nodeList.Add(new ExponentExpression());
- return State.Value;
- }
- private static State GotLessThan(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- exp._previousInput = JsonPathExpressionInput.LessThan;
- return State.ValueOrOperator;
- }
- private static State GotEqual(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- if (exp._previousInput.HasValue)
- {
- switch (exp._previousInput)
- {
- case JsonPathExpressionInput.Equal:
- exp._nodeList.Add(new IsEqualExpression());
- break;
- case JsonPathExpressionInput.Bang:
- exp._nodeList.Add(new IsNotEqualExpression());
- break;
- default:
- throw new JsonPathSyntaxException(exp.GetErrorLocation(), "Operator '={0}' not recognized.", exp._source[exp._index - 1]);
- }
- exp._previousInput = null;
- return State.Value;
- }
- exp._previousInput = JsonPathExpressionInput.Equal;
- return State.Comparison;
- }
- private static State GotAnd(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- if (exp._previousInput.HasValue)
- {
- if (exp._previousInput == JsonPathExpressionInput.And)
- exp._nodeList.Add(new AndExpression());
- else
- throw new JsonPathSyntaxException(exp.GetErrorLocation(), "Operator '&{0}' not recognized.", exp._source[exp._index - 1]);
- exp._previousInput = null;
- return State.Value;
- }
- exp._previousInput = JsonPathExpressionInput.And;
- return State.Comparison;
- }
- private static State GotOr(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- if (exp._previousInput.HasValue)
- {
- if (exp._previousInput == JsonPathExpressionInput.Or)
- exp._nodeList.Add(new OrExpression());
- else
- throw new JsonPathSyntaxException(exp.GetErrorLocation(), "Operator '|{0}' not recognized.", exp._source[exp._index - 1]);
- exp._previousInput = null;
- return State.Value;
- }
- exp._previousInput = JsonPathExpressionInput.Or;
- return State.Comparison;
- }
- private static State GotGreaterThan(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- exp._previousInput = JsonPathExpressionInput.GreaterThan;
- return State.ValueOrOperator;
- }
- private static State GotNot(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- exp._previousInput = JsonPathExpressionInput.Bang;
- return State.BooleanOrComparison;
- }
- private static State GotNamedConstant(object owner, JsonPathExpressionInput input)
- {
- ExpressionTreeNode bang = null;
- var exp = owner as Expression;
- if (exp._previousInput.HasValue)
- {
- bang = new NotExpression();
- exp._previousInput = null;
- }
- object value;
- var substring = exp._source.Substring(exp._index - 1).ToLower();
- if (substring.StartsWith("true"))
- {
- if (bang != null)
- exp._nodeList.Add(bang);
- value = true;
- exp._index += 3;
- }
- else if (substring.StartsWith("false"))
- {
- if (bang != null)
- exp._nodeList.Add(bang);
- value = false;
- exp._index += 4;
- }
- else if (substring.StartsWith("null"))
- {
- if (bang != null)
- throw new JsonPathSyntaxException(exp.GetErrorLocation(), "Cannot apply '!' operator to 'null'.");
- value = JsonValue.Null;
- exp._index += 3;
- }
- else
- {
- var constant = new string(substring.TakeWhile(char.IsLetterOrDigit).ToArray());
- throw new JsonPathSyntaxException(exp.GetErrorLocation(), "Constant value '{0}' not recognized", constant);
- }
- exp._nodeList.Add(new ValueExpression {Value = value});
- return State.Operator;
- }
- private static State FinalizeComparison(object owner, JsonPathExpressionInput input)
- {
- var exp = owner as Expression;
- switch (input)
- {
- case JsonPathExpressionInput.LessThan:
- exp._nodeList.Add(new IsLessThanExpression());
- break;
- case JsonPathExpressionInput.Equal:
- switch (exp._previousInput)
- {
- case JsonPathExpressionInput.LessThan:
- exp._nodeList.Add(new IsLessThanEqualExpression());
- break;
- case JsonPathExpressionInput.GreaterThan:
- exp._nodeList.Add(new IsGreaterThanEqualExpression());
- break;
- case JsonPathExpressionInput.Bang:
- exp._nodeList.Add(new IsNotEqualExpression());
- break;
- default:
- throw new JsonPathSyntaxException(exp.GetErrorLocation(), "Operator '{0}=' not recognized.", exp._source[exp._index - 2]);
- }
- break;
- case JsonPathExpressionInput.GreaterThan:
- exp._nodeList.Add(new IsGreaterThanExpression());
- break;
- case JsonPathExpressionInput.Bang:
- exp._nodeList.Add(new NotExpression());
- break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- exp._previousInput = null;
- return State.NumberOrPath;
- }
- private static void CheckComparison(Expression exp)
- {
- if (exp._previousInput.HasValue)
- {
- var previous = exp._previousInput.Value;
- exp._previousInput = null;
- FinalizeComparison(exp, previous);
- }
- }
- private static ExpressionTreeNode BuildTree(IList> nodes)
- {
- if (!nodes.Any()) return null;
- var minPriority = nodes.Min(n => n.Priority);
- var root = nodes.Last(n => n.Priority == minPriority);
- var branch = root as ExpressionTreeBranch;
- if (branch != null)
- {
- var split = nodes.IndexOf(root);
- var left = nodes.Take(split).ToList();
- var right = nodes.Skip(split + 1).ToList();
- branch.Left = BuildTree(left);
- branch.Right = BuildTree(right);
- }
- var not = root as NotExpression;
- if (not != null)
- {
- var split = nodes.IndexOf(root);
- var right = nodes.Skip(split + 1).FirstOrDefault();
- not.Root = right;
- }
- return root;
+ return _root?.GetHashCode() ?? 0;
}
}
}
diff --git a/Manatee.Json/Path/Expressions/ExpressionTreeBranch.cs b/Manatee.Json/Path/Expressions/ExpressionTreeBranch.cs
index 696b9fe..08179d3 100644
--- a/Manatee.Json/Path/Expressions/ExpressionTreeBranch.cs
+++ b/Manatee.Json/Path/Expressions/ExpressionTreeBranch.cs
@@ -26,5 +26,18 @@ internal abstract class ExpressionTreeBranch : ExpressionTreeNode
{
public ExpressionTreeNode Left { get; set; }
public ExpressionTreeNode Right { get; set; }
+
+ protected bool Equals(ExpressionTreeBranch other)
+ {
+ return Equals(Left, other.Left) && Equals(Right, other.Right);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as ExpressionTreeBranch);
+ }
+ public override int GetHashCode()
+ {
+ unchecked { return ((Left?.GetHashCode() ?? 0)*397) ^ (Right?.GetHashCode() ?? 0); }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/ExpressionTreeNode.cs b/Manatee.Json/Path/Expressions/ExpressionTreeNode.cs
index dbe2d84..a4f16d0 100644
--- a/Manatee.Json/Path/Expressions/ExpressionTreeNode.cs
+++ b/Manatee.Json/Path/Expressions/ExpressionTreeNode.cs
@@ -24,8 +24,17 @@ namespace Manatee.Json.Path.Expressions
{
internal abstract class ExpressionTreeNode
{
- public abstract int Priority { get; }
+ private int _priorityBump;
+
+ public int Priority => BasePriority + _priorityBump;
+
+ protected abstract int BasePriority { get; }
public abstract object Evaluate(T json, JsonValue root);
+
+ public void BumpPriority()
+ {
+ _priorityBump += 10;
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/FieldExpression.cs b/Manatee.Json/Path/Expressions/FieldExpression.cs
index 27ddcde..a03197c 100644
--- a/Manatee.Json/Path/Expressions/FieldExpression.cs
+++ b/Manatee.Json/Path/Expressions/FieldExpression.cs
@@ -26,15 +26,19 @@
namespace Manatee.Json.Path.Expressions
{
- internal class FieldExpression : ExpressionTreeNode
+ internal class FieldExpression : ExpressionTreeNode, IEquatable>
{
- public override int Priority => 6;
public FieldInfo Field { get; set; }
public object Source { get; set; }
+ protected override int BasePriority => 6;
+
public override object Evaluate(T json, JsonValue root)
{
- if (Field.FieldType == typeof(string))
+ if (Field.FieldType == typeof(string) ||
+ Field.FieldType == typeof(JsonArray) ||
+ Field.FieldType == typeof(JsonObject) ||
+ Field.FieldType == typeof(JsonValue))
return Field.GetValue(Source);
return Convert.ToDouble(Field.GetValue(Source));
}
@@ -45,5 +49,19 @@ public override string ToString()
? $"\"{value}\""
: value?.ToString() ?? "null";
}
+ public bool Equals(FieldExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Equals(Field, other.Field) && Equals(Source, other.Source);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as FieldExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked { return ((Field != null ? Field.GetHashCode() : 0)*397) ^ (Source?.GetHashCode() ?? 0); }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/HasPropertyExpression.cs b/Manatee.Json/Path/Expressions/HasPropertyExpression.cs
index 0b79936..79d7bec 100644
--- a/Manatee.Json/Path/Expressions/HasPropertyExpression.cs
+++ b/Manatee.Json/Path/Expressions/HasPropertyExpression.cs
@@ -24,11 +24,12 @@
namespace Manatee.Json.Path.Expressions
{
- internal class HasPropertyExpression : ExpressionTreeNode
+ internal class HasPropertyExpression : PathExpression, IEquatable>
{
- public override int Priority => 6;
public string Name { get; set; }
+ protected override int BasePriority => 6;
+
public override object Evaluate(T json, JsonValue root)
{
var value = json as JsonValue;
@@ -43,5 +44,24 @@ public override string ToString()
{
return $"@.{Name}";
}
+ public bool Equals(HasPropertyExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other) && string.Equals(Name, other.Name);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as HasPropertyExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ (Name?.GetHashCode() ?? 0);
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/IndexOfExpression.cs b/Manatee.Json/Path/Expressions/IndexOfExpression.cs
index 3224b0a..65f8f29 100644
--- a/Manatee.Json/Path/Expressions/IndexOfExpression.cs
+++ b/Manatee.Json/Path/Expressions/IndexOfExpression.cs
@@ -22,18 +22,20 @@
***************************************************************************************/
using System;
using System.Linq;
+using Manatee.Json.Internal;
namespace Manatee.Json.Path.Expressions
{
- internal class IndexOfExpression : PathExpression
+ internal class IndexOfExpression : PathExpression, IEquatable>
{
- public override int Priority => 6;
public JsonValue Parameter { get; set; }
public ExpressionTreeNode ParameterExpression { get; set; }
+ protected override int BasePriority => 6;
+
public override object Evaluate(T json, JsonValue root)
{
- var value = IsLocal ? json as JsonValue : root;
+ var value = IsLocal ? json.AsJsonValue() : root;
if (value == null)
throw new NotSupportedException("IndexOf requires a JsonValue to evaluate.");
var results = Path.Evaluate(value);
@@ -51,6 +53,26 @@ public override string ToString()
var parameter = ParameterExpression?.ToString() ?? Parameter.ToString();
return string.Format(IsLocal ? "@{0}.indexOf({1})" : "${0}.indexOf({1})", path, parameter);
}
+ public bool Equals(IndexOfExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other) && Equals(ParameterExpression, other.ParameterExpression);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as IndexOfExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode*397) ^ (Parameter != null ? Parameter.GetHashCode() : 0);
+ hashCode = (hashCode*397) ^ (ParameterExpression?.GetHashCode() ?? 0);
+ return hashCode;
+ }
+ }
private JsonValue GetParameter()
{
diff --git a/Manatee.Json/Path/Expressions/IsEqualExpression.cs b/Manatee.Json/Path/Expressions/IsEqualExpression.cs
index 6a90e1d..3221031 100644
--- a/Manatee.Json/Path/Expressions/IsEqualExpression.cs
+++ b/Manatee.Json/Path/Expressions/IsEqualExpression.cs
@@ -25,9 +25,9 @@
namespace Manatee.Json.Path.Expressions
{
- internal class IsEqualExpression : ExpressionTreeBranch
+ internal class IsEqualExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 1;
+ protected override int BasePriority => 1;
public override object Evaluate(T json, JsonValue root)
{
@@ -41,5 +41,24 @@ public override string ToString()
{
return $"{Left} == {Right}";
}
+ public bool Equals(IsEqualExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as IsEqualExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/IsGreaterThanEqualExpression.cs b/Manatee.Json/Path/Expressions/IsGreaterThanEqualExpression.cs
index fa55ed7..8f1e0e4 100644
--- a/Manatee.Json/Path/Expressions/IsGreaterThanEqualExpression.cs
+++ b/Manatee.Json/Path/Expressions/IsGreaterThanEqualExpression.cs
@@ -21,11 +21,13 @@
***************************************************************************************/
+using System;
+
namespace Manatee.Json.Path.Expressions
{
- internal class IsGreaterThanEqualExpression : ExpressionTreeBranch
+ internal class IsGreaterThanEqualExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 1;
+ protected override int BasePriority => 1;
public override object Evaluate(T json, JsonValue root)
{
@@ -37,5 +39,24 @@ public override string ToString()
{
return $"{Left} >= {Right}";
}
+ public bool Equals(IsGreaterThanEqualExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as IsGreaterThanEqualExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/IsGreaterThanExpression.cs b/Manatee.Json/Path/Expressions/IsGreaterThanExpression.cs
index 7f6b819..93d9ba2 100644
--- a/Manatee.Json/Path/Expressions/IsGreaterThanExpression.cs
+++ b/Manatee.Json/Path/Expressions/IsGreaterThanExpression.cs
@@ -21,11 +21,13 @@
***************************************************************************************/
+using System;
+
namespace Manatee.Json.Path.Expressions
{
- internal class IsGreaterThanExpression : ExpressionTreeBranch
+ internal class IsGreaterThanExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 1;
+ protected override int BasePriority => 1;
public override object Evaluate(T json, JsonValue root)
{
@@ -37,5 +39,25 @@ public override string ToString()
{
return $"{Left} > {Right}";
}
+ public bool Equals(IsGreaterThanExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((IsGreaterThanExpression) obj);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/IsLessThanEqualExpression.cs b/Manatee.Json/Path/Expressions/IsLessThanEqualExpression.cs
index 5819946..cdfa826 100644
--- a/Manatee.Json/Path/Expressions/IsLessThanEqualExpression.cs
+++ b/Manatee.Json/Path/Expressions/IsLessThanEqualExpression.cs
@@ -21,11 +21,13 @@
***************************************************************************************/
+using System;
+
namespace Manatee.Json.Path.Expressions
{
- internal class IsLessThanEqualExpression : ExpressionTreeBranch
+ internal class IsLessThanEqualExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 1;
+ protected override int BasePriority => 1;
public override object Evaluate(T json, JsonValue root)
{
@@ -37,5 +39,24 @@ public override string ToString()
{
return $"{Left} <= {Right}";
}
+ public bool Equals(IsLessThanEqualExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as IsLessThanEqualExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/IsLessThanExpression.cs b/Manatee.Json/Path/Expressions/IsLessThanExpression.cs
index df9b8e2..a1952d0 100644
--- a/Manatee.Json/Path/Expressions/IsLessThanExpression.cs
+++ b/Manatee.Json/Path/Expressions/IsLessThanExpression.cs
@@ -21,11 +21,13 @@
***************************************************************************************/
+using System;
+
namespace Manatee.Json.Path.Expressions
{
- internal class IsLessThanExpression : ExpressionTreeBranch
+ internal class IsLessThanExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 1;
+ protected override int BasePriority => 1;
public override object Evaluate(T json, JsonValue root)
{
@@ -37,5 +39,24 @@ public override string ToString()
{
return $"{Left} < {Right}";
}
+ public bool Equals(IsLessThanExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as IsLessThanExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/IsNotEqualExpression.cs b/Manatee.Json/Path/Expressions/IsNotEqualExpression.cs
index cc0090f..d7c36cc 100644
--- a/Manatee.Json/Path/Expressions/IsNotEqualExpression.cs
+++ b/Manatee.Json/Path/Expressions/IsNotEqualExpression.cs
@@ -25,9 +25,9 @@
namespace Manatee.Json.Path.Expressions
{
- internal class IsNotEqualExpression : ExpressionTreeBranch
+ internal class IsNotEqualExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 1;
+ protected override int BasePriority => 1;
public override object Evaluate(T json, JsonValue root)
{
@@ -41,5 +41,24 @@ public override string ToString()
{
return $"{Left} != {Right}";
}
+ public bool Equals(IsNotEqualExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as IsNotEqualExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/LengthExpression.cs b/Manatee.Json/Path/Expressions/LengthExpression.cs
index e42d716..c50bf9b 100644
--- a/Manatee.Json/Path/Expressions/LengthExpression.cs
+++ b/Manatee.Json/Path/Expressions/LengthExpression.cs
@@ -25,9 +25,9 @@
namespace Manatee.Json.Path.Expressions
{
- internal class LengthExpression : PathExpression
+ internal class LengthExpression : PathExpression, IEquatable>
{
- public override int Priority => 6;
+ protected override int BasePriority => 6;
public override object Evaluate(T json, JsonValue root)
{
@@ -66,5 +66,24 @@ public override string ToString()
var path = Path == null ? string.Empty : Path.GetRawString();
return (IsLocal ? "@" : "$") + $"{path}.length";
}
+ public bool Equals(LengthExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as LengthExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/ModuloExpression.cs b/Manatee.Json/Path/Expressions/ModuloExpression.cs
index 6e6d6a7..93f846d 100644
--- a/Manatee.Json/Path/Expressions/ModuloExpression.cs
+++ b/Manatee.Json/Path/Expressions/ModuloExpression.cs
@@ -20,11 +20,13 @@
Purpose: Expresses the intent to calculate the modulo of two numbers.
***************************************************************************************/
+using System;
+
namespace Manatee.Json.Path.Expressions
{
- internal class ModuloExpression : ExpressionTreeBranch
+ internal class ModuloExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 2;
+ protected override int BasePriority => 2;
public override object Evaluate(T json, JsonValue root)
{
@@ -39,5 +41,24 @@ public override string ToString()
var right = Right.Priority <= Priority ? $"({Right})" : Right.ToString();
return $"{left}%{right}";
}
+ public bool Equals(ModuloExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as ModuloExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/MultiplyExpression.cs b/Manatee.Json/Path/Expressions/MultiplyExpression.cs
index 5296dd7..3892ce0 100644
--- a/Manatee.Json/Path/Expressions/MultiplyExpression.cs
+++ b/Manatee.Json/Path/Expressions/MultiplyExpression.cs
@@ -21,11 +21,13 @@
***************************************************************************************/
+using System;
+
namespace Manatee.Json.Path.Expressions
{
- internal class MultiplyExpression : ExpressionTreeBranch
+ internal class MultiplyExpression : ExpressionTreeBranch, IEquatable>
{
- public override int Priority => 3;
+ protected override int BasePriority => 3;
public override object Evaluate(T json, JsonValue root)
{
@@ -40,5 +42,24 @@ public override string ToString()
var right = Right.Priority <= Priority ? $"({Right})" : Right.ToString();
return $"{left}*{right}";
}
+ public bool Equals(MultiplyExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as MultiplyExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode * 397) ^ GetType().GetHashCode();
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/NameExpresssion.cs b/Manatee.Json/Path/Expressions/NameExpresssion.cs
index 2494677..83bdb8b 100644
--- a/Manatee.Json/Path/Expressions/NameExpresssion.cs
+++ b/Manatee.Json/Path/Expressions/NameExpresssion.cs
@@ -23,18 +23,20 @@
***************************************************************************************/
using System;
using System.Linq;
+using Manatee.Json.Internal;
namespace Manatee.Json.Path.Expressions
{
- internal class NameExpression : PathExpression
+ internal class NameExpression : PathExpression, IEquatable>
{
- public override int Priority => 6;
public string Name { get; set; }
public ExpressionTreeNode NameExp { get; set; }
+ protected override int BasePriority => 6;
+
public override object Evaluate(T json, JsonValue root)
{
- var value = IsLocal ? json as JsonValue : root;
+ var value = IsLocal ? json.AsJsonValue() : root;
if (value == null)
throw new NotSupportedException("Name requires a JsonValue to evaluate.");
var results = Path.Evaluate(value);
@@ -59,5 +61,25 @@ private string GetName()
return (string)value;
return Name;
}
+ public bool Equals(NameExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return base.Equals(other) && string.Equals(Name, other.Name) && Equals(NameExp, other.NameExp);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as NameExpression);
+ }
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ int hashCode = base.GetHashCode();
+ hashCode = (hashCode*397) ^ (Name?.GetHashCode() ?? 0);
+ hashCode = (hashCode*397) ^ (NameExp?.GetHashCode() ?? 0);
+ return hashCode;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/NegateExpression.cs b/Manatee.Json/Path/Expressions/NegateExpression.cs
index ab21e68..77deaec 100644
--- a/Manatee.Json/Path/Expressions/NegateExpression.cs
+++ b/Manatee.Json/Path/Expressions/NegateExpression.cs
@@ -24,11 +24,12 @@
namespace Manatee.Json.Path.Expressions
{
- internal class NegateExpression : ExpressionTreeNode
+ internal class NegateExpression : ExpressionTreeNode, IEquatable>
{
- public override int Priority => 6;
public ExpressionTreeNode Root { get; set; }
+ protected override int BasePriority => 6;
+
public override object Evaluate(T json, JsonValue root)
{
return -Convert.ToDouble(Root.Evaluate(json, root));
@@ -39,5 +40,19 @@ public override string ToString()
? $"-({Root})"
: $"-{Root}";
}
+ public bool Equals(NegateExpression other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Equals(Root, other.Root);
+ }
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as NegateExpression);
+ }
+ public override int GetHashCode()
+ {
+ return Root?.GetHashCode() ?? 0;
+ }
}
}
\ No newline at end of file
diff --git a/Manatee.Json/Path/Expressions/NotExpression.cs b/Manatee.Json/Path/Expressions/NotExpression.cs
index 6a79a21..3c5d550 100644
--- a/Manatee.Json/Path/Expressions/NotExpression.cs
+++ b/Manatee.Json/Path/Expressions/NotExpression.cs
@@ -20,13 +20,16 @@
Purpose: Expresses the intent to invert a boolean.
***************************************************************************************/
+using System;
+
namespace Manatee.Json.Path.Expressions
{
- internal class NotExpression : ExpressionTreeNode
+ internal class NotExpression