diff --git a/Doc/JsonSchema.aml b/Doc/JsonSchema.aml
index feb06ea5..cf04b466 100644
--- a/Doc/JsonSchema.aml
+++ b/Doc/JsonSchema.aml
@@ -13,6 +13,22 @@
_blank
+
+
+
+ The JSON Schema standard isn't final.
+
+ Json.NET implements
+
+ JSON Schema Draft 3
+ http://tools.ietf.org/search/draft-zyp-json-schema-03/
+ _blank
+ .
+
+ 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.
+
+
+
diff --git a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs
index 4d70475c..818b4407 100644
--- a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs
+++ b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/JPathExecuteTests.cs
@@ -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(
+ @"Index * not valid on JObject.",
+ () => { o.SelectToken("[*]", true); });
+ }
+
+ [Test]
+ public void EvaluateSliceOnObjectWithError()
+ {
+ JObject o = new JObject(
+ new JProperty("Blah", 1));
+
+ ExceptionAssert.Throws(
+ @"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(
+ @"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(
+ @"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(
+ "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(
+ "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(
+ "Array slice of 99 to * returned no results.",
+ () => { a.SelectToken("[99:]", true); });
+
+ ExceptionAssert.Throws(
+ "Array slice of 1 to -19 returned no results.",
+ () => { a.SelectToken("[1:-19]", true); });
+
+ ExceptionAssert.Throws(
+ "Array slice of * to -19 returned no results.",
+ () => { a.SelectToken("[:-19]", true); });
+
+ a = new JArray();
+
+ ExceptionAssert.Throws(
+ "Array slice of * to * returned no results.",
+ () => { a.SelectToken("[:]", true); });
+ }
+
[Test]
public void EvaluateOutOfBoundsIndxer()
{
diff --git a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs
index 6571c920..fb04e301 100644
--- a/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs
+++ b/Src/Newtonsoft.Json.Tests/Linq/JsonPath/QueryExpressionTests.cs
@@ -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
+ {
+ 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
+ {
+ 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)));
+ }
}
}
diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs
index 3b810f2c..c5a894c9 100644
--- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs
+++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs
@@ -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>();
+
+ input.Add(new Stack(new List { "One", "Two", "Three" }));
+ input.Add(new Stack(new List { "Four", "Five", "Six" }));
+ input.Add(new Stack(new List { "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>>(serialized,
+ new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }
+ );
+
+ foreach (var stack in output)
+ {
+ foreach (var value in stack)
+ {
+ Console.WriteLine(value);
+ }
+ }
+ }
}
#if !NETFX_CORE
diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs
index 372ab34b..1abc5590 100644
--- a/Src/Newtonsoft.Json/JsonConvert.cs
+++ b/Src/Newtonsoft.Json/JsonConvert.cs
@@ -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
///
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);
diff --git a/Src/Newtonsoft.Json/Linq/JArray.cs b/Src/Newtonsoft.Json/Linq/JArray.cs
index fb0fbe3d..adebfd00 100644
--- a/Src/Newtonsoft.Json/Linq/JArray.cs
+++ b/Src/Newtonsoft.Json/Linq/JArray.cs
@@ -143,14 +143,15 @@ namespace Newtonsoft.Json.Linq
///
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;
+ }
}
///
diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs
index 379954c3..9198237f 100644
--- a/Src/Newtonsoft.Json/Linq/JObject.cs
+++ b/Src/Newtonsoft.Json/Linq/JObject.cs
@@ -326,14 +326,15 @@ namespace Newtonsoft.Json.Linq
///
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;
+ }
}
///
diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs
index 5b75ba0b..4f58be3e 100644
--- a/Src/Newtonsoft.Json/Linq/JToken.cs
+++ b/Src/Newtonsoft.Json/Linq/JToken.cs
@@ -1853,14 +1853,15 @@ namespace Newtonsoft.Json.Linq
/// A populated from the string that contains JSON.
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;
+ }
}
///
@@ -1907,48 +1908,61 @@ namespace Newtonsoft.Json.Linq
}
///
- /// Selects the token that matches the object path.
+ /// Selects a using a JPath expression. Selects the token that matches the object path.
///
///
- /// The object path from the current to the
- /// to be returned. This must be a string of property names or array indexes separated
- /// by periods, such as Tables[0].DefaultView[0].Price in C# or
- /// Tables(0).DefaultView(0).Price in Visual Basic.
+ /// A that contains a JPath expression.
///
- /// The that matches the object path or a null reference if no matching token is found.
+ /// A , or null.
public JToken SelectToken(string path)
{
return SelectToken(path, false);
}
///
- /// Selects the token that matches the object path.
+ /// Selects a using a JPath expression. Selects the token that matches the object path.
///
///
- /// The object path from the current to the
- /// to be returned. This must be a string of property names or array indexes separated
- /// by periods, such as Tables[0].DefaultView[0].Price in C# or
- /// Tables(0).DefaultView(0).Price in Visual Basic.
+ /// A that contains a JPath expression.
///
- /// A flag to indicate whether an error should be thrown if no token is found.
- /// The that matches the object path.
+ /// A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression.
+ /// A .
public JToken SelectToken(string path, bool errorWhenNoMatch)
{
JPath p = new JPath(path);
- // todo change this to a foreach
- List 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;
}
+ ///
+ /// Selects a collection of elements using a JPath expression.
+ ///
+ ///
+ /// A that contains a JPath expression.
+ ///
+ /// An that contains the selected elements.
public IEnumerable SelectTokens(string path)
{
return SelectTokens(path, false);
}
+ ///
+ /// Selects a collection of elements using a JPath expression.
+ ///
+ ///
+ /// A that contains a JPath expression.
+ ///
+ /// A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression.
+ /// An that contains the selected elements.
public IEnumerable SelectTokens(string path, bool errorWhenNoMatch)
{
JPath p = new JPath(path);
diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/ArraySliceFilter.cs b/Src/Newtonsoft.Json/Linq/JsonPath/ArraySliceFilter.cs
index 262cbe3d..ee229dec 100644
--- a/Src/Newtonsoft.Json/Linq/JsonPath/ArraySliceFilter.cs
+++ b/Src/Newtonsoft.Json/Linq/JsonPath/ArraySliceFilter.cs
@@ -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 sourceCollection = t as IList ?? new List(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);
+ }
}
}
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/FieldFilter.cs b/Src/Newtonsoft.Json/Linq/JsonPath/FieldFilter.cs
index d4ae7dcf..2b9bfaa0 100644
--- a/Src/Newtonsoft.Json/Linq/JsonPath/FieldFilter.cs
+++ b/Src/Newtonsoft.Json/Linq/JsonPath/FieldFilter.cs
@@ -27,8 +27,6 @@ namespace Newtonsoft.Json.Linq.JsonPath
}
else
{
- // todo - error when no results
-
foreach (KeyValuePair p in o)
{
yield return p.Value;
diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/FieldMultipleFilter.cs b/Src/Newtonsoft.Json/Linq/JsonPath/FieldMultipleFilter.cs
index 1fbabcbe..7480a514 100644
--- a/Src/Newtonsoft.Json/Linq/JsonPath/FieldMultipleFilter.cs
+++ b/Src/Newtonsoft.Json/Linq/JsonPath/FieldMultipleFilter.cs
@@ -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));
+ }
}
}
}
diff --git a/Src/Newtonsoft.Json/Schema/JsonSchema.cs b/Src/Newtonsoft.Json/Schema/JsonSchema.cs
index 0084a6f1..25ad8c6d 100644
--- a/Src/Newtonsoft.Json/Schema/JsonSchema.cs
+++ b/Src/Newtonsoft.Json/Schema/JsonSchema.cs
@@ -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);
+ }
}
///