-JsonPath++
This commit is contained in:
@@ -433,7 +433,7 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
|
||||
[EnumMember(Value = "baz")]
|
||||
SerializeAsBaz = 0x4,
|
||||
};
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
private enum Bar
|
||||
@@ -444,7 +444,7 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
|
||||
[EnumMember(Value = "baz")]
|
||||
SerializeAsBaz
|
||||
};
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,47 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
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()
|
||||
{
|
||||
@@ -69,6 +110,14 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
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()
|
||||
{
|
||||
@@ -216,6 +265,20 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
Assert.AreEqual(1, (int)t);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateWildcardProperty()
|
||||
{
|
||||
JObject o = new JObject(
|
||||
new JProperty("Blah", 1),
|
||||
new JProperty("Blah2", 2));
|
||||
|
||||
IList<JToken> t = o.SelectTokens("$.*").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(2, t.Count);
|
||||
Assert.AreEqual(1, (int)t[0]);
|
||||
Assert.AreEqual(2, (int)t[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void QuoteName()
|
||||
{
|
||||
@@ -329,6 +392,20 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
Assert.AreEqual(2, (int)t);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateWildcardArray()
|
||||
{
|
||||
JArray a = new JArray(1, 2, 3, 4);
|
||||
|
||||
List<JToken> t = a.SelectTokens("[*]").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(4, t.Count);
|
||||
Assert.AreEqual(1, (int)t[0]);
|
||||
Assert.AreEqual(2, (int)t[1]);
|
||||
Assert.AreEqual(3, (int)t[2]);
|
||||
Assert.AreEqual(4, (int)t[3]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateArrayMultipleIndexes()
|
||||
{
|
||||
@@ -342,6 +419,77 @@ namespace Newtonsoft.Json.Tests.Linq
|
||||
Assert.AreEqual(1, (int)t.ElementAt(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateScan()
|
||||
{
|
||||
JObject o1 = new JObject{ {"Name", 1} };
|
||||
JObject o2 = new JObject{ {"Name", 2} };
|
||||
JArray a = new JArray(o1, o2);
|
||||
|
||||
IList<JToken> t = a.SelectTokens("$..Name").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(2, t.Count);
|
||||
Assert.AreEqual(1, (int)t[0]);
|
||||
Assert.AreEqual(2, (int)t[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateWildcardScan()
|
||||
{
|
||||
JObject o1 = new JObject { { "Name", 1 } };
|
||||
JObject o2 = new JObject { { "Name", 2 } };
|
||||
JArray a = new JArray(o1, o2);
|
||||
|
||||
IList<JToken> t = a.SelectTokens("$..*").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(5, t.Count);
|
||||
Assert.IsTrue(JToken.DeepEquals(a, t[0]));
|
||||
Assert.IsTrue(JToken.DeepEquals(o1, t[1]));
|
||||
Assert.AreEqual(1, (int)t[2]);
|
||||
Assert.IsTrue(JToken.DeepEquals(o2, t[3]));
|
||||
Assert.AreEqual(2, (int)t[4]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateScanNestResults()
|
||||
{
|
||||
JObject o1 = new JObject { { "Name", 1 } };
|
||||
JObject o2 = new JObject { { "Name", 2 } };
|
||||
JObject o3 = new JObject { { "Name", new JObject { { "Name", new JArray(3) } } } };
|
||||
JArray a = new JArray(o1, o2, o3);
|
||||
|
||||
IList<JToken> t = a.SelectTokens("$..Name").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(4, t.Count);
|
||||
Assert.AreEqual(1, (int)t[0]);
|
||||
Assert.AreEqual(2, (int)t[1]);
|
||||
Assert.IsTrue(JToken.DeepEquals(new JObject { { "Name", new JArray(3) } }, t[2]));
|
||||
Assert.IsTrue(JToken.DeepEquals(new JArray(3), t[3]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateWildcardScanNestResults()
|
||||
{
|
||||
JObject o1 = new JObject { { "Name", 1 } };
|
||||
JObject o2 = new JObject { { "Name", 2 } };
|
||||
JObject o3 = new JObject { { "Name", new JObject { { "Name", new JArray(3) } } } };
|
||||
JArray a = new JArray(o1, o2, o3);
|
||||
|
||||
IList<JToken> t = a.SelectTokens("$..*").ToList();
|
||||
Assert.IsNotNull(t);
|
||||
Assert.AreEqual(9, t.Count);
|
||||
|
||||
Assert.IsTrue(JToken.DeepEquals(a, t[0]));
|
||||
Assert.IsTrue(JToken.DeepEquals(o1, t[1]));
|
||||
Assert.AreEqual(1, (int)t[2]);
|
||||
Assert.IsTrue(JToken.DeepEquals(o2, t[3]));
|
||||
Assert.AreEqual(2, (int)t[4]);
|
||||
Assert.IsTrue(JToken.DeepEquals(o3, t[5]));
|
||||
Assert.IsTrue(JToken.DeepEquals(new JObject { { "Name", new JArray(3) } }, t[6]));
|
||||
Assert.IsTrue(JToken.DeepEquals(new JArray(3), t[7]));
|
||||
Assert.AreEqual(3, (int)t[8]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EvaluateSinglePropertyReturningArray()
|
||||
{
|
||||
|
||||
@@ -27,6 +27,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
|
||||
namespace Newtonsoft.Json.Linq
|
||||
@@ -48,7 +49,7 @@ namespace Newtonsoft.Json.Linq
|
||||
|
||||
internal class ArrayIndexFilter : PathFilter
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public int? Index { get; set; }
|
||||
|
||||
public override FilterType Type
|
||||
{
|
||||
@@ -184,6 +185,8 @@ namespace Newtonsoft.Json.Linq
|
||||
if (_currentIndex > currentPartStartIndex)
|
||||
{
|
||||
string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
|
||||
if (member == "*")
|
||||
member = null;
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
Parts.Add(filter);
|
||||
scan = false;
|
||||
@@ -208,6 +211,8 @@ namespace Newtonsoft.Json.Linq
|
||||
if (_currentIndex > currentPartStartIndex)
|
||||
{
|
||||
string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
|
||||
if (member == "*")
|
||||
member = null;
|
||||
PathFilter filter = (scan) ? (PathFilter)new ScanFilter() { Name = member } : new FieldFilter() { Name = member };
|
||||
Parts.Add(filter);
|
||||
}
|
||||
@@ -285,6 +290,17 @@ namespace Newtonsoft.Json.Linq
|
||||
|
||||
start = _currentIndex + 1;
|
||||
}
|
||||
else if (currentCharacter == '*')
|
||||
{
|
||||
_currentIndex++;
|
||||
EnsureLength("Path ended with open indexer.");
|
||||
|
||||
if (_expression[_currentIndex] != indexerCloseChar)
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter);
|
||||
|
||||
Parts.Add(new ArrayIndexFilter());
|
||||
return;
|
||||
}
|
||||
else if (!char.IsDigit(currentCharacter))
|
||||
{
|
||||
throw new JsonException("Unexpected character while parsing path indexer: " + currentCharacter);
|
||||
@@ -397,13 +413,16 @@ namespace Newtonsoft.Json.Linq
|
||||
switch (filter.Type)
|
||||
{
|
||||
case FilterType.ArrayIndex:
|
||||
current = ExecuteArrayIndexFilter(current, errorWhenNoMatch, (ArrayIndexFilter)filter);
|
||||
current = ExecuteArrayIndexFilter(current, errorWhenNoMatch, (ArrayIndexFilter) filter);
|
||||
break;
|
||||
case FilterType.ArrayMultipleIndex:
|
||||
current = ExecuteArrayMultipleIndexFilter(current, errorWhenNoMatch, (ArrayMultipleIndexFilter)filter);
|
||||
current = ExecuteArrayMultipleIndexFilter(current, errorWhenNoMatch, (ArrayMultipleIndexFilter) filter);
|
||||
break;
|
||||
case FilterType.Field:
|
||||
current = ExecuteFieldFilter(current, errorWhenNoMatch, (FieldFilter)filter);
|
||||
current = ExecuteFieldFilter(current, errorWhenNoMatch, (FieldFilter) filter);
|
||||
break;
|
||||
case FilterType.Scan:
|
||||
current = ExecuteScanFilter(current, errorWhenNoMatch, (ScanFilter) filter);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
@@ -413,6 +432,56 @@ namespace Newtonsoft.Json.Linq
|
||||
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)
|
||||
@@ -434,17 +503,29 @@ namespace Newtonsoft.Json.Linq
|
||||
JObject o = t as JObject;
|
||||
if (o != null)
|
||||
{
|
||||
JToken v = o[fieldFilter.Name];
|
||||
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));
|
||||
if (v == null && errorWhenNoMatch)
|
||||
throw new JsonException("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, fieldFilter.Name));
|
||||
|
||||
yield return v;
|
||||
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));
|
||||
throw new JsonException("Property '{0}' not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, fieldFilter.Name ?? "*", t.GetType().Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -453,10 +534,28 @@ namespace Newtonsoft.Json.Linq
|
||||
{
|
||||
foreach (JToken t in current)
|
||||
{
|
||||
JToken v = GetTokenIndex(t, errorWhenNoMatch, arrayIndexFilter.Index);
|
||||
if (arrayIndexFilter.Index != null)
|
||||
{
|
||||
JToken v = GetTokenIndex(t, errorWhenNoMatch, arrayIndexFilter.Index.Value);
|
||||
|
||||
if (v != null)
|
||||
yield return v;
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user