-JsonPath++
This commit is contained in:
+51
-210
@@ -25,6 +25,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq.JsonPath;
|
||||
#if !NETFX_CORE
|
||||
using NUnit.Framework;
|
||||
#else
|
||||
@@ -40,219 +41,11 @@ using System.Linq;
|
||||
|
||||
#endif
|
||||
|
||||
namespace Newtonsoft.Json.Tests.Linq
|
||||
namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
{
|
||||
[TestFixture]
|
||||
public class JPathTests : TestFixtureBase
|
||||
public class JPathExecuteTests : TestFixtureBase
|
||||
{
|
||||
[Test]
|
||||
public void SingleProperty()
|
||||
{
|
||||
JPath path = new JPath("Blah");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Parts[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$.Blah");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Parts[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WildcardPropertyWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$.*");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual(null, ((FieldFilter)path.Parts[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WildcardArrayWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$.[*]");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual(null, ((ArrayIndexFilter)path.Parts[0]).Index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WildcardArray()
|
||||
{
|
||||
JPath path = new JPath("[*]");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual(null, ((ArrayIndexFilter)path.Parts[0]).Index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WildcardArrayWithProperty()
|
||||
{
|
||||
JPath path = new JPath("[*].derp");
|
||||
Assert.AreEqual(2, path.Parts.Count);
|
||||
Assert.AreEqual(null, ((ArrayIndexFilter)path.Parts[0]).Index);
|
||||
Assert.AreEqual("derp", ((FieldFilter)path.Parts[1]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QuotedWildcardPropertyWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$.['*']");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual("*", ((FieldFilter)path.Parts[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SingleScanWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$..Blah");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual("Blah", ((ScanFilter)path.Parts[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WildcardScanWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$..*");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual(null, ((ScanFilter)path.Parts[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TwoProperties()
|
||||
{
|
||||
JPath path = new JPath("Blah.Two");
|
||||
Assert.AreEqual(2, path.Parts.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Parts[0]).Name);
|
||||
Assert.AreEqual("Two", ((FieldFilter)path.Parts[1]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnePropertyOneScan()
|
||||
{
|
||||
JPath path = new JPath("Blah..Two");
|
||||
Assert.AreEqual(2, path.Parts.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Parts[0]).Name);
|
||||
Assert.AreEqual("Two", ((ScanFilter)path.Parts[1]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndIndexer()
|
||||
{
|
||||
JPath path = new JPath("Blah[0]");
|
||||
Assert.AreEqual(2, path.Parts.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Parts[0]).Name);
|
||||
Assert.AreEqual(0, ((ArrayIndexFilter)path.Parts[1]).Index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndFilter()
|
||||
{
|
||||
JPath path = new JPath("Blah[?(@.name=='hi')]");
|
||||
Assert.AreEqual(2, path.Parts.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Parts[0]).Name);
|
||||
List<object> expressions = ((QueryFilter)path.Parts[1]).Expression;
|
||||
Assert.AreEqual(1, expressions.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultiplePropertiesAndIndexers()
|
||||
{
|
||||
JPath path = new JPath("Blah[0]..Two.Three[1].Four");
|
||||
Assert.AreEqual(6, path.Parts.Count);
|
||||
Assert.AreEqual(FilterType.Field, path.Parts[0].Type);
|
||||
Assert.AreEqual("Blah", ((FieldFilter) path.Parts[0]).Name);
|
||||
Assert.AreEqual(FilterType.ArrayIndex, path.Parts[1].Type);
|
||||
Assert.AreEqual(0, ((ArrayIndexFilter) path.Parts[1]).Index);
|
||||
Assert.AreEqual(FilterType.Scan, path.Parts[2].Type);
|
||||
Assert.AreEqual("Two", ((ScanFilter)path.Parts[2]).Name);
|
||||
Assert.AreEqual("Three", ((FieldFilter)path.Parts[3]).Name);
|
||||
Assert.AreEqual(1, ((ArrayIndexFilter)path.Parts[4]).Index);
|
||||
Assert.AreEqual("Four", ((FieldFilter)path.Parts[5]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BadCharactersInIndexer()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
@"Unexpected character while parsing path indexer: [",
|
||||
() => { new JPath("Blah[[0]].Two.Three[1].Four"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnclosedIndexer()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
@"Path ended with open indexer.",
|
||||
() => { new JPath("Blah[0"); });
|
||||
}
|
||||
|
||||
//[Test]
|
||||
//public void AdditionalDots()
|
||||
//{
|
||||
// JPath path = new JPath(".Blah..[0]..Two.Three....[1].Four.");
|
||||
// Assert.AreEqual(6, path.Parts.Count);
|
||||
// Assert.AreEqual("Blah", path.Parts[0]);
|
||||
// Assert.AreEqual(0, path.Parts[1]);
|
||||
// Assert.AreEqual("Two", path.Parts[2]);
|
||||
// Assert.AreEqual("Three", path.Parts[3]);
|
||||
// Assert.AreEqual(1, path.Parts[4]);
|
||||
// Assert.AreEqual("Four", path.Parts[5]);
|
||||
//}
|
||||
|
||||
[Test]
|
||||
public void IndexerOnly()
|
||||
{
|
||||
JPath path = new JPath("[111119990]");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual(111119990, ((ArrayIndexFilter)path.Parts[0]).Index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultipleIndexes()
|
||||
{
|
||||
JPath path = new JPath("[111119990,3]");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual(2, ((ArrayMultipleIndexFilter)path.Parts[0]).Indexes.Count);
|
||||
Assert.AreEqual(111119990, ((ArrayMultipleIndexFilter)path.Parts[0]).Indexes[0]);
|
||||
Assert.AreEqual(3, ((ArrayMultipleIndexFilter)path.Parts[0]).Indexes[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EmptyIndexer()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
"Array index expected.",
|
||||
() => { new JPath("[]"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexerCloseInProperty()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
"Unexpected character while parsing path: ]",
|
||||
() => { new JPath("]"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AdjacentIndexers()
|
||||
{
|
||||
JPath path = new JPath("[1][0][0][" + int.MaxValue + "]");
|
||||
Assert.AreEqual(4, path.Parts.Count);
|
||||
Assert.AreEqual(1, ((ArrayIndexFilter)path.Parts[0]).Index);
|
||||
Assert.AreEqual(0, ((ArrayIndexFilter)path.Parts[1]).Index);
|
||||
Assert.AreEqual(0, ((ArrayIndexFilter)path.Parts[2]).Index);
|
||||
Assert.AreEqual(int.MaxValue, ((ArrayIndexFilter)path.Parts[3]).Index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MissingDotAfterIndexer()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
"Unexpected character following indexer: B",
|
||||
() => { new JPath("[1]Blah"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateSingleProperty()
|
||||
{
|
||||
@@ -392,6 +185,54 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
Assert.AreEqual(2, (int)t);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateArraySlice()
|
||||
{
|
||||
JArray a = new JArray(1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||
IList<JToken> t = null;
|
||||
|
||||
t = a.SelectTokens("[-3:]").ToList();
|
||||
Assert.AreEqual(3, t.Count);
|
||||
Assert.AreEqual(7, (int)t[0]);
|
||||
Assert.AreEqual(8, (int)t[1]);
|
||||
Assert.AreEqual(9, (int)t[2]);
|
||||
|
||||
t = a.SelectTokens("[-1:-2:-1]").ToList();
|
||||
Assert.AreEqual(1, t.Count);
|
||||
Assert.AreEqual(9, (int)t[0]);
|
||||
|
||||
t = a.SelectTokens("[-2:-1]").ToList();
|
||||
Assert.AreEqual(1, t.Count);
|
||||
Assert.AreEqual(8, (int)t[0]);
|
||||
|
||||
t = a.SelectTokens("[1:1]").ToList();
|
||||
Assert.AreEqual(0, t.Count);
|
||||
|
||||
t = a.SelectTokens("[1:2]").ToList();
|
||||
Assert.AreEqual(1, t.Count);
|
||||
Assert.AreEqual(2, (int)t[0]);
|
||||
|
||||
t = a.SelectTokens("[::-1]").ToList();
|
||||
Assert.AreEqual(9, t.Count);
|
||||
Assert.AreEqual(9, (int)t[0]);
|
||||
Assert.AreEqual(8, (int)t[1]);
|
||||
Assert.AreEqual(7, (int)t[2]);
|
||||
Assert.AreEqual(6, (int)t[3]);
|
||||
Assert.AreEqual(5, (int)t[4]);
|
||||
Assert.AreEqual(4, (int)t[5]);
|
||||
Assert.AreEqual(3, (int)t[6]);
|
||||
Assert.AreEqual(2, (int)t[7]);
|
||||
Assert.AreEqual(1, (int)t[8]);
|
||||
|
||||
t = a.SelectTokens("[::-2]").ToList();
|
||||
Assert.AreEqual(5, t.Count);
|
||||
Assert.AreEqual(9, (int)t[0]);
|
||||
Assert.AreEqual(7, (int)t[1]);
|
||||
Assert.AreEqual(5, (int)t[2]);
|
||||
Assert.AreEqual(3, (int)t[3]);
|
||||
Assert.AreEqual(1, (int)t[4]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateWildcardArray()
|
||||
{
|
||||
@@ -0,0 +1,383 @@
|
||||
#region License
|
||||
// Copyright (c) 2007 James Newton-King
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use,
|
||||
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following
|
||||
// conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
// OTHER DEALINGS IN THE SOFTWARE.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq.JsonPath;
|
||||
#if !NETFX_CORE
|
||||
using NUnit.Framework;
|
||||
#else
|
||||
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
|
||||
using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute;
|
||||
using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute;
|
||||
#endif
|
||||
using Newtonsoft.Json.Linq;
|
||||
#if NET20
|
||||
using Newtonsoft.Json.Utilities.LinqBridge;
|
||||
#else
|
||||
using System.Linq;
|
||||
|
||||
#endif
|
||||
|
||||
namespace Newtonsoft.Json.Tests.Linq.JsonPath
|
||||
{
|
||||
[TestFixture]
|
||||
public class JPathParseTests : TestFixtureBase
|
||||
{
|
||||
[Test]
|
||||
public void SingleProperty()
|
||||
{
|
||||
JPath path = new JPath("Blah");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SingleQuotedProperty()
|
||||
{
|
||||
JPath path = new JPath("['Blah']");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SingleQuotedPropertyWithWhitespace()
|
||||
{
|
||||
JPath path = new JPath("[ 'Blah' ]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SingleQuotedPropertyWithDots()
|
||||
{
|
||||
JPath path = new JPath("['Blah.Ha']");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual("Blah.Ha", ((FieldFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SingleQuotedPropertyWithBrackets()
|
||||
{
|
||||
JPath path = new JPath("['[*]']");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual("[*]", ((FieldFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$.Blah");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyWithRootWithStartAndEndWhitespace()
|
||||
{
|
||||
JPath path = new JPath(" $.Blah ");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RootWithBadWhitespace()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
@"Unexpected character while parsing path: ",
|
||||
() => { new JPath("$ .Blah"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RootWithBadWhitespace2()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
@"Unexpected character while parsing path: ",
|
||||
() => { new JPath("$. Blah"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WildcardPropertyWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$.*");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(null, ((FieldFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WildcardArrayWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$.[*]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(null, ((ArrayIndexFilter)path.Filters[0]).Index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WildcardArray()
|
||||
{
|
||||
JPath path = new JPath("[*]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(null, ((ArrayIndexFilter)path.Filters[0]).Index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WildcardArrayWithProperty()
|
||||
{
|
||||
JPath path = new JPath("[ * ].derp");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual(null, ((ArrayIndexFilter)path.Filters[0]).Index);
|
||||
Assert.AreEqual("derp", ((FieldFilter)path.Filters[1]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QuotedWildcardPropertyWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$.['*']");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual("*", ((FieldFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SingleScanWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$..Blah");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((ScanFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WildcardScanWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$..*");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(null, ((ScanFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WildcardScanWithRootWithWhitespace()
|
||||
{
|
||||
JPath path = new JPath("$..* ");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(null, ((ScanFilter)path.Filters[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TwoProperties()
|
||||
{
|
||||
JPath path = new JPath("Blah.Two");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
Assert.AreEqual("Two", ((FieldFilter)path.Filters[1]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnePropertyOneScan()
|
||||
{
|
||||
JPath path = new JPath("Blah..Two");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
Assert.AreEqual("Two", ((ScanFilter)path.Filters[1]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndIndexer()
|
||||
{
|
||||
JPath path = new JPath("Blah[0]");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
Assert.AreEqual(0, ((ArrayIndexFilter)path.Filters[1]).Index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SinglePropertyAndFilter()
|
||||
{
|
||||
JPath path = new JPath("Blah[?(@.name=='hi')]");
|
||||
Assert.AreEqual(2, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter)path.Filters[0]).Name);
|
||||
List<object> expressions = ((QueryFilter)path.Filters[1]).Expression;
|
||||
Assert.AreEqual(1, expressions.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultiplePropertiesAndIndexers()
|
||||
{
|
||||
JPath path = new JPath("Blah[0]..Two.Three[1].Four");
|
||||
Assert.AreEqual(6, path.Filters.Count);
|
||||
Assert.AreEqual("Blah", ((FieldFilter) path.Filters[0]).Name);
|
||||
Assert.AreEqual(0, ((ArrayIndexFilter) path.Filters[1]).Index);
|
||||
Assert.AreEqual("Two", ((ScanFilter)path.Filters[2]).Name);
|
||||
Assert.AreEqual("Three", ((FieldFilter)path.Filters[3]).Name);
|
||||
Assert.AreEqual(1, ((ArrayIndexFilter)path.Filters[4]).Index);
|
||||
Assert.AreEqual("Four", ((FieldFilter)path.Filters[5]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BadCharactersInIndexer()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
@"Unexpected character while parsing path indexer: [",
|
||||
() => { new JPath("Blah[[0]].Two.Three[1].Four"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnclosedIndexer()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
@"Path ended with open indexer.",
|
||||
() => { new JPath("Blah[0"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexerOnly()
|
||||
{
|
||||
JPath path = new JPath("[111119990]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(111119990, ((ArrayIndexFilter)path.Filters[0]).Index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexerOnlyWithWhitespace()
|
||||
{
|
||||
JPath path = new JPath("[ 10 ]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(10, ((ArrayIndexFilter)path.Filters[0]).Index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultipleIndexes()
|
||||
{
|
||||
JPath path = new JPath("[111119990,3]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(2, ((ArrayMultipleIndexFilter)path.Filters[0]).Indexes.Count);
|
||||
Assert.AreEqual(111119990, ((ArrayMultipleIndexFilter)path.Filters[0]).Indexes[0]);
|
||||
Assert.AreEqual(3, ((ArrayMultipleIndexFilter)path.Filters[0]).Indexes[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultipleIndexesWithWhitespace()
|
||||
{
|
||||
JPath path = new JPath("[ 111119990 , 3 ]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(2, ((ArrayMultipleIndexFilter)path.Filters[0]).Indexes.Count);
|
||||
Assert.AreEqual(111119990, ((ArrayMultipleIndexFilter)path.Filters[0]).Indexes[0]);
|
||||
Assert.AreEqual(3, ((ArrayMultipleIndexFilter)path.Filters[0]).Indexes[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SlicingIndexAll()
|
||||
{
|
||||
JPath path = new JPath("[111119990:3:2]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(111119990, ((ArraySliceFilter)path.Filters[0]).Start);
|
||||
Assert.AreEqual(3, ((ArraySliceFilter)path.Filters[0]).End);
|
||||
Assert.AreEqual(2, ((ArraySliceFilter)path.Filters[0]).Step);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SlicingIndex()
|
||||
{
|
||||
JPath path = new JPath("[111119990:3]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(111119990, ((ArraySliceFilter)path.Filters[0]).Start);
|
||||
Assert.AreEqual(3, ((ArraySliceFilter)path.Filters[0]).End);
|
||||
Assert.AreEqual(null, ((ArraySliceFilter)path.Filters[0]).Step);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SlicingIndexNegative()
|
||||
{
|
||||
JPath path = new JPath("[-111119990:-3:-2]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(-111119990, ((ArraySliceFilter)path.Filters[0]).Start);
|
||||
Assert.AreEqual(-3, ((ArraySliceFilter)path.Filters[0]).End);
|
||||
Assert.AreEqual(-2, ((ArraySliceFilter)path.Filters[0]).Step);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SlicingIndexEmptyStop()
|
||||
{
|
||||
JPath path = new JPath("[ -3 : ]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(-3, ((ArraySliceFilter)path.Filters[0]).Start);
|
||||
Assert.AreEqual(null, ((ArraySliceFilter)path.Filters[0]).End);
|
||||
Assert.AreEqual(null, ((ArraySliceFilter)path.Filters[0]).Step);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SlicingIndexEmptyStart()
|
||||
{
|
||||
JPath path = new JPath("[ : 1 : ]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(null, ((ArraySliceFilter)path.Filters[0]).Start);
|
||||
Assert.AreEqual(1, ((ArraySliceFilter)path.Filters[0]).End);
|
||||
Assert.AreEqual(null, ((ArraySliceFilter)path.Filters[0]).Step);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SlicingIndexWhitespace()
|
||||
{
|
||||
JPath path = new JPath("[ -111119990 : -3 : -2 ]");
|
||||
Assert.AreEqual(1, path.Filters.Count);
|
||||
Assert.AreEqual(-111119990, ((ArraySliceFilter)path.Filters[0]).Start);
|
||||
Assert.AreEqual(-3, ((ArraySliceFilter)path.Filters[0]).End);
|
||||
Assert.AreEqual(-2, ((ArraySliceFilter)path.Filters[0]).Step);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EmptyIndexer()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
"Array index expected.",
|
||||
() => { new JPath("[]"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexerCloseInProperty()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
"Unexpected character while parsing path: ]",
|
||||
() => { new JPath("]"); });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AdjacentIndexers()
|
||||
{
|
||||
JPath path = new JPath("[1][0][0][" + int.MaxValue + "]");
|
||||
Assert.AreEqual(4, path.Filters.Count);
|
||||
Assert.AreEqual(1, ((ArrayIndexFilter)path.Filters[0]).Index);
|
||||
Assert.AreEqual(0, ((ArrayIndexFilter)path.Filters[1]).Index);
|
||||
Assert.AreEqual(0, ((ArrayIndexFilter)path.Filters[2]).Index);
|
||||
Assert.AreEqual(int.MaxValue, ((ArrayIndexFilter)path.Filters[3]).Index);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MissingDotAfterIndexer()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
"Unexpected character following indexer: B",
|
||||
() => { new JPath("[1]Blah"); });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,8 @@
|
||||
<Compile Include="Linq\JArrayTests.cs" />
|
||||
<Compile Include="Linq\JConstructorTests.cs" />
|
||||
<Compile Include="Linq\JObjectTests.cs" />
|
||||
<Compile Include="Linq\JPathTests.cs" />
|
||||
<Compile Include="Linq\JsonPath\JPathExecuteTests.cs" />
|
||||
<Compile Include="Linq\JsonPath\JPathParseTests.cs" />
|
||||
<Compile Include="Linq\JPropertyTests.cs" />
|
||||
<Compile Include="Linq\JRawTests.cs" />
|
||||
<Compile Include="Linq\JTokenEqualityComparerTests.cs" />
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq.JsonPath;
|
||||
#if !(NET35 || NET20 || PORTABLE40)
|
||||
using System.Dynamic;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
|
||||
namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
internal class ArrayIndexFilter : PathFilter
|
||||
{
|
||||
public int? Index { get; set; }
|
||||
|
||||
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch)
|
||||
{
|
||||
foreach (JToken t in current)
|
||||
{
|
||||
if (Index != null)
|
||||
{
|
||||
JToken v = GetTokenIndex(t, errorWhenNoMatch, Index.Value);
|
||||
|
||||
if (v != null)
|
||||
yield return v;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t is JArray || t is JConstructor)
|
||||
{
|
||||
foreach (JToken v in t)
|
||||
{
|
||||
yield return v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Index * not valid on {0}.".FormatWith(CultureInfo.InvariantCulture, t.GetType().Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
internal class ArrayMultipleIndexFilter : PathFilter
|
||||
{
|
||||
public List<int> Indexes { get; set; }
|
||||
|
||||
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch)
|
||||
{
|
||||
foreach (JToken t in current)
|
||||
{
|
||||
foreach (int i in Indexes)
|
||||
{
|
||||
JToken v = GetTokenIndex(t, errorWhenNoMatch, i);
|
||||
|
||||
if (v != null)
|
||||
yield return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
internal class ArraySliceFilter : PathFilter
|
||||
{
|
||||
public int? Start { get; set; }
|
||||
public int? End { get; set; }
|
||||
public int? Step { get; set; }
|
||||
|
||||
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch)
|
||||
{
|
||||
if (Step == 0)
|
||||
throw new JsonException("Step cannot be zero.");
|
||||
|
||||
foreach (JToken t in current)
|
||||
{
|
||||
IList<JToken> sourceCollection = t as IList<JToken> ?? new List<JToken>(t);
|
||||
|
||||
// nothing to slice
|
||||
if (sourceCollection.Count == 0)
|
||||
yield break;
|
||||
|
||||
// set defaults for null arguments
|
||||
int stepCount = Step ?? 1;
|
||||
int startIndex = Start ?? ((stepCount > 0) ? 0 : sourceCollection.Count - 1);
|
||||
int stopIndex = End ?? ((stepCount > 0) ? sourceCollection.Count : -1);
|
||||
|
||||
// start from the end of the list if start is negitive
|
||||
if (Start < 0) startIndex = sourceCollection.Count + startIndex;
|
||||
|
||||
// end from the start of the list if stop is negitive
|
||||
if (End < 0) stopIndex = sourceCollection.Count + stopIndex;
|
||||
|
||||
// ensure indexes keep within collection bounds
|
||||
startIndex = Math.Max(startIndex, (stepCount > 0) ? 0 : int.MinValue);
|
||||
startIndex = Math.Min(startIndex, (stepCount > 0) ? sourceCollection.Count : sourceCollection.Count - 1);
|
||||
stopIndex = Math.Max(stopIndex, -1);
|
||||
stopIndex = Math.Min(stopIndex, sourceCollection.Count);
|
||||
|
||||
for (int i = startIndex; (stepCount > 0) ? i < stopIndex : i > stopIndex; i += stepCount)
|
||||
{
|
||||
yield return sourceCollection[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
|
||||
namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
internal class FieldFilter : PathFilter
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch)
|
||||
{
|
||||
foreach (JToken t in current)
|
||||
{
|
||||
JObject o = t as JObject;
|
||||
if (o != null)
|
||||
{
|
||||
if (Name != null)
|
||||
{
|
||||
JToken v = o[Name];
|
||||
|
||||
if (v == null && errorWhenNoMatch)
|
||||
throw new JsonException("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, Name));
|
||||
|
||||
yield return v;
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo - error when no results
|
||||
|
||||
foreach (KeyValuePair<string, JToken> p in o)
|
||||
{
|
||||
yield return p.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Property '{0}' not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, Name ?? "*", t.GetType().Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,89 +30,12 @@ using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
|
||||
namespace Newtonsoft.Json.Linq
|
||||
namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
internal enum FilterType
|
||||
{
|
||||
ArrayIndex,
|
||||
ArrayMultipleIndex,
|
||||
ArraySlice,
|
||||
Field,
|
||||
Scan,
|
||||
Query
|
||||
}
|
||||
|
||||
internal abstract class PathFilter
|
||||
{
|
||||
public abstract FilterType Type { get; }
|
||||
}
|
||||
|
||||
internal class ArrayIndexFilter : PathFilter
|
||||
{
|
||||
public int? Index { get; set; }
|
||||
|
||||
public override FilterType Type
|
||||
{
|
||||
get { return FilterType.ArrayIndex; }
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayMultipleIndexFilter : PathFilter
|
||||
{
|
||||
public List<int> Indexes { get; set; }
|
||||
|
||||
public override FilterType Type
|
||||
{
|
||||
get { return FilterType.ArrayMultipleIndex; }
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArraySliceFilter : PathFilter
|
||||
{
|
||||
public int? Start { get; set; }
|
||||
public int? End { get; set; }
|
||||
public int? Skip { get; set; }
|
||||
|
||||
public override FilterType Type
|
||||
{
|
||||
get { return FilterType.ArraySlice; }
|
||||
}
|
||||
}
|
||||
|
||||
internal class FieldFilter : PathFilter
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public override FilterType Type
|
||||
{
|
||||
get { return FilterType.Field; }
|
||||
}
|
||||
}
|
||||
|
||||
internal class ScanFilter : PathFilter
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public override FilterType Type
|
||||
{
|
||||
get { return FilterType.Scan; }
|
||||
}
|
||||
}
|
||||
|
||||
internal class QueryFilter : PathFilter
|
||||
{
|
||||
public List<object> Expression { get; set; }
|
||||
|
||||
public override FilterType Type
|
||||
{
|
||||
get { return FilterType.Query; }
|
||||
}
|
||||
}
|
||||
|
||||
internal class JPath
|
||||
{
|
||||
private readonly string _expression;
|
||||
public List<PathFilter> Parts { get; private set; }
|
||||
public List<PathFilter> Filters { get; private set; }
|
||||
|
||||
private int _currentIndex;
|
||||
|
||||
@@ -120,7 +43,7 @@ namespace Newtonsoft.Json.Linq
|
||||
{
|
||||
ValidationUtils.ArgumentNotNull(expression, "expression");
|
||||
_expression = expression;
|
||||
Parts = new List<PathFilter>();
|
||||
Filters = new List<PathFilter>();
|
||||
|
||||
ParseMain();
|
||||
}
|
||||
@@ -131,7 +54,10 @@ namespace Newtonsoft.Json.Linq
|
||||
bool followingIndexer = false;
|
||||
bool scan = false;
|
||||
|
||||
EnsureLength("Empty path.");
|
||||
if (_expression.Length == 0)
|
||||
return;
|
||||
|
||||
EatWhitespace();
|
||||
|
||||
if (_expression[_currentIndex] == '$')
|
||||
{
|
||||
@@ -140,7 +66,7 @@ namespace Newtonsoft.Json.Linq
|
||||
EnsureLength("Unexpected end while parsing path.");
|
||||
|
||||
if (_expression[_currentIndex] != '.')
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]);
|
||||
throw new JsonException("Unexpected character while parsing path: " + _expression[_currentIndex]);
|
||||
|
||||
_currentIndex++;
|
||||
|
||||
@@ -150,12 +76,9 @@ namespace Newtonsoft.Json.Linq
|
||||
{
|
||||
scan = true;
|
||||
_currentIndex++;
|
||||
currentPartStartIndex = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPartStartIndex = 2;
|
||||
}
|
||||
|
||||
currentPartStartIndex = _currentIndex;
|
||||
}
|
||||
|
||||
while (_currentIndex < _expression.Length)
|
||||
@@ -170,17 +93,23 @@ namespace Newtonsoft.Json.Linq
|
||||
{
|
||||
string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
Parts.Add(filter);
|
||||
Filters.Add(filter);
|
||||
scan = false;
|
||||
}
|
||||
|
||||
ParseIndexer(currentChar);
|
||||
currentPartStartIndex = _currentIndex + 1;
|
||||
_currentIndex++;
|
||||
currentPartStartIndex = _currentIndex;
|
||||
followingIndexer = true;
|
||||
break;
|
||||
case ']':
|
||||
case ')':
|
||||
throw new JsonException("Unexpected character while parsing path: " + currentChar);
|
||||
case ' ':
|
||||
EatWhitespace();
|
||||
if (_currentIndex < _expression.Length)
|
||||
throw new JsonException("Unexpected character while parsing path: " + currentChar);
|
||||
break;
|
||||
case '.':
|
||||
if (_currentIndex > currentPartStartIndex)
|
||||
{
|
||||
@@ -188,7 +117,7 @@ namespace Newtonsoft.Json.Linq
|
||||
if (member == "*")
|
||||
member = null;
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
Parts.Add(filter);
|
||||
Filters.Add(filter);
|
||||
scan = false;
|
||||
}
|
||||
if (_currentIndex + 1 < _expression.Length && _expression[_currentIndex + 1] == '.')
|
||||
@@ -196,25 +125,26 @@ namespace Newtonsoft.Json.Linq
|
||||
scan = true;
|
||||
_currentIndex++;
|
||||
}
|
||||
currentPartStartIndex = _currentIndex + 1;
|
||||
_currentIndex++;
|
||||
currentPartStartIndex = _currentIndex;
|
||||
followingIndexer = false;
|
||||
break;
|
||||
default:
|
||||
if (followingIndexer)
|
||||
throw new JsonException("Unexpected character following indexer: " + currentChar);
|
||||
|
||||
_currentIndex++;
|
||||
break;
|
||||
}
|
||||
|
||||
_currentIndex++;
|
||||
}
|
||||
|
||||
if (_currentIndex > currentPartStartIndex)
|
||||
{
|
||||
string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
|
||||
string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex).TrimEnd();
|
||||
if (member == "*")
|
||||
member = null;
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
Parts.Add(filter);
|
||||
Filters.Add(filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,6 +156,8 @@ namespace Newtonsoft.Json.Linq
|
||||
|
||||
EnsureLength("Path ended with open indexer.");
|
||||
|
||||
EatWhitespace();
|
||||
|
||||
if (_expression[_currentIndex] == '\'')
|
||||
{
|
||||
ParseQuotedField(indexerCloseChar);
|
||||
@@ -243,7 +175,9 @@ namespace Newtonsoft.Json.Linq
|
||||
private void ParseArrayIndexer(char indexerCloseChar)
|
||||
{
|
||||
int start = _currentIndex;
|
||||
int? end = null;
|
||||
List<int> indexes = null;
|
||||
int colonCount = 0;
|
||||
int? startIndex = null;
|
||||
int? endIndex = null;
|
||||
int? step = null;
|
||||
@@ -252,32 +186,58 @@ namespace Newtonsoft.Json.Linq
|
||||
{
|
||||
char currentCharacter = _expression[_currentIndex];
|
||||
|
||||
if (currentCharacter == ' ')
|
||||
{
|
||||
end = _currentIndex;
|
||||
EatWhitespace();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentCharacter == indexerCloseChar)
|
||||
{
|
||||
int length = _currentIndex - start;
|
||||
|
||||
if (length == 0)
|
||||
throw new JsonException("Array index expected.");
|
||||
|
||||
string indexer = _expression.Substring(start, length);
|
||||
int index = Convert.ToInt32(indexer, CultureInfo.InvariantCulture);
|
||||
int length = (end ?? _currentIndex) - start;
|
||||
|
||||
if (indexes != null)
|
||||
{
|
||||
if (length == 0)
|
||||
throw new JsonException("Array index expected.");
|
||||
|
||||
string indexer = _expression.Substring(start, length);
|
||||
int index = Convert.ToInt32(indexer, CultureInfo.InvariantCulture);
|
||||
|
||||
indexes.Add(index);
|
||||
Parts.Add(new ArrayMultipleIndexFilter { Indexes = indexes });
|
||||
Filters.Add(new ArrayMultipleIndexFilter { Indexes = indexes });
|
||||
}
|
||||
else if (colonCount > 0)
|
||||
{
|
||||
if (length > 0)
|
||||
{
|
||||
string indexer = _expression.Substring(start, length);
|
||||
int index = Convert.ToInt32(indexer, CultureInfo.InvariantCulture);
|
||||
|
||||
if (colonCount == 1)
|
||||
endIndex = index;
|
||||
else
|
||||
step = index;
|
||||
}
|
||||
|
||||
Filters.Add(new ArraySliceFilter { Start = startIndex, End = endIndex, Step = step });
|
||||
}
|
||||
else
|
||||
{
|
||||
Parts.Add(new ArrayIndexFilter { Index = index });
|
||||
if (length == 0)
|
||||
throw new JsonException("Array index expected.");
|
||||
|
||||
string indexer = _expression.Substring(start, length);
|
||||
int index = Convert.ToInt32(indexer, CultureInfo.InvariantCulture);
|
||||
|
||||
Filters.Add(new ArrayIndexFilter { Index = index });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentCharacter == ',')
|
||||
} else if (currentCharacter == ',')
|
||||
{
|
||||
int length = _currentIndex - start;
|
||||
int length = (end ?? _currentIndex) - start;
|
||||
|
||||
if (length == 0)
|
||||
throw new JsonException("Array index expected.");
|
||||
@@ -288,30 +248,79 @@ namespace Newtonsoft.Json.Linq
|
||||
string indexer = _expression.Substring(start, length);
|
||||
indexes.Add(Convert.ToInt32(indexer, CultureInfo.InvariantCulture));
|
||||
|
||||
start = _currentIndex + 1;
|
||||
_currentIndex++;
|
||||
|
||||
EatWhitespace();
|
||||
|
||||
start = _currentIndex;
|
||||
end = null;
|
||||
}
|
||||
else if (currentCharacter == '*')
|
||||
{
|
||||
_currentIndex++;
|
||||
EnsureLength("Path ended with open indexer.");
|
||||
EatWhitespace();
|
||||
|
||||
if (_expression[_currentIndex] != indexerCloseChar)
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter);
|
||||
|
||||
Parts.Add(new ArrayIndexFilter());
|
||||
Filters.Add(new ArrayIndexFilter());
|
||||
return;
|
||||
}
|
||||
else if (!char.IsDigit(currentCharacter))
|
||||
else if (currentCharacter == ':')
|
||||
{
|
||||
int length = (end ?? _currentIndex) - start;
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
string indexer = _expression.Substring(start, length);
|
||||
int index = Convert.ToInt32(indexer, CultureInfo.InvariantCulture);
|
||||
|
||||
if (colonCount == 0)
|
||||
startIndex = index;
|
||||
else if (colonCount == 1)
|
||||
endIndex = index;
|
||||
else
|
||||
step = index;
|
||||
}
|
||||
|
||||
colonCount++;
|
||||
|
||||
_currentIndex++;
|
||||
|
||||
EatWhitespace();
|
||||
|
||||
start = _currentIndex;
|
||||
end = null;
|
||||
}
|
||||
else if (!char.IsDigit(currentCharacter) && currentCharacter != '-')
|
||||
{
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (end != null)
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter);
|
||||
|
||||
_currentIndex++;
|
||||
}
|
||||
|
||||
_currentIndex++;
|
||||
}
|
||||
|
||||
throw new JsonException("Path ended with open indexer.");
|
||||
}
|
||||
|
||||
private void EatWhitespace()
|
||||
{
|
||||
while (_currentIndex < _expression.Length)
|
||||
{
|
||||
if (_expression[_currentIndex] != ' ')
|
||||
break;
|
||||
|
||||
_currentIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseQuery(char indexerCloseChar)
|
||||
{
|
||||
_currentIndex++;
|
||||
@@ -334,7 +343,7 @@ namespace Newtonsoft.Json.Linq
|
||||
if (_expression[_currentIndex] != indexerCloseChar)
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]);
|
||||
|
||||
Parts.Add(new QueryFilter
|
||||
Filters.Add(new QueryFilter
|
||||
{
|
||||
Expression = expressions
|
||||
});
|
||||
@@ -383,11 +392,13 @@ namespace Newtonsoft.Json.Linq
|
||||
// check that character after the quote is to close the index
|
||||
_currentIndex++;
|
||||
EnsureLength("Path ended with open indexer.");
|
||||
EatWhitespace();
|
||||
|
||||
if (_expression[_currentIndex] != indexerCloseChar)
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter);
|
||||
|
||||
string indexer = _expression.Substring(start, length);
|
||||
Parts.Add(new FieldFilter { Name = indexer });
|
||||
Filters.Add(new FieldFilter { Name = indexer });
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -408,193 +419,12 @@ namespace Newtonsoft.Json.Linq
|
||||
{
|
||||
IEnumerable<JToken> current = new [] { root };
|
||||
|
||||
foreach (PathFilter filter in Parts)
|
||||
foreach (PathFilter filter in Filters)
|
||||
{
|
||||
switch (filter.Type)
|
||||
{
|
||||
case FilterType.ArrayIndex:
|
||||
current = ExecuteArrayIndexFilter(current, errorWhenNoMatch, (ArrayIndexFilter) filter);
|
||||
break;
|
||||
case FilterType.ArrayMultipleIndex:
|
||||
current = ExecuteArrayMultipleIndexFilter(current, errorWhenNoMatch, (ArrayMultipleIndexFilter) filter);
|
||||
break;
|
||||
case FilterType.Field:
|
||||
current = ExecuteFieldFilter(current, errorWhenNoMatch, (FieldFilter) filter);
|
||||
break;
|
||||
case FilterType.Scan:
|
||||
current = ExecuteScanFilter(current, errorWhenNoMatch, (ScanFilter) filter);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
current = filter.ExecuteFilter(current, errorWhenNoMatch);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
private static IEnumerable<JToken> GetDescendants(IEnumerable<JToken> source, string name)
|
||||
{
|
||||
foreach (JToken root in source)
|
||||
{
|
||||
if (name == null)
|
||||
yield return root;
|
||||
|
||||
JToken current = root;
|
||||
JToken container = root;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (container != null)
|
||||
{
|
||||
current = container.First;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (current != null && current != root && current == current.Parent.Last)
|
||||
{
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
if (current == null || current == root)
|
||||
break;
|
||||
current = current.Next;
|
||||
}
|
||||
|
||||
JProperty e = current as JProperty;
|
||||
if (e != null)
|
||||
{
|
||||
if (e.Name == name)
|
||||
yield return e.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (name == null)
|
||||
yield return current;
|
||||
}
|
||||
|
||||
container = current as JContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<JToken> ExecuteScanFilter(IEnumerable<JToken> current, bool errorWhenNoMatch, ScanFilter scanFilter)
|
||||
{
|
||||
return GetDescendants(current, scanFilter.Name);
|
||||
}
|
||||
|
||||
private IEnumerable<JToken> ExecuteArrayMultipleIndexFilter(IEnumerable<JToken> current, bool errorWhenNoMatch, ArrayMultipleIndexFilter arrayMultipleIndexFilter)
|
||||
{
|
||||
foreach (JToken t in current)
|
||||
{
|
||||
foreach (int i in arrayMultipleIndexFilter.Indexes)
|
||||
{
|
||||
JToken v = GetTokenIndex(t, errorWhenNoMatch, i);
|
||||
|
||||
if (v != null)
|
||||
yield return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<JToken> ExecuteFieldFilter(IEnumerable<JToken> current, bool errorWhenNoMatch, FieldFilter fieldFilter)
|
||||
{
|
||||
foreach (JToken t in current)
|
||||
{
|
||||
JObject o = t as JObject;
|
||||
if (o != null)
|
||||
{
|
||||
if (fieldFilter.Name != null)
|
||||
{
|
||||
JToken v = o[fieldFilter.Name];
|
||||
|
||||
if (v == null && errorWhenNoMatch)
|
||||
throw new JsonException("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, fieldFilter.Name));
|
||||
|
||||
yield return v;
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo - error when no results
|
||||
|
||||
foreach (KeyValuePair<string, JToken> p in o)
|
||||
{
|
||||
yield return p.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Property '{0}' not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, fieldFilter.Name ?? "*", t.GetType().Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<JToken> ExecuteArrayIndexFilter(IEnumerable<JToken> current, bool errorWhenNoMatch, ArrayIndexFilter arrayIndexFilter)
|
||||
{
|
||||
foreach (JToken t in current)
|
||||
{
|
||||
if (arrayIndexFilter.Index != null)
|
||||
{
|
||||
JToken v = GetTokenIndex(t, errorWhenNoMatch, arrayIndexFilter.Index.Value);
|
||||
|
||||
if (v != null)
|
||||
yield return v;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (t is JArray || t is JConstructor)
|
||||
{
|
||||
foreach (JToken v in t)
|
||||
{
|
||||
yield return v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Index * not valid on {0}.".FormatWith(CultureInfo.InvariantCulture, t.GetType().Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static JToken GetTokenIndex(JToken t, bool errorWhenNoMatch, int index)
|
||||
{
|
||||
JArray a = t as JArray;
|
||||
JConstructor c = t as JConstructor;
|
||||
|
||||
if (a != null)
|
||||
{
|
||||
if (a.Count <= index)
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Index {0} outside the bounds of JArray.".FormatWith(CultureInfo.InvariantCulture, index));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return a[index];
|
||||
}
|
||||
else if (c != null)
|
||||
{
|
||||
if (c.Count <= index)
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Index {0} outside the bounds of JConstructor.".FormatWith(CultureInfo.InvariantCulture, index));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return c[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Index {0} not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, index, t.GetType().Name));
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
|
||||
namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
internal abstract class PathFilter
|
||||
{
|
||||
public abstract IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch);
|
||||
|
||||
protected static JToken GetTokenIndex(JToken t, bool errorWhenNoMatch, int index)
|
||||
{
|
||||
JArray a = t as JArray;
|
||||
JConstructor c = t as JConstructor;
|
||||
|
||||
if (a != null)
|
||||
{
|
||||
if (a.Count <= index)
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Index {0} outside the bounds of JArray.".FormatWith(CultureInfo.InvariantCulture, index));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return a[index];
|
||||
}
|
||||
else if (c != null)
|
||||
{
|
||||
if (c.Count <= index)
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Index {0} outside the bounds of JConstructor.".FormatWith(CultureInfo.InvariantCulture, index));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return c[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Index {0} not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, index, t.GetType().Name));
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
internal class QueryFilter : PathFilter
|
||||
{
|
||||
public List<object> Expression { get; set; }
|
||||
|
||||
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Newtonsoft.Json.Linq.JsonPath
|
||||
{
|
||||
internal class ScanFilter : PathFilter
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public override IEnumerable<JToken> ExecuteFilter(IEnumerable<JToken> current, bool errorWhenNoMatch)
|
||||
{
|
||||
foreach (JToken root in current)
|
||||
{
|
||||
if (Name == null)
|
||||
yield return root;
|
||||
|
||||
JToken value = root;
|
||||
JToken container = root;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (container != null)
|
||||
{
|
||||
value = container.First;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (value != null && value != root && value == value.Parent.Last)
|
||||
{
|
||||
value = value.Parent;
|
||||
}
|
||||
|
||||
if (value == null || value == root)
|
||||
break;
|
||||
|
||||
value = value.Next;
|
||||
}
|
||||
|
||||
JProperty e = value as JProperty;
|
||||
if (e != null)
|
||||
{
|
||||
if (e.Name == Name)
|
||||
yield return e.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Name == null)
|
||||
yield return value;
|
||||
}
|
||||
|
||||
container = value as JContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,11 +111,18 @@
|
||||
<Compile Include="Linq\JContainer.cs" />
|
||||
<Compile Include="Linq\JEnumerable.cs" />
|
||||
<Compile Include="Linq\JObject.cs" />
|
||||
<Compile Include="Linq\JPath.cs" />
|
||||
<Compile Include="Linq\JsonPath\ArrayIndexFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\ArrayMultipleIndexFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\ArraySliceFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\FieldFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\JPath.cs" />
|
||||
<Compile Include="Linq\JProperty.cs" />
|
||||
<Compile Include="Linq\JPropertyDescriptor.cs" />
|
||||
<Compile Include="Linq\JPropertyKeyedCollection.cs" />
|
||||
<Compile Include="Linq\JRaw.cs" />
|
||||
<Compile Include="Linq\JsonPath\PathFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\QueryFilter.cs" />
|
||||
<Compile Include="Linq\JsonPath\ScanFilter.cs" />
|
||||
<Compile Include="Linq\JToken.cs" />
|
||||
<Compile Include="Linq\JTokenEqualityComparer.cs" />
|
||||
<Compile Include="Linq\JTokenReader.cs" />
|
||||
|
||||
Reference in New Issue
Block a user