-Initial JsonPath
This commit is contained in:
@@ -50,7 +50,23 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
{
|
||||
JPath path = new JPath("Blah");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual("Blah", path.Parts[0]);
|
||||
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 SingleScanWithRoot()
|
||||
{
|
||||
JPath path = new JPath("$..Blah");
|
||||
Assert.AreEqual(1, path.Parts.Count);
|
||||
Assert.AreEqual("Blah", ((ScanFilter)path.Parts[0]).Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -58,8 +74,17 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
{
|
||||
JPath path = new JPath("Blah.Two");
|
||||
Assert.AreEqual(2, path.Parts.Count);
|
||||
Assert.AreEqual("Blah", path.Parts[0]);
|
||||
Assert.AreEqual("Two", path.Parts[1]);
|
||||
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]
|
||||
@@ -67,21 +92,34 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
{
|
||||
JPath path = new JPath("Blah[0]");
|
||||
Assert.AreEqual(2, path.Parts.Count);
|
||||
Assert.AreEqual("Blah", path.Parts[0]);
|
||||
Assert.AreEqual(0, path.Parts[1]);
|
||||
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");
|
||||
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]);
|
||||
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]
|
||||
@@ -96,36 +134,46 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
public void UnclosedIndexer()
|
||||
{
|
||||
ExceptionAssert.Throws<JsonException>(
|
||||
@"Path ended with open indexer. Expected ]",
|
||||
@"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 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, path.Parts[0]);
|
||||
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>(
|
||||
"Empty path indexer.",
|
||||
"Array index expected.",
|
||||
() => { new JPath("[]"); });
|
||||
}
|
||||
|
||||
@@ -142,10 +190,10 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
{
|
||||
JPath path = new JPath("[1][0][0][" + int.MaxValue + "]");
|
||||
Assert.AreEqual(4, path.Parts.Count);
|
||||
Assert.AreEqual(1, path.Parts[0]);
|
||||
Assert.AreEqual(0, path.Parts[1]);
|
||||
Assert.AreEqual(0, path.Parts[2]);
|
||||
Assert.AreEqual(int.MaxValue, path.Parts[3]);
|
||||
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]
|
||||
@@ -168,6 +216,18 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
Assert.AreEqual(1, (int)t);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QuoteName()
|
||||
{
|
||||
JObject o = new JObject(
|
||||
new JProperty("Blah", 1));
|
||||
|
||||
JToken t = o.SelectToken("['Blah']");
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(JTokenType.Integer, t.Type);
|
||||
Assert.AreEqual(1, (int)t);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateMissingProperty()
|
||||
{
|
||||
@@ -269,6 +329,19 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
Assert.AreEqual(2, (int)t);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateArrayMultipleIndexes()
|
||||
{
|
||||
JArray a = new JArray(1, 2, 3, 4);
|
||||
|
||||
IEnumerable<JToken> t = a.SelectTokens("[1,2,0]");
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(3, t.Count());
|
||||
Assert.AreEqual(2, (int)t.ElementAt(0));
|
||||
Assert.AreEqual(3, (int)t.ElementAt(1));
|
||||
Assert.AreEqual(1, (int)t.ElementAt(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateSinglePropertyReturningArray()
|
||||
{
|
||||
|
||||
@@ -26,14 +26,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
|
||||
namespace Newtonsoft.Json.Linq
|
||||
{
|
||||
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<object> Parts { get; private set; }
|
||||
public List<PathFilter> Parts { get; private set; }
|
||||
|
||||
private int _currentIndex;
|
||||
|
||||
@@ -41,7 +119,7 @@ namespace Newtonsoft.Json.Linq
|
||||
{
|
||||
ValidationUtils.ArgumentNotNull(expression, "expression");
|
||||
_expression = expression;
|
||||
Parts = new List<object>();
|
||||
Parts = new List<PathFilter>();
|
||||
|
||||
ParseMain();
|
||||
}
|
||||
@@ -50,6 +128,34 @@ namespace Newtonsoft.Json.Linq
|
||||
{
|
||||
int currentPartStartIndex = _currentIndex;
|
||||
bool followingIndexer = false;
|
||||
bool scan = false;
|
||||
|
||||
EnsureLength("Empty path.");
|
||||
|
||||
if (_expression[_currentIndex] == '$')
|
||||
{
|
||||
_currentIndex++;
|
||||
|
||||
EnsureLength("Unexpected end while parsing path.");
|
||||
|
||||
if (_expression[_currentIndex] != '.')
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]);
|
||||
|
||||
_currentIndex++;
|
||||
|
||||
EnsureLength("Unexpected end while parsing path.");
|
||||
|
||||
if (_expression[_currentIndex] == '.')
|
||||
{
|
||||
scan = true;
|
||||
_currentIndex++;
|
||||
currentPartStartIndex = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPartStartIndex = 2;
|
||||
}
|
||||
}
|
||||
|
||||
while (_currentIndex < _expression.Length)
|
||||
{
|
||||
@@ -62,7 +168,9 @@ namespace Newtonsoft.Json.Linq
|
||||
if (_currentIndex > currentPartStartIndex)
|
||||
{
|
||||
string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
|
||||
Parts.Add(member);
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
Parts.Add(filter);
|
||||
scan = false;
|
||||
}
|
||||
|
||||
ParseIndexer(currentChar);
|
||||
@@ -76,7 +184,14 @@ namespace Newtonsoft.Json.Linq
|
||||
if (_currentIndex > currentPartStartIndex)
|
||||
{
|
||||
string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
|
||||
Parts.Add(member);
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
Parts.Add(filter);
|
||||
scan = false;
|
||||
}
|
||||
if (_currentIndex + 1 < _expression.Length && _expression[_currentIndex + 1] == '.')
|
||||
{
|
||||
scan = true;
|
||||
_currentIndex++;
|
||||
}
|
||||
currentPartStartIndex = _currentIndex + 1;
|
||||
followingIndexer = false;
|
||||
@@ -93,7 +208,8 @@ namespace Newtonsoft.Json.Linq
|
||||
if (_currentIndex > currentPartStartIndex)
|
||||
{
|
||||
string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
|
||||
Parts.Add(member);
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
Parts.Add(filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,23 +218,74 @@ namespace Newtonsoft.Json.Linq
|
||||
_currentIndex++;
|
||||
|
||||
char indexerCloseChar = (indexerOpenChar == '[') ? ']' : ')';
|
||||
int indexerStart = _currentIndex;
|
||||
int indexerLength = 0;
|
||||
bool indexerClosed = false;
|
||||
|
||||
EnsureLength("Path ended with open indexer.");
|
||||
|
||||
if (_expression[_currentIndex] == '\'')
|
||||
{
|
||||
ParseQuotedField(indexerCloseChar);
|
||||
}
|
||||
else if (_expression[_currentIndex] == '?')
|
||||
{
|
||||
ParseQuery(indexerCloseChar);
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseArrayIndexer(indexerCloseChar);
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseArrayIndexer(char indexerCloseChar)
|
||||
{
|
||||
int start = _currentIndex;
|
||||
List<int> indexes = null;
|
||||
int? startIndex = null;
|
||||
int? endIndex = null;
|
||||
int? step = null;
|
||||
|
||||
while (_currentIndex < _expression.Length)
|
||||
{
|
||||
char currentCharacter = _expression[_currentIndex];
|
||||
if (char.IsDigit(currentCharacter))
|
||||
|
||||
if (currentCharacter == indexerCloseChar)
|
||||
{
|
||||
indexerLength++;
|
||||
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);
|
||||
|
||||
if (indexes != null)
|
||||
{
|
||||
indexes.Add(index);
|
||||
Parts.Add(new ArrayMultipleIndexFilter { Indexes = indexes });
|
||||
}
|
||||
else
|
||||
{
|
||||
Parts.Add(new ArrayIndexFilter { Index = index });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (currentCharacter == indexerCloseChar)
|
||||
|
||||
if (currentCharacter == ',')
|
||||
{
|
||||
indexerClosed = true;
|
||||
break;
|
||||
int length = _currentIndex - start;
|
||||
|
||||
if (length == 0)
|
||||
throw new JsonException("Array index expected.");
|
||||
|
||||
if (indexes == null)
|
||||
indexes = new List<int>();
|
||||
|
||||
string indexer = _expression.Substring(start, length);
|
||||
indexes.Add(Convert.ToInt32(indexer, CultureInfo.InvariantCulture));
|
||||
|
||||
start = _currentIndex + 1;
|
||||
}
|
||||
else
|
||||
else if (!char.IsDigit(currentCharacter))
|
||||
{
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter);
|
||||
}
|
||||
@@ -126,83 +293,209 @@ namespace Newtonsoft.Json.Linq
|
||||
_currentIndex++;
|
||||
}
|
||||
|
||||
if (!indexerClosed)
|
||||
throw new JsonException("Path ended with open indexer. Expected " + indexerCloseChar);
|
||||
|
||||
if (indexerLength == 0)
|
||||
throw new JsonException("Empty path indexer.");
|
||||
|
||||
string indexer = _expression.Substring(indexerStart, indexerLength);
|
||||
Parts.Add(Convert.ToInt32(indexer, CultureInfo.InvariantCulture));
|
||||
throw new JsonException("Path ended with open indexer.");
|
||||
}
|
||||
|
||||
internal JToken Evaluate(JToken root, bool errorWhenNoMatch)
|
||||
private void ParseQuery(char indexerCloseChar)
|
||||
{
|
||||
JToken current = root;
|
||||
_currentIndex++;
|
||||
EnsureLength("Path ended with open indexer.");
|
||||
|
||||
foreach (object part in Parts)
|
||||
if (_expression[_currentIndex] != '(')
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]);
|
||||
|
||||
List<object> expressions = new List<object>();
|
||||
|
||||
while (true)
|
||||
{
|
||||
string propertyName = part as string;
|
||||
if (propertyName != null)
|
||||
ParseExpression();
|
||||
expressions.Add(string.Empty);
|
||||
|
||||
if (_expression[_currentIndex] == ')')
|
||||
{
|
||||
JObject o = current as JObject;
|
||||
if (o != null)
|
||||
_currentIndex++;
|
||||
EnsureLength("Path ended with open indexer.");
|
||||
if (_expression[_currentIndex] != indexerCloseChar)
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + _expression[_currentIndex]);
|
||||
|
||||
Parts.Add(new QueryFilter
|
||||
{
|
||||
current = o[propertyName];
|
||||
|
||||
if (current == null && errorWhenNoMatch)
|
||||
throw new JsonException("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, propertyName));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Property '{0}' not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, propertyName, current.GetType().Name));
|
||||
|
||||
return null;
|
||||
}
|
||||
Expression = expressions
|
||||
});
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = (int)part;
|
||||
_currentIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JArray a = current as JArray;
|
||||
JConstructor c = current as JConstructor;
|
||||
private void ParseExpression()
|
||||
{
|
||||
_currentIndex++;
|
||||
while (_currentIndex < _expression.Length)
|
||||
{
|
||||
char currentCharacter = _expression[_currentIndex];
|
||||
|
||||
if (a != null)
|
||||
{
|
||||
if (a.Count <= index)
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Index {0} outside the bounds of JArray.".FormatWith(CultureInfo.InvariantCulture, index));
|
||||
if (currentCharacter == ')')
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
private void ParseQuotedField(char indexerCloseChar)
|
||||
{
|
||||
_currentIndex++;
|
||||
int start = _currentIndex;
|
||||
|
||||
current = 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));
|
||||
while (_currentIndex < _expression.Length)
|
||||
{
|
||||
char currentCharacter = _expression[_currentIndex];
|
||||
if (currentCharacter == '\'')
|
||||
{
|
||||
int length = _currentIndex - start;
|
||||
|
||||
return null;
|
||||
}
|
||||
if (length == 0)
|
||||
throw new JsonException("Empty path indexer.");
|
||||
|
||||
current = c[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (errorWhenNoMatch)
|
||||
throw new JsonException("Index {0} not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, index, current.GetType().Name));
|
||||
// check that character after the quote is to close the index
|
||||
_currentIndex++;
|
||||
EnsureLength("Path ended with open indexer.");
|
||||
if (_expression[_currentIndex] != indexerCloseChar)
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter);
|
||||
|
||||
return null;
|
||||
}
|
||||
string indexer = _expression.Substring(start, length);
|
||||
Parts.Add(new FieldFilter { Name = indexer });
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_currentIndex++;
|
||||
}
|
||||
|
||||
throw new JsonException("Path ended with open indexer.");
|
||||
}
|
||||
|
||||
private void EnsureLength(string message)
|
||||
{
|
||||
if (_currentIndex >= _expression.Length)
|
||||
throw new JsonException(message);
|
||||
}
|
||||
|
||||
internal IEnumerable<JToken> Evaluate(JToken root, bool errorWhenNoMatch)
|
||||
{
|
||||
IEnumerable<JToken> current = new [] { root };
|
||||
|
||||
foreach (PathFilter filter in Parts)
|
||||
{
|
||||
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;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
JToken v = GetTokenIndex(t, errorWhenNoMatch, arrayIndexFilter.Index);
|
||||
|
||||
if (v != null)
|
||||
yield return v;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1932,6 +1932,23 @@ namespace Newtonsoft.Json.Linq
|
||||
/// <param name="errorWhenNoMatch">A flag to indicate whether an error should be thrown if no token is found.</param>
|
||||
/// <returns>The <see cref="JToken"/> that matches the object path.</returns>
|
||||
public JToken SelectToken(string path, bool errorWhenNoMatch)
|
||||
{
|
||||
JPath p = new JPath(path);
|
||||
|
||||
// todo change this to a foreach
|
||||
List<JToken> result = p.Evaluate(this, errorWhenNoMatch).ToList();
|
||||
if (result.Count > 1)
|
||||
throw new Exception("TODO");
|
||||
|
||||
return result.SingleOrDefault();
|
||||
}
|
||||
|
||||
public IEnumerable<JToken> SelectTokens(string path)
|
||||
{
|
||||
return SelectTokens(path, false);
|
||||
}
|
||||
|
||||
public IEnumerable<JToken> SelectTokens(string path, bool errorWhenNoMatch)
|
||||
{
|
||||
JPath p = new JPath(path);
|
||||
return p.Evaluate(this, errorWhenNoMatch);
|
||||
|
||||
Reference in New Issue
Block a user