-Fixed error on no result for JSON Path queries

-Fixed not disposing JsonReader/JsonWriter in some places
This commit is contained in:
James Newton-King
2013-12-22 13:52:56 +13:00
parent 49a4a94d74
commit d529190b46
12 changed files with 294 additions and 72 deletions
+16
View File
@@ -13,6 +13,22 @@
<linkTarget>_blank</linkTarget>
</externalLink></para>
</introduction>
<alert class="caution">
<para>
The JSON Schema standard isn't final.
Json.NET implements
<externalLink>
<linkText>JSON Schema Draft 3</linkText>
<linkUri>http://tools.ietf.org/search/draft-zyp-json-schema-03/</linkUri>
<linkTarget>_blank</linkTarget>
</externalLink>.
There will be breaking changes to some parts of Json.NET's JSON Schema API when it is updated to implement newer drafts of JSON Schema.
</para>
</alert>
<!-- Optional procedures followed by optional code example but must have
at least one procedure or code example -->
<section>
@@ -119,6 +119,28 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
() => { o.SelectToken("[1]", true); });
}
[Test]
public void EvaluateWildcardIndexOnObjectWithError()
{
JObject o = new JObject(
new JProperty("Blah", 1));
ExceptionAssert.Throws<JsonException>(
@"Index * not valid on JObject.",
() => { o.SelectToken("[*]", true); });
}
[Test]
public void EvaluateSliceOnObjectWithError()
{
JObject o = new JObject(
new JProperty("Blah", 1));
ExceptionAssert.Throws<JsonException>(
@"Array slice is not valid on JObject.",
() => { o.SelectToken("[:]", true); });
}
[Test]
public void EvaluatePropertyOnArray()
{
@@ -128,6 +150,16 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
Assert.IsNull(t);
}
[Test]
public void EvaluateMultipleResultsError()
{
JArray a = new JArray(1, 2, 3, 4, 5);
ExceptionAssert.Throws<JsonException>(
@"Path returned multiple tokens.",
() => { a.SelectToken("[0, 1]"); });
}
[Test]
public void EvaluatePropertyOnArrayWithError()
{
@@ -138,6 +170,16 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
() => { a.SelectToken("BlahBlah", true); });
}
[Test]
public void EvaluateNoResultsWithMultipleArrayIndexes()
{
JArray a = new JArray(1, 2, 3, 4, 5);
ExceptionAssert.Throws<JsonException>(
@"Index 9 outside the bounds of JArray.",
() => { a.SelectToken("[9,10]", true); });
}
[Test]
public void EvaluateConstructorOutOfBoundsIndxerWithError()
{
@@ -148,6 +190,14 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
() => { c.SelectToken("[1]", true); });
}
[Test]
public void EvaluateConstructorOutOfBoundsIndxer()
{
JConstructor c = new JConstructor("Blah");
Assert.IsNull(c.SelectToken("[1]"));
}
[Test]
public void EvaluateMissingPropertyWithError()
{
@@ -159,6 +209,51 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
() => { o.SelectToken("Missing", true); });
}
[Test]
public void EvaluateMissingPropertyIndexWithError()
{
JObject o = new JObject(
new JProperty("Blah", 1));
ExceptionAssert.Throws<JsonException>(
"Property 'Missing' does not exist on JObject.",
() => { o.SelectToken("['Missing','Missing2']", true); });
}
[Test]
public void EvaluateMultiPropertyIndexOnArrayWithError()
{
JArray a = new JArray(1, 2, 3, 4, 5);
ExceptionAssert.Throws<JsonException>(
"Properties 'Missing', 'Missing2' not valid on JArray.",
() => { a.SelectToken("['Missing','Missing2']", true); });
}
[Test]
public void EvaluateArraySliceWithError()
{
JArray a = new JArray(1, 2, 3, 4, 5);
ExceptionAssert.Throws<JsonException>(
"Array slice of 99 to * returned no results.",
() => { a.SelectToken("[99:]", true); });
ExceptionAssert.Throws<JsonException>(
"Array slice of 1 to -19 returned no results.",
() => { a.SelectToken("[1:-19]", true); });
ExceptionAssert.Throws<JsonException>(
"Array slice of * to -19 returned no results.",
() => { a.SelectToken("[:-19]", true); });
a = new JArray();
ExceptionAssert.Throws<JsonException>(
"Array slice of * to * returned no results.",
() => { a.SelectToken("[:]", true); });
}
[Test]
public void EvaluateOutOfBoundsIndxer()
{
@@ -131,5 +131,39 @@ namespace Newtonsoft.Json.Tests.Linq.JsonPath
Assert.IsFalse(compositeExpression.IsMatch(o3));
}
[Test]
public void BooleanExpressionTest()
{
BooleanQueryExpression e1 = new BooleanQueryExpression
{
Operator = QueryOperator.LessThan,
Value = new JValue(3),
Path = new List<PathFilter>
{
new ArrayIndexFilter()
}
};
Assert.IsTrue(e1.IsMatch(new JArray(1, 2, 3, 4, 5)));
Assert.IsTrue(e1.IsMatch(new JArray(2, 3, 4, 5)));
Assert.IsFalse(e1.IsMatch(new JArray(3, 4, 5)));
Assert.IsFalse(e1.IsMatch(new JArray(4, 5)));
BooleanQueryExpression e2 = new BooleanQueryExpression
{
Operator = QueryOperator.LessThanOrEquals,
Value = new JValue(3),
Path = new List<PathFilter>
{
new ArrayIndexFilter()
}
};
Assert.IsTrue(e2.IsMatch(new JArray(1, 2, 3, 4, 5)));
Assert.IsTrue(e2.IsMatch(new JArray(2, 3, 4, 5)));
Assert.IsTrue(e2.IsMatch(new JArray(3, 4, 5)));
Assert.IsFalse(e2.IsMatch(new JArray(4, 5)));
}
}
}
@@ -1653,6 +1653,35 @@ namespace Newtonsoft.Json.Tests.Serialization
Assert.AreEqual("string!", ((MyChild)p2.Child).MyProperty);
}
#endif
[Test]
public void ListOfStackWithFullAssemblyName()
{
var input = new List<Stack<string>>();
input.Add(new Stack<string>(new List<string> { "One", "Two", "Three" }));
input.Add(new Stack<string>(new List<string> { "Four", "Five", "Six" }));
input.Add(new Stack<string>(new List<string> { "Seven", "Eight", "Nine" }));
string serialized = JsonConvert.SerializeObject(input,
Newtonsoft.Json.Formatting.Indented,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, TypeNameAssemblyFormat = FormatterAssemblyStyle.Full } // TypeNameHandling.Auto will work
);
Console.WriteLine(serialized);
var output = JsonConvert.DeserializeObject<List<Stack<string>>>(serialized,
new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }
);
foreach (var stack in output)
{
foreach (var value in stack)
{
Console.WriteLine(value);
}
}
}
}
#if !NETFX_CORE
+5 -4
View File
@@ -819,14 +819,16 @@ namespace Newtonsoft.Json
{
ValidationUtils.ArgumentNotNull(value, "value");
StringReader sr = new StringReader(value);
JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);
// by default DeserializeObject should check for additional content
if (!jsonSerializer.IsCheckAdditionalContentSet())
jsonSerializer.CheckAdditionalContent = true;
return jsonSerializer.Deserialize(new JsonTextReader(sr), type);
using (var reader = new JsonTextReader(new StringReader(value)))
{
return jsonSerializer.Deserialize(reader, type);
}
}
#if !(NET20 || NET35 || PORTABLE40)
@@ -916,10 +918,9 @@ namespace Newtonsoft.Json
/// </param>
public static void PopulateObject(string value, object target, JsonSerializerSettings settings)
{
StringReader sr = new StringReader(value);
JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);
using (JsonReader jsonReader = new JsonTextReader(sr))
using (JsonReader jsonReader = new JsonTextReader(new StringReader(value)))
{
jsonSerializer.Populate(jsonReader, target);
+7 -6
View File
@@ -143,14 +143,15 @@ namespace Newtonsoft.Json.Linq
/// </example>
public new static JArray Parse(string json)
{
JsonReader reader = new JsonTextReader(new StringReader(json));
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
{
JArray a = Load(reader);
JArray a = Load(reader);
if (reader.Read() && reader.TokenType != JsonToken.Comment)
throw JsonReaderException.Create(reader, "Additional text found in JSON string after parsing content.");
if (reader.Read() && reader.TokenType != JsonToken.Comment)
throw JsonReaderException.Create(reader, "Additional text found in JSON string after parsing content.");
return a;
return a;
}
}
/// <summary>
+7 -6
View File
@@ -326,14 +326,15 @@ namespace Newtonsoft.Json.Linq
/// </example>
public new static JObject Parse(string json)
{
JsonReader reader = new JsonTextReader(new StringReader(json));
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
{
JObject o = Load(reader);
JObject o = Load(reader);
if (reader.Read() && reader.TokenType != JsonToken.Comment)
throw JsonReaderException.Create(reader, "Additional text found in JSON string after parsing content.");
if (reader.Read() && reader.TokenType != JsonToken.Comment)
throw JsonReaderException.Create(reader, "Additional text found in JSON string after parsing content.");
return o;
return o;
}
}
/// <summary>
+38 -24
View File
@@ -1853,14 +1853,15 @@ namespace Newtonsoft.Json.Linq
/// <returns>A <see cref="JToken"/> populated from the string that contains JSON.</returns>
public static JToken Parse(string json)
{
JsonReader reader = new JsonTextReader(new StringReader(json));
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
{
JToken t = Load(reader);
JToken t = Load(reader);
if (reader.Read() && reader.TokenType != JsonToken.Comment)
throw JsonReaderException.Create(reader, "Additional text found in JSON string after parsing content.");
if (reader.Read() && reader.TokenType != JsonToken.Comment)
throw JsonReaderException.Create(reader, "Additional text found in JSON string after parsing content.");
return t;
return t;
}
}
/// <summary>
@@ -1907,48 +1908,61 @@ namespace Newtonsoft.Json.Linq
}
/// <summary>
/// Selects the token that matches the object path.
/// Selects a <see cref="JToken"/> using a JPath expression. Selects the token that matches the object path.
/// </summary>
/// <param name="path">
/// The object path from the current <see cref="JToken"/> to the <see cref="JToken"/>
/// to be returned. This must be a string of property names or array indexes separated
/// by periods, such as <code>Tables[0].DefaultView[0].Price</code> in C# or
/// <code>Tables(0).DefaultView(0).Price</code> in Visual Basic.
/// A <see cref="String"/> that contains a JPath expression.
/// </param>
/// <returns>The <see cref="JToken"/> that matches the object path or a null reference if no matching token is found.</returns>
/// <returns>A <see cref="JToken"/>, or null.</returns>
public JToken SelectToken(string path)
{
return SelectToken(path, false);
}
/// <summary>
/// Selects the token that matches the object path.
/// Selects a <see cref="JToken"/> using a JPath expression. Selects the token that matches the object path.
/// </summary>
/// <param name="path">
/// The object path from the current <see cref="JToken"/> to the <see cref="JToken"/>
/// to be returned. This must be a string of property names or array indexes separated
/// by periods, such as <code>Tables[0].DefaultView[0].Price</code> in C# or
/// <code>Tables(0).DefaultView(0).Price</code> in Visual Basic.
/// A <see cref="String"/> that contains a JPath expression.
/// </param>
/// <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>
/// <param name="errorWhenNoMatch">A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression.</param>
/// <returns>A <see cref="JToken"/>.</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");
JToken token = null;
foreach (JToken t in p.Evaluate(this, errorWhenNoMatch))
{
if (token != null)
throw new JsonException("Path returned multiple tokens.");
return result.SingleOrDefault();
token = t;
}
return token;
}
/// <summary>
/// Selects a collection of elements using a JPath expression.
/// </summary>
/// <param name="path">
/// A <see cref="String"/> that contains a JPath expression.
/// </param>
/// <returns>An <see cref="IEnumerable{JToken}"/> that contains the selected elements.</returns>
public IEnumerable<JToken> SelectTokens(string path)
{
return SelectTokens(path, false);
}
/// <summary>
/// Selects a collection of elements using a JPath expression.
/// </summary>
/// <param name="path">
/// A <see cref="String"/> that contains a JPath expression.
/// </param>
/// <param name="errorWhenNoMatch">A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression.</param>
/// <returns>An <see cref="IEnumerable{JToken}"/> that contains the selected elements.</returns>
public IEnumerable<JToken> SelectTokens(string path, bool errorWhenNoMatch)
{
JPath p = new JPath(path);
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json.Utilities;
namespace Newtonsoft.Json.Linq.JsonPath
{
@@ -14,33 +16,59 @@ namespace Newtonsoft.Json.Linq.JsonPath
if (Step == 0)
throw new JsonException("Step cannot be zero.");
// todo error when no match
foreach (JToken t in current)
{
IList<JToken> sourceCollection = t as IList<JToken> ?? new List<JToken>(t);
// 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)
JArray a = t as JArray;
if (a != null)
{
yield return sourceCollection[i];
// set defaults for null arguments
int stepCount = Step ?? 1;
int startIndex = Start ?? ((stepCount > 0) ? 0 : a.Count - 1);
int stopIndex = End ?? ((stepCount > 0) ? a.Count : -1);
// start from the end of the list if start is negitive
if (Start < 0) startIndex = a.Count + startIndex;
// end from the start of the list if stop is negitive
if (End < 0) stopIndex = a.Count + stopIndex;
// ensure indexes keep within collection bounds
startIndex = Math.Max(startIndex, (stepCount > 0) ? 0 : int.MinValue);
startIndex = Math.Min(startIndex, (stepCount > 0) ? a.Count : a.Count - 1);
stopIndex = Math.Max(stopIndex, -1);
stopIndex = Math.Min(stopIndex, a.Count);
bool positiveStep = (stepCount > 0);
if (IsValid(startIndex, stopIndex, positiveStep))
{
for (int i = startIndex; IsValid(i, stopIndex, positiveStep); i += stepCount)
{
yield return a[i];
}
}
else
{
if (errorWhenNoMatch)
throw new JsonException("Array slice of {0} to {1} returned no results.".FormatWith(CultureInfo.InvariantCulture,
Start != null ? Start.Value.ToString(CultureInfo.InvariantCulture) : "*",
End != null ? End.Value.ToString(CultureInfo.InvariantCulture) : "*"));
}
}
else
{
if (errorWhenNoMatch)
throw new JsonException("Array slice is not valid on {0}.".FormatWith(CultureInfo.InvariantCulture, t.GetType().Name));
}
}
}
private bool IsValid(int index, int stopIndex, bool positiveStep)
{
if (positiveStep)
return (index < stopIndex);
return (index > stopIndex);
}
}
}
@@ -27,8 +27,6 @@ namespace Newtonsoft.Json.Linq.JsonPath
}
else
{
// todo - error when no results
foreach (KeyValuePair<string, JToken> p in o)
{
yield return p.Value;
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Newtonsoft.Json.Utilities;
namespace Newtonsoft.Json.Linq.JsonPath
@@ -21,13 +22,16 @@ namespace Newtonsoft.Json.Linq.JsonPath
if (v != null)
yield return v;
if (errorWhenNoMatch)
throw new JsonException("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, name));
}
}
//else
//{
// if (errorWhenNoMatch)
// throw new JsonException("Property '{0}' not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, Name ?? "*", t.GetType().Name));
//}
else
{
if (errorWhenNoMatch)
throw new JsonException("Properties {0} not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", Names.Select(n => "'" + n + "'")), t.GetType().Name));
}
}
}
}
+4 -3
View File
@@ -301,9 +301,10 @@ namespace Newtonsoft.Json.Schema
{
ValidationUtils.ArgumentNotNull(json, "json");
JsonReader reader = new JsonTextReader(new StringReader(json));
return Read(reader, resolver);
using (JsonReader reader = new JsonTextReader(new StringReader(json)))
{
return Read(reader, resolver);
}
}
/// <summary>