-JsonPath++

This commit is contained in:
unknown
2013-11-15 17:25:36 +13:00
parent d8ddf1b433
commit 513ced87ca
3 changed files with 261 additions and 14 deletions
@@ -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()
{
+111 -12
View File
@@ -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));
}
}
}
}