From d529190b467928796dad1e02b9e5f0d7c361b1a9 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sun, 22 Dec 2013 13:52:56 +1300 Subject: [PATCH 01/13] -Fixed error on no result for JSON Path queries -Fixed not disposing JsonReader/JsonWriter in some places --- Doc/JsonSchema.aml | 16 ++++ .../Linq/JsonPath/JPathExecuteTests.cs | 95 +++++++++++++++++++ .../Linq/JsonPath/QueryExpressionTests.cs | 34 +++++++ .../Serialization/TypeNameHandlingTests.cs | 29 ++++++ Src/Newtonsoft.Json/JsonConvert.cs | 9 +- Src/Newtonsoft.Json/Linq/JArray.cs | 13 +-- Src/Newtonsoft.Json/Linq/JObject.cs | 13 +-- Src/Newtonsoft.Json/Linq/JToken.cs | 62 +++++++----- .../Linq/JsonPath/ArraySliceFilter.cs | 72 +++++++++----- .../Linq/JsonPath/FieldFilter.cs | 2 - .../Linq/JsonPath/FieldMultipleFilter.cs | 14 ++- Src/Newtonsoft.Json/Schema/JsonSchema.cs | 7 +- 12 files changed, 294 insertions(+), 72 deletions(-) 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); + } } /// From 40c24307dec072e4edb92a5dc18bedad60f99980 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Mon, 23 Dec 2013 22:42:38 +1300 Subject: [PATCH 02/13] -Fixed error when wrapping an exception in a JsonConverter --- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 2 +- .../SerializationErrorHandlingTests.cs | 151 ++++++++++++++++++ .../Linq/JsonPath/FieldMultipleFilter.cs | 6 +- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/ErrorContext.cs | 2 +- .../JsonSerializerInternalReader.cs | 4 + .../JsonSerializerInternalWriter.cs | 23 ++- 8 files changed, 186 insertions(+), 6 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 4922c0cb..fc0b2a81 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.8.16815")] +[assembly: AssemblyFileVersion("5.0.8.16823")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 07a9d0b8..9c9f92c8 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -4257,7 +4257,7 @@ To fix this error either change the environment to be fully trusted, change the #if !NET20 dateTimeOffsetText = @"2000-12-20T22:59:59+02:00"; #else - dateTimeOffsetText = @"12/20/2000 22:59:59 +02:00"; + dateTimeOffsetText = @"12/20/2000 22:59:59 +02:00"; #endif ISerializableTestObject o = new ISerializableTestObject("String!", int.MinValue, dateTimeOffset, person); diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs index cd993214..9fb267b8 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs @@ -715,6 +715,157 @@ namespace Newtonsoft.Json.Tests.Serialization @"Unterminated string. Expected delimiter: "". Path '', line 1, position 3.", () => { ErrorTestObject obj = s.Deserialize(jReader); }); } + + public class RootThing + { + public Something Something { get; set; } + } + + public class RootSomethingElse + { + public SomethingElse SomethingElse { get; set; } + } + + /// + /// This could be an object we are passing up in an interface. + /// + [JsonConverter(typeof(SomethingConverter))] + public class Something + { + public class SomethingConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return true; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + try + { + // Do own stuff. + // Then call deserialise for inner object. + var innerObject = serializer.Deserialize(reader, typeof(SomethingElse)); + + return null; + } + catch (Exception ex) + { + // If we get an error wrap it in something less scary. + throw new Exception("An error occurred.", ex); + } + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + try + { + Something s = (Something)value; + + // Do own stuff. + // Then call serialise for inner object. + serializer.Serialize(writer, s.RootSomethingElse); + } + catch (Exception ex) + { + // If we get an error wrap it in something less scary. + throw new Exception("An error occurred.", ex); + } + } + } + + public RootSomethingElse RootSomethingElse { get; set; } + + public Something() + { + this.RootSomethingElse = new RootSomethingElse(); + } + } + + /// + /// This is an object that is contained in the interface object. + /// + [JsonConverter(typeof(SomethingElseConverter))] + public class SomethingElse + { + public class SomethingElseConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return true; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } + + } + + [Test] + public void DeserializeWrappingErrorsAndErrorHandling() + { + var serialiser = JsonSerializer.Create(new JsonSerializerSettings() { }); + + string foo = "{ something: { rootSomethingElse { somethingElse: 0 } } }"; + var reader = new System.IO.StringReader(foo); + + ExceptionAssert.Throws( + "An error occurred.", + () => { serialiser.Deserialize(reader, typeof(Something)); }); + } + + [Test] + public void SerializeWrappingErrorsAndErrorHandling() + { + var serialiser = JsonSerializer.Create(new JsonSerializerSettings() { }); + + Something s = new Something + { + RootSomethingElse = new RootSomethingElse + { + SomethingElse = new SomethingElse() + } + }; + RootThing r = new RootThing + { + Something = s + }; + + var writer = new System.IO.StringWriter(); + + ExceptionAssert.Throws( + "An error occurred.", + () => { serialiser.Serialize(writer, r); }); + } + + [Test] + public void DeserializeRootConverter() + { + SomethingElse result = JsonConvert.DeserializeObject("{}", new JsonSerializerSettings + { + Error = (o, e) => { e.ErrorContext.Handled = true; } + }); + + Assert.IsNull(result); + } + + [Test] + public void SerializeRootConverter() + { + string result = JsonConvert.SerializeObject(new SomethingElse(), new JsonSerializerSettings + { + Error = (o, e) => { e.ErrorContext.Handled = true; } + }); + + Assert.AreEqual(string.Empty, result); + } } internal interface IErrorPerson2 diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/FieldMultipleFilter.cs b/Src/Newtonsoft.Json/Linq/JsonPath/FieldMultipleFilter.cs index 7480a514..8349f4ae 100644 --- a/Src/Newtonsoft.Json/Linq/JsonPath/FieldMultipleFilter.cs +++ b/Src/Newtonsoft.Json/Linq/JsonPath/FieldMultipleFilter.cs @@ -1,6 +1,10 @@ using System.Collections.Generic; using System.Globalization; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else using System.Linq; +#endif using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json.Linq.JsonPath @@ -30,7 +34,7 @@ namespace Newtonsoft.Json.Linq.JsonPath else { if (errorWhenNoMatch) - throw new JsonException("Properties {0} not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", Names.Select(n => "'" + n + "'")), t.GetType().Name)); + throw new JsonException("Properties {0} not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", Names.Select(n => "'" + n + "'").ToArray()), t.GetType().Name)); } } } diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 8e31cb5f..d3d2b37e 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -91,5 +91,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.8.16815")] +[assembly: AssemblyFileVersion("5.0.8.16823")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/ErrorContext.cs b/Src/Newtonsoft.Json/Serialization/ErrorContext.cs index 3bdf11ab..bcb2fbbe 100644 --- a/Src/Newtonsoft.Json/Serialization/ErrorContext.cs +++ b/Src/Newtonsoft.Json/Serialization/ErrorContext.cs @@ -43,7 +43,7 @@ namespace Newtonsoft.Json.Serialization internal bool Traced { get; set; } /// - /// Gets or sets the error. + /// Gets the error. /// /// The error. public Exception Error { get; private set; } diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 10f06d34..3e1c084a 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -174,6 +174,10 @@ namespace Newtonsoft.Json.Serialization } else { + // clear context in case serializer is being used inside a converter + // if the converter wraps the error then not clearing the context will cause this error: + // "Current error context error is different to requested error." + ClearErrorContext(); throw; } } diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index c6d99bf0..33466430 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -65,7 +65,28 @@ namespace Newtonsoft.Json.Serialization if (objectType != null) _rootContract = Serializer._contractResolver.ResolveContract(objectType); - SerializeValue(jsonWriter, value, GetContractSafe(value), null, null, null); + JsonContract contract = GetContractSafe(value); + + try + { + + SerializeValue(jsonWriter, value, contract, null, null, null); + } + catch (Exception ex) + { + if (IsErrorHandled(null, contract, null, null, jsonWriter.Path, ex)) + { + HandleError(jsonWriter, 0); + } + else + { + // clear context in case serializer is being used inside a converter + // if the converter wraps the error then not clearing the context will cause this error: + // "Current error context error is different to requested error." + ClearErrorContext(); + throw; + } + } } private JsonSerializerProxy GetInternalSerializer() From cc730cc860e7f8aa1b85a1298f49cf431c9d72f9 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 26 Dec 2013 22:04:56 +1300 Subject: [PATCH 03/13] -Improved number parsing error message --- .../JsonTextReaderTest.cs | 96 ++++++++++++++++--- .../Properties/AssemblyInfo.cs | 2 +- Src/Newtonsoft.Json.sln.DotSettings | 1 + Src/Newtonsoft.Json/JsonTextReader.cs | 48 +++++++--- .../Properties/AssemblyInfo.cs | 2 +- Src/Newtonsoft.Json/Utilities/ConvertUtils.cs | 29 +++--- 6 files changed, 136 insertions(+), 42 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index f2d51d0e..7b6b6df8 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -799,8 +799,8 @@ Parameter name: reader", for (int j = 1; j < 1000; j++) { long total = j + i; - ExceptionAssert.Throws( - "Arithmetic operation resulted in an overflow.", + ExceptionAssert.Throws( + "JSON integer " + total + " is too large or small for an Int32. Path '', line 1, position 10.", () => { reader = new JsonTextReader(new StringReader(total.ToString(CultureInfo.InvariantCulture))); @@ -822,8 +822,8 @@ Parameter name: reader", for (int j = 1; j < 1000; j++) { long total = -j + i; - ExceptionAssert.Throws( - "Arithmetic operation resulted in an overflow.", + ExceptionAssert.Throws( + "JSON integer " + total + " is too large or small for an Int32. Path '', line 1, position 11.", () => { reader = new JsonTextReader(new StringReader(total.ToString(CultureInfo.InvariantCulture))); @@ -1230,22 +1230,96 @@ bye", reader.Value); Assert.AreEqual(int.MinValue, reader.ReadAsInt32()); reader = new JsonTextReader(new StringReader(long.MaxValue.ToString())); - ExceptionAssert.Throws("Arithmetic operation resulted in an overflow.", () => reader.ReadAsInt32()); + ExceptionAssert.Throws("JSON integer 9223372036854775807 is too large or small for an Int32. Path '', line 1, position 19.", () => reader.ReadAsInt32()); reader = new JsonTextReader(new StringReader("9999999999999999999999999999999999999999999999999999999999999999999999999999asdasdasd")); - ExceptionAssert.Throws("Input string was not in a correct format.", () => reader.ReadAsInt32()); + ExceptionAssert.Throws("Input string '9999999999999999999999999999999999999999999999999999999999999999999999999999a' is not a valid integer. Path '', line 1, position 77.", () => reader.ReadAsInt32()); reader = new JsonTextReader(new StringReader("1E-06")); - ExceptionAssert.Throws("Input string was not in a correct format.", () => reader.ReadAsInt32()); + ExceptionAssert.Throws("Input string '1E-06' is not a valid integer. Path '', line 1, position 5.", () => reader.ReadAsInt32()); reader = new JsonTextReader(new StringReader("1.1")); - ExceptionAssert.Throws("Input string was not in a correct format.", () => reader.ReadAsInt32()); + ExceptionAssert.Throws("Input string '1.1' is not a valid integer. Path '', line 1, position 3.", () => reader.ReadAsInt32()); reader = new JsonTextReader(new StringReader("")); Assert.AreEqual(null, reader.ReadAsInt32()); reader = new JsonTextReader(new StringReader("-")); - ExceptionAssert.Throws("Input string was not in a correct format.", () => reader.ReadAsInt32()); + ExceptionAssert.Throws("Input string '-' is not a valid integer. Path '', line 1, position 1.", () => reader.ReadAsInt32()); + } + + [Test] + public void ParseDecimals() + { + JsonTextReader reader = null; + + reader = new JsonTextReader(new StringReader("1.1")); + Assert.AreEqual(1.1m, reader.ReadAsDecimal()); + + reader = new JsonTextReader(new StringReader("-1.1")); + Assert.AreEqual(-1.1m, reader.ReadAsDecimal()); + + reader = new JsonTextReader(new StringReader("0.0")); + Assert.AreEqual(0.0m, reader.ReadAsDecimal()); + + reader = new JsonTextReader(new StringReader("-0.0")); + Assert.AreEqual(0, reader.ReadAsDecimal()); + + reader = new JsonTextReader(new StringReader("9999999999999999999999999999999999999999999999999999999999999999999999999999asdasdasd")); + ExceptionAssert.Throws("Input string '9999999999999999999999999999999999999999999999999999999999999999999999999999a' is not a valid decimal. Path '', line 1, position 77.", () => reader.ReadAsDecimal()); + + reader = new JsonTextReader(new StringReader("9999999999999999999999999999999999999999999999999999999999999999999999999999asdasdasd")); + reader.FloatParseHandling = Json.FloatParseHandling.Decimal; + ExceptionAssert.Throws("Input string '9999999999999999999999999999999999999999999999999999999999999999999999999999a' is not a valid decimal. Path '', line 1, position 77.", () => reader.Read()); + + reader = new JsonTextReader(new StringReader("1E-06")); + Assert.AreEqual(0.000001m, reader.ReadAsDecimal()); + + reader = new JsonTextReader(new StringReader("")); + Assert.AreEqual(null, reader.ReadAsDecimal()); + + reader = new JsonTextReader(new StringReader("-")); + ExceptionAssert.Throws("Input string '-' is not a valid decimal. Path '', line 1, position 1.", () => reader.ReadAsDecimal()); + } + + [Test] + public void ParseDoubles() + { + JsonTextReader reader = null; + + reader = new JsonTextReader(new StringReader("1.1")); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(typeof(double), reader.ValueType); + Assert.AreEqual(1.1d, reader.Value); + + reader = new JsonTextReader(new StringReader("-1.1")); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(typeof(double), reader.ValueType); + Assert.AreEqual(-1.1d, reader.Value); + + reader = new JsonTextReader(new StringReader("0.0")); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(typeof(double), reader.ValueType); + Assert.AreEqual(0.0d, reader.Value); + + reader = new JsonTextReader(new StringReader("-0.0")); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(typeof(double), reader.ValueType); + Assert.AreEqual(-0.0m, reader.Value); + + reader = new JsonTextReader(new StringReader("9999999999999999999999999999999999999999999999999999999999999999999999999999asdasdasd")); + ExceptionAssert.Throws("Input string '9999999999999999999999999999999999999999999999999999999999999999999999999999a' is not a valid number. Path '', line 1, position 77.", () => reader.Read()); + + reader = new JsonTextReader(new StringReader("1E-06")); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(typeof(double), reader.ValueType); + Assert.AreEqual(0.000001m, reader.Value); + + reader = new JsonTextReader(new StringReader("")); + Assert.IsFalse(reader.Read()); + + reader = new JsonTextReader(new StringReader("-")); + ExceptionAssert.Throws("Input string '-' is not a valid number. Path '', line 1, position 1.", () => reader.Read()); } [Test] @@ -1975,8 +2049,8 @@ bye", reader.Value); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); - ExceptionAssert.Throws( - "Input string was not in a correct format.", + ExceptionAssert.Throws( + "Input string '1.1' is not a valid integer. Path 'Name', line 1, position 12.", () => { reader.ReadAsInt32(); }); } diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index fc0b2a81..ffa2caa4 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.8.16823")] +[assembly: AssemblyFileVersion("5.0.8.16826")] diff --git a/Src/Newtonsoft.Json.sln.DotSettings b/Src/Newtonsoft.Json.sln.DotSettings index 6a89a056..c97ec47f 100644 --- a/Src/Newtonsoft.Json.sln.DotSettings +++ b/Src/Newtonsoft.Json.sln.DotSettings @@ -2,4 +2,5 @@ False True False + True \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index 3e006808..c1acd2e1 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -1195,7 +1195,14 @@ namespace Newtonsoft.Json } else { - numberValue = ConvertUtils.Int32Parse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length); + int value; + ParseResult parseResult = ConvertUtils.Int32TryParse(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length, out value); + if (parseResult == ParseResult.Success) + numberValue = value; + else if (parseResult == ParseResult.Overflow) + throw JsonReaderException.Create(this, "JSON integer {0} is too large or small for an Int32.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); + else + throw JsonReaderException.Create(this, "Input string '{0}' is not a valid integer.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); } numberType = JsonToken.Integer; @@ -1222,7 +1229,11 @@ namespace Newtonsoft.Json { string number = _stringReference.ToString(); - numberValue = decimal.Parse(number, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture); + decimal value; + if (decimal.TryParse(number, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out value)) + numberValue = value; + else + throw JsonReaderException.Create(this, "Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); } numberType = JsonToken.Float; @@ -1253,17 +1264,6 @@ namespace Newtonsoft.Json numberValue = value; numberType = JsonToken.Integer; } - else if (parseResult == ParseResult.Invalid) - { - string number = _stringReference.ToString(); - - if (_floatParseHandling == FloatParseHandling.Decimal) - numberValue = decimal.Parse(number, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture); - else - numberValue = Convert.ToDouble(number, CultureInfo.InvariantCulture); - - numberType = JsonToken.Float; - } else if (parseResult == ParseResult.Overflow) { #if !(NET20 || NET35 || PORTABLE40 || PORTABLE) @@ -1271,13 +1271,31 @@ namespace Newtonsoft.Json numberValue = BigInteger.Parse(number, CultureInfo.InvariantCulture); numberType = JsonToken.Integer; #else - // todo - validate number was a valid integer to make sure overflow was the reason for failure throw JsonReaderException.Create(this, "JSON integer {0} is too large or small for an Int64.".FormatWith(CultureInfo.InvariantCulture, _stringReference.ToString())); #endif } else { - throw JsonReaderException.Create(this, "Unknown error parsing integer."); + string number = _stringReference.ToString(); + + if (_floatParseHandling == FloatParseHandling.Decimal) + { + decimal d; + if (decimal.TryParse(number, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out d)) + numberValue = d; + else + throw JsonReaderException.Create(this, "Input string '{0}' is not a valid decimal.".FormatWith(CultureInfo.InvariantCulture, number)); + } + else + { + double d; + if (double.TryParse(number, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out d)) + numberValue = d; + else + throw JsonReaderException.Create(this, "Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number)); + } + + numberType = JsonToken.Float; } } } diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index d3d2b37e..30769750 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -91,5 +91,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.8.16823")] +[assembly: AssemblyFileVersion("5.0.8.16826")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index a33da5bb..d87e904c 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -565,10 +565,12 @@ namespace Newtonsoft.Json.Utilities } } - public static int Int32Parse(char[] chars, int start, int length) + public static ParseResult Int32TryParse(char[] chars, int start, int length, out int value) { + value = 0; + if (length == 0) - throw new FormatException("Input string was not in a correct format."); + return ParseResult.Invalid; bool isNegative = (chars[start] == '-'); @@ -576,13 +578,12 @@ namespace Newtonsoft.Json.Utilities { // text just a negative sign if (length == 1) - throw new FormatException("Input string was not in a correct format."); + return ParseResult.Invalid; start++; length--; } - int result = 0; int end = start + length; for (int i = start; i < end; i++) @@ -590,12 +591,12 @@ namespace Newtonsoft.Json.Utilities int c = chars[i] - '0'; if (c < 0 || c > 9) - throw new FormatException("Input string was not in a correct format."); + return ParseResult.Invalid; - int newValue = (10 * result) - c; + int newValue = (10 * value) - c; // overflow has caused the number to loop around - if (newValue > result) + if (newValue > value) { i++; @@ -606,13 +607,13 @@ namespace Newtonsoft.Json.Utilities c = chars[i] - '0'; if (c < 0 || c > 9) - throw new FormatException("Input string was not in a correct format."); + return ParseResult.Invalid; } - throw new OverflowException(); + return ParseResult.Overflow; } - result = newValue; + value = newValue; } // go from negative to positive to avoids overflow @@ -620,13 +621,13 @@ namespace Newtonsoft.Json.Utilities if (!isNegative) { // negative integer can be one bigger than positive - if (result == int.MinValue) - throw new OverflowException(); + if (value == int.MinValue) + return ParseResult.Overflow; - result = -result; + value = -value; } - return result; + return ParseResult.Success; } public static ParseResult Int64TryParse(char[] chars, int start, int length, out long value) From 6562d9d31cedc5bba570a182ed4c180c02cf6f0f Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sun, 29 Dec 2013 19:45:41 +1300 Subject: [PATCH 04/13] -Fixed using extension data with a non-default constructor --- .../Serialization/JsonSerializerTest.cs | 78 +++++++++++++++++++ Src/Newtonsoft.Json.Tests/TestFixtureBase.cs | 2 +- .../Serialization/ExpressionValueProvider.cs | 2 +- .../JsonSerializerInternalReader.cs | 30 +++++-- 4 files changed, 103 insertions(+), 9 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 9c9f92c8..ff3faa01 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -604,6 +604,19 @@ namespace Newtonsoft.Json.Tests.Serialization public IDictionary ExtensionData; } + public class PublicExtensionDataAttributeTestClassWithNonDefaultConstructor + { + public string Name { get; set; } + + public PublicExtensionDataAttributeTestClassWithNonDefaultConstructor(string name) + { + Name = name; + } + + [JsonExtensionData] + public IDictionary ExtensionData; + } + public class PublicNoReadExtensionDataAttributeTestClass { public string Name { get; set; } @@ -785,6 +798,34 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(2m, bizzaroC2.HourlyWage); } + [Test] + public void DeserializePublicExtensionDataTypeNamdHandlingNonDefaultConstructor() + { + string json = @"{ + ""$id"": ""1"", + ""Name"": ""Name!"", + ""Test"": 1, + ""Self"": { + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.WagePerson, Newtonsoft.Json.Tests"", + ""HourlyWage"": 2.0, + ""Name"": null, + ""BirthDate"": ""0001-01-01T00:00:00"", + ""LastModified"": ""0001-01-01T00:00:00"" + } +}"; + + PublicExtensionDataAttributeTestClassWithNonDefaultConstructor c2 = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects + }); + + Assert.AreEqual("Name!", c2.Name); + + WagePerson bizzaroC2 = (WagePerson)c2.ExtensionData["Self"]; + + Assert.AreEqual(2m, bizzaroC2.HourlyWage); + } + [Test] public void SerializePublicExtensionDataTypeNamdHandling() { @@ -8715,6 +8756,43 @@ Parameter name: value", Assert.AreEqual("EventName!", values2[0][0, 0].EventName); } + public class ExtensionDataDeserializeWithNonDefaultConstructor + { + public ExtensionDataDeserializeWithNonDefaultConstructor(string name) + { + Name = name; + } + + [JsonExtensionData] + public IDictionary _extensionData; + + public string Name { get; set; } + } + + [Test] + public void ExtensionDataDeserializeWithNonDefaultConstructorTest() + { + ExtensionDataDeserializeWithNonDefaultConstructor c = new ExtensionDataDeserializeWithNonDefaultConstructor("Name!"); + c._extensionData = new Dictionary + { + {"Key!", "Value!"} + }; + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Name"": ""Name!"", + ""Key!"": ""Value!"" +}", json); + + var c2 = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("Name!", c2.Name); + Assert.IsNotNull(c2._extensionData); + Assert.AreEqual(1, c2._extensionData.Count); + Assert.AreEqual("Value!", (string)c2._extensionData["Key!"]); + } + #if NETFX_CORE [Test] public void SerializeWinRTJsonObject() diff --git a/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs b/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs index 3ff2597f..5a16f609 100644 --- a/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs +++ b/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs @@ -178,7 +178,7 @@ namespace Newtonsoft.Json.Tests { try { - action(); + ; action(); Assert.Fail("Exception of type {0} expected; got none exception", typeof(TException).Name); } diff --git a/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs b/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs index 47102d5e..ae1e7cba 100644 --- a/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs +++ b/Src/Newtonsoft.Json/Serialization/ExpressionValueProvider.cs @@ -75,7 +75,7 @@ namespace Newtonsoft.Json.Serialization if (!ReflectionUtils.IsNullable(ReflectionUtils.GetMemberUnderlyingType(_memberInfo))) throw new JsonSerializationException("Incompatible value. Cannot set {0} to null.".FormatWith(CultureInfo.InvariantCulture, _memberInfo)); } - else if (!ReflectionUtils.GetMemberUnderlyingType(_memberInfo).IsAssignableFrom(value.GetType())) + else if (!ReflectionUtils.GetMemberUnderlyingType(_memberInfo).IsInstanceOfType(value)) { throw new JsonSerializationException("Incompatible value. Cannot set {0} to type {1}.".FormatWith(CultureInfo.InvariantCulture, _memberInfo, value.GetType())); } diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 3e1c084a..a1eed470 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -1458,7 +1458,8 @@ To fix this error either change the environment to be fully trusted, change the if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Deserializing {0} using a non-default constructor '{1}'.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType, constructorInfo)), null); - IDictionary propertyValues = ResolvePropertyAndConstructorValues(contract, containerProperty, reader, objectType); + IDictionary extensionData; + IDictionary propertyValues = ResolvePropertyAndConstructorValues(contract, containerProperty, reader, objectType, out extensionData); IDictionary constructorParameters = constructorInfo.GetParameters().ToDictionary(p => p, p => (object)null); IDictionary remainingPropertyValues = new Dictionary(); @@ -1493,7 +1494,7 @@ To fix this error either change the environment to be fully trusted, change the JsonProperty property = remainingPropertyValue.Key; object value = remainingPropertyValue.Value; - if (ShouldSetPropertyValue(remainingPropertyValue.Key, remainingPropertyValue.Value)) + if (ShouldSetPropertyValue(property, value)) { property.ValueProvider.SetValue(createdObject, value); } @@ -1537,6 +1538,14 @@ To fix this error either change the environment to be fully trusted, change the } } + if (extensionData != null) + { + foreach (KeyValuePair e in extensionData) + { + contract.ExtensionDataSetter(createdObject, e.Key, e.Value); + } + } + EndObject(createdObject, reader, contract, reader.Depth, propertiesPresence); OnDeserialized(reader, contract, createdObject); @@ -1556,8 +1565,10 @@ To fix this error either change the environment to be fully trusted, change the return value; } - private IDictionary ResolvePropertyAndConstructorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType) + private IDictionary ResolvePropertyAndConstructorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType, out IDictionary extensionData) { + extensionData = (contract.ExtensionDataSetter != null) ? new Dictionary() : null; + IDictionary propertyValues = new Dictionary(); bool exit = false; do @@ -1594,10 +1605,7 @@ To fix this error either change the environment to be fully trusted, change the propertyValue = CreateValueInternal(reader, property.PropertyType, property.PropertyContract, property, contract, containerProperty, null); propertyValues[property] = propertyValue; - } - else - { - reader.Skip(); + continue; } } else @@ -1610,7 +1618,15 @@ To fix this error either change the environment to be fully trusted, change the if (Serializer._missingMemberHandling == MissingMemberHandling.Error) throw JsonSerializationException.Create(reader, "Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, objectType.Name)); + } + if (extensionData != null) + { + object value = CreateValueInternal(reader, null, null, null, contract, containerProperty, null); + extensionData[memberName] = value; + } + else + { reader.Skip(); } break; From 62f6a2c256a207505d4951ba6e5af53acf614cf5 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 8 Jan 2014 22:28:25 +1300 Subject: [PATCH 05/13] -Fixed number parsing culture bugs --- Src/Newtonsoft.Json.Tests/TestFixtureBase.cs | 2 +- Src/Newtonsoft.Json/JsonTextReader.cs | 2 +- Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs | 4 ++-- Src/Newtonsoft.Json/Newtonsoft.Json.ruleset | 4 +++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs b/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs index 5a16f609..3ff2597f 100644 --- a/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs +++ b/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs @@ -178,7 +178,7 @@ namespace Newtonsoft.Json.Tests { try { - ; action(); + action(); Assert.Fail("Exception of type {0} expected; got none exception", typeof(TException).Name); } diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index c1acd2e1..fb9c25de 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -1289,7 +1289,7 @@ namespace Newtonsoft.Json else { double d; - if (double.TryParse(number, NumberStyles.Float | NumberStyles.AllowThousands, NumberFormatInfo.CurrentInfo, out d)) + if (double.TryParse(number, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out d)) numberValue = d; else throw JsonReaderException.Create(this, "Input string '{0}' is not a valid number.".FormatWith(CultureInfo.InvariantCulture, number)); diff --git a/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs b/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs index 7c4d6093..da37b05f 100644 --- a/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs +++ b/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs @@ -490,7 +490,7 @@ namespace Newtonsoft.Json.Linq.JsonPath if (numberText.IndexOfAny(new char[] { '.', 'E', 'e' }) != -1) { double d; - if (double.TryParse(numberText, out d)) + if (double.TryParse(numberText, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out d)) return d; else throw new JsonException("Could not read query value."); @@ -498,7 +498,7 @@ namespace Newtonsoft.Json.Linq.JsonPath else { long l; - if (long.TryParse(numberText, out l)) + if (long.TryParse(numberText, NumberStyles.Integer, CultureInfo.InvariantCulture, out l)) return l; else throw new JsonException("Could not read query value."); diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.ruleset b/Src/Newtonsoft.Json/Newtonsoft.Json.ruleset index d80dec16..ff6201db 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.ruleset +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.ruleset @@ -1,5 +1,5 @@  - + @@ -67,6 +67,8 @@ + + From f878541047b3aba6648a3e110bc4654e81bbffb2 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 9 Jan 2014 22:01:10 +1300 Subject: [PATCH 06/13] -Incremented version to 6.0.1 -Changed .NET 4 Portable NuGet target to portable-net40+sl5+wp80+win8+monotouch+monoandroid -Fixed Uri serialization roundtrip by using Uri.OriginalString -Added Decimal and DateTimeOffset constructors to JValue -Changed DefaultMemberSearchFlags on DefaultContractResolver to obsolete --- Build/Newtonsoft.Json.nuspec | 2 +- Build/build.ps1 | 6 +++--- .../Newtonsoft.Json.Tests.Portable40.csproj | 1 - .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 14 +++++++++++++ Src/Newtonsoft.Json/JsonConvert.cs | 2 +- Src/Newtonsoft.Json/Linq/JValue.cs | 20 +++++++++++++++++++ .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/DefaultContractResolver.cs | 9 ++++++++- 9 files changed, 49 insertions(+), 9 deletions(-) diff --git a/Build/Newtonsoft.Json.nuspec b/Build/Newtonsoft.Json.nuspec index 42f6cea8..8ee8494c 100644 --- a/Build/Newtonsoft.Json.nuspec +++ b/Build/Newtonsoft.Json.nuspec @@ -2,7 +2,7 @@ Newtonsoft.Json - 5.0.8 + 6.0.1-beta1 Json.NET Json.NET is a popular high-performance JSON framework for .NET James Newton-King diff --git a/Build/build.ps1 b/Build/build.ps1 index 2456bf30..633e88c5 100644 --- a/Build/build.ps1 +++ b/Build/build.ps1 @@ -1,7 +1,7 @@ properties { - $zipFileName = "Json50r8.zip" + $zipFileName = "Json60r1.zip" $majorVersion = "4.5" - $majorWithReleaseVersion = "5.0.8" + $majorWithReleaseVersion = "6.0.1" $version = GetVersion $majorWithReleaseVersion $signAssemblies = $false $signKeyPath = "C:\Development\Releases\newtonsoft.snk" @@ -19,7 +19,7 @@ $builds = @( @{Name = "Newtonsoft.Json"; TestsName = "Newtonsoft.Json.Tests"; Constants=""; FinalDir="Net45"; NuGetDir = "net45"; Framework="net-4.0"; Sign=$true}, @{Name = "Newtonsoft.Json.Portable"; TestsName = "Newtonsoft.Json.Tests.Portable"; Constants="PORTABLE"; FinalDir="Portable"; NuGetDir = "portable-net45+wp80+win8"; Framework="net-4.0"; Sign=$true}, - @{Name = "Newtonsoft.Json.Portable40"; TestsName = "Newtonsoft.Json.Tests.Portable40"; Constants="PORTABLE40"; FinalDir="Portable40"; NuGetDir = "portable-net40+sl4+wp7+win8"; Framework="net-4.0"; Sign=$true}, + @{Name = "Newtonsoft.Json.Portable40"; TestsName = "Newtonsoft.Json.Tests.Portable40"; Constants="PORTABLE40"; FinalDir="Portable40"; NuGetDir = "portable-net40+sl5+wp80+win8+monotouch+monoandroid"; Framework="net-4.0"; Sign=$true}, @{Name = "Newtonsoft.Json.WinRT"; TestsName = $null; Constants="NETFX_CORE"; FinalDir="WinRT"; NuGetDir = "netcore45"; Framework="net-4.5"; Sign=$true}, @{Name = "Newtonsoft.Json.Net40"; TestsName = "Newtonsoft.Json.Tests.Net40"; Constants="NET40"; FinalDir="Net40"; NuGetDir = "net40"; Framework="net-4.0"; Sign=$true}, @{Name = "Newtonsoft.Json.Net35"; TestsName = "Newtonsoft.Json.Tests.Net35"; Constants="NET35"; FinalDir="Net35"; NuGetDir = "net35"; Framework="net-2.0"; Sign=$true}, diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj index e68bf380..f48311e0 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj @@ -102,7 +102,6 @@ 3.5 - 3.5 diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index ffa2caa4..6a34fb83 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.8.16826")] +[assembly: AssemblyFileVersion("6.0.1.16909")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index ff3faa01..917f1f89 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -9937,6 +9937,20 @@ Parameter name: value", }", s); } #endif + + [Test] + public void RoundtripUriOriginalString() + { + string originalUri = "https://test.com?m=a%2bb"; + + Uri uriWithPlus = new Uri(originalUri); + + string jsonWithPlus = JsonConvert.SerializeObject(uriWithPlus); + + Uri uriWithPlus2 = JsonConvert.DeserializeObject(jsonWithPlus); + + Assert.AreEqual(originalUri, uriWithPlus2.OriginalString); + } } public class PersonReference diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 1abc5590..20d60d17 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -397,7 +397,7 @@ namespace Newtonsoft.Json internal static string ToString(Uri value, char quoteChar) { - return ToString(value.ToString(), quoteChar); + return ToString(value.OriginalString, quoteChar); } /// diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 1c789b29..15f79fe4 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -73,6 +73,15 @@ namespace Newtonsoft.Json.Linq { } + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(decimal value) + : this(value, JTokenType.Float) + { + } + /// /// Initializes a new instance of the class with the given value. /// @@ -119,6 +128,17 @@ namespace Newtonsoft.Json.Linq { } +#if !NET20 + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(DateTimeOffset value) + : this(value, JTokenType.Date) + { + } +#endif + /// /// Initializes a new instance of the class with the given value. /// diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 30769750..08253f81 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -91,5 +91,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.8.16826")] +[assembly: AssemblyFileVersion("6.0.1.16909")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index b5fac9ed..777b0abf 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -143,9 +143,10 @@ namespace Newtonsoft.Json.Serialization /// Gets or sets the default members search flags. /// /// The default members search flags. + [ObsoleteAttribute("DefaultMembersSearchFlags is obsolete. Inherit from DefaultContractResolver and override the GetSerializableMembers method instead.")] public BindingFlags DefaultMembersSearchFlags { get; set; } #else - private BindingFlags DefaultMembersSearchFlags = BindingFlags.Instance | BindingFlags.Public; + private BindingFlags DefaultMembersSearchFlags = BindingFlags.Instance | BindingFlags.Public; #endif /// @@ -194,7 +195,9 @@ namespace Newtonsoft.Json.Serialization public DefaultContractResolver(bool shareCache) { #if !NETFX_CORE +#pragma warning disable 618 DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.Instance; +#pragma warning restore 618 #endif #if !(NETFX_CORE || PORTABLE || PORTABLE40) IgnoreSerializableAttribute = true; @@ -280,8 +283,10 @@ namespace Newtonsoft.Json.Serialization DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(objectType); #endif +#pragma warning disable 618 List defaultMembers = ReflectionUtils.GetFieldsAndProperties(objectType, DefaultMembersSearchFlags) .Where(m => !ReflectionUtils.IsIndexedProperty(m)).ToList(); +#pragma warning restore 618 foreach (MemberInfo member in allMembers) { @@ -1248,8 +1253,10 @@ namespace Newtonsoft.Json.Serialization property.ItemTypeNameHandling = (propertyAttribute != null) ? propertyAttribute._itemTypeNameHandling : null; allowNonPublicAccess = false; +#pragma warning disable 618 if ((DefaultMembersSearchFlags & BindingFlags.NonPublic) == BindingFlags.NonPublic) allowNonPublicAccess = true; +#pragma warning restore 618 if (propertyAttribute != null) allowNonPublicAccess = true; if (memberSerialization == MemberSerialization.Fields) From de991c8f96c371928790e5e9596eefe5016a03ae Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 9 Jan 2014 23:42:55 +1300 Subject: [PATCH 07/13] -Fixed build warnings --- .../CamelCasePropertyNamesContractResolverTests.cs | 2 ++ .../Serialization/ContractResolverTests.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs index 2a3668ab..de48cad2 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs @@ -102,6 +102,7 @@ namespace Newtonsoft.Json.Tests.Serialization } #if !(NETFX_CORE || PORTABLE || PORTABLE40) +#pragma warning disable 618 [Test] public void MemberSearchFlags() { @@ -133,6 +134,7 @@ namespace Newtonsoft.Json.Tests.Serialization // readonly Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(PrivateMembersClass).GetField("i", BindingFlags.Instance | BindingFlags.NonPublic), deserializedPrivateMembersClass)); } +#pragma warning restore 618 #endif [Test] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs index 49aa618a..98716ed7 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs @@ -214,6 +214,7 @@ namespace Newtonsoft.Json.Tests.Serialization } #if !(NETFX_CORE || PORTABLE || PORTABLE40) +#pragma warning disable 618 [Test] public void SerializeCompilerGeneratedMembers() { @@ -258,6 +259,7 @@ namespace Newtonsoft.Json.Tests.Serialization ""IntProperty"": 2 }", includeCompilerGeneratedJson); } +#pragma warning restore 618 #endif } } \ No newline at end of file From fdb2811bae10dd285a155e07cb8ea1c2e7683a3a Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 11 Jan 2014 00:00:50 +1300 Subject: [PATCH 08/13] -Fixed WinRT tests --- Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index 7b6b6df8..601cb783 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -1305,7 +1305,7 @@ bye", reader.Value); reader = new JsonTextReader(new StringReader("-0.0")); Assert.IsTrue(reader.Read()); Assert.AreEqual(typeof(double), reader.ValueType); - Assert.AreEqual(-0.0m, reader.Value); + Assert.AreEqual(-0.0d, reader.Value); reader = new JsonTextReader(new StringReader("9999999999999999999999999999999999999999999999999999999999999999999999999999asdasdasd")); ExceptionAssert.Throws("Input string '9999999999999999999999999999999999999999999999999999999999999999999999999999a' is not a valid number. Path '', line 1, position 77.", () => reader.Read()); @@ -1313,7 +1313,7 @@ bye", reader.Value); reader = new JsonTextReader(new StringReader("1E-06")); Assert.IsTrue(reader.Read()); Assert.AreEqual(typeof(double), reader.ValueType); - Assert.AreEqual(0.000001m, reader.Value); + Assert.AreEqual(0.000001d, reader.Value); reader = new JsonTextReader(new StringReader("")); Assert.IsFalse(reader.Read()); From 0a5fc77966c130cff7ea4b11974004d501d86ed5 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 11 Jan 2014 12:18:04 +1300 Subject: [PATCH 09/13] -Updated readme.txt --- Doc/readme.txt | 14 +++++++------- Src/Newtonsoft.Json/Linq/JToken.cs | 4 +++- Src/Newtonsoft.Json/Utilities/StringUtils.cs | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Doc/readme.txt b/Doc/readme.txt index 807930ca..9f8dff29 100644 --- a/Doc/readme.txt +++ b/Doc/readme.txt @@ -1,7 +1,7 @@ Json.NET -http://james.newtonking.com/projects/json-net.aspx -http://www.codeplex.com/json/ +http://james.newtonking.com/json +http://www.codeplex.com/json https://github.com/JamesNK/Newtonsoft.Json @@ -45,12 +45,12 @@ Json.NET has different libraries for the various .NET Framework versions. .NET 4.5, Windows Phone 8, Windows 8 Store -Portable40: - .NET 4.0, Windows Phone 7, Windows 8 Store, Silverlight 4 + .NET 4.0, Windows Phone 8, Windows 8 Store, Silverlight 5, MonoTouch, MonoDroid Notes: -Microsoft stopped support for the Compact Framework in Visual Studio 2010. -For a Compact Framework 3.5 build download Json.NET 3.5. - -For a Silverlight 3.0 build download Json.NET 3.5. +For a Compact Framework 3.5 build download Json.NET 3.5 +For a Silverlight 3.0 build download Json.NET 3.5 +For a Silverlight 4.0 build download Json.NET 5 +For a Windows Phone 7 build download Json.NET 5 \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 4f58be3e..c8bbf9e9 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -208,7 +208,9 @@ namespace Newtonsoft.Json.Linq case JTokenType.Constructor: int index = ((IList)current).IndexOf(next); - sb.Append("[" + index + "]"); + sb.Append("["); + sb.Append(index); + sb.Append("]"); break; } } diff --git a/Src/Newtonsoft.Json/Utilities/StringUtils.cs b/Src/Newtonsoft.Json/Utilities/StringUtils.cs index ea52e12f..572151ce 100644 --- a/Src/Newtonsoft.Json/Utilities/StringUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/StringUtils.cs @@ -165,7 +165,7 @@ namespace Newtonsoft.Json.Utilities #if !(NETFX_CORE || PORTABLE) lowerCase = char.ToLower(s[i], CultureInfo.InvariantCulture); #else - lowerCase = char.ToLower(s[i]); + lowerCase = char.ToLower(s[i]); #endif sb.Append(lowerCase); From 547c3baf9e38feb7313f53c1ea87e5942c11e8d8 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 11 Jan 2014 13:13:26 +1300 Subject: [PATCH 10/13] -Updated readme.txt --- Doc/readme.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Doc/readme.txt b/Doc/readme.txt index 9f8dff29..46beeee8 100644 --- a/Doc/readme.txt +++ b/Doc/readme.txt @@ -50,7 +50,5 @@ Json.NET has different libraries for the various .NET Framework versions. Notes: -For a Compact Framework 3.5 build download Json.NET 3.5 -For a Silverlight 3.0 build download Json.NET 3.5 -For a Silverlight 4.0 build download Json.NET 5 -For a Windows Phone 7 build download Json.NET 5 \ No newline at end of file +For a Compact Framework 3.5 and Silverlight 3.0 builds download Json.NET 3.5 +For a Silverlight 4.0 and Windows Phone 7 builds download Json.NET 5 \ No newline at end of file From c4da9cfb17f98f3577aadda2636c98798aedcbd3 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sun, 19 Jan 2014 12:50:56 +1300 Subject: [PATCH 11/13] -Added support for reading single line comments --- .../JsonTextReaderTest.cs | 164 ++++++++++++++++++ .../JsonTextWriterTest.cs | 48 +++++ .../Properties/AssemblyInfo.cs | 2 +- Src/Newtonsoft.Json/JsonTextReader.cs | 50 ++++-- .../Properties/AssemblyInfo.cs | 2 +- 5 files changed, 254 insertions(+), 12 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index 601cb783..d80e0ae0 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -2522,6 +2522,170 @@ bye", reader.Value); Assert.AreEqual(JsonToken.EndConstructor, reader.TokenType); } + [Test] + public void SingleLineComments() + { + string json = @"//comment*//*hi*/ +{//comment +Name://comment +true//comment after true" + StringUtils.CarriageReturn + @" +,//comment after comma" + StringUtils.CarriageReturnLineFeed + @" +""ExpiryDate""://comment" + StringUtils.LineFeed + @" +new +" + StringUtils.LineFeed + + @"Date +(//comment +null//comment +), + ""Price"": 3.99, + ""Sizes"": //comment +[//comment + + ""Small""//comment +]//comment +}//comment +//comment 1 "; + + JsonTextReader reader = new JsonTextReader(new StreamReader(new SlowStream(json, new UTF8Encoding(false), 1))); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + Assert.AreEqual("comment*//*hi*/", reader.Value); + Assert.AreEqual(2, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(2, reader.LineNumber); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + Assert.AreEqual(3, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("Name", reader.Value); + Assert.AreEqual(3, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + Assert.AreEqual(4, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Boolean, reader.TokenType); + Assert.AreEqual(true, reader.Value); + Assert.AreEqual(4, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + Assert.AreEqual("comment after true", reader.Value); + Assert.AreEqual(5, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + Assert.AreEqual("comment after comma", reader.Value); + Assert.AreEqual(7, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("ExpiryDate", reader.Value); + Assert.AreEqual(8, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + Assert.AreEqual(9, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartConstructor, reader.TokenType); + Assert.AreEqual(13, reader.LineNumber); + Assert.AreEqual("Date", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + Assert.AreEqual(14, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + Assert.AreEqual(15, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndConstructor, reader.TokenType); + Assert.AreEqual(15, reader.LineNumber); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("Price", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("Sizes", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + Assert.AreEqual("comment ", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + Assert.AreEqual("comment 1 ", reader.Value); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void JustSinglelineComment() + { + string json = @"//comment"; + + JsonTextReader reader = new JsonTextReader(new StreamReader(new SlowStream(json, new UTF8Encoding(false), 1))); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Comment, reader.TokenType); + Assert.AreEqual("comment", reader.Value); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ErrorReadingComment() + { + string json = @"/"; + + JsonTextReader reader = new JsonTextReader(new StreamReader(new SlowStream(json, new UTF8Encoding(false), 1))); + + ExceptionAssert.Throws( + "Unexpected end while parsing comment. Path '', line 1, position 1.", + () => { reader.Read(); }); + } + [Test] public void ParseOctalNumber() { diff --git a/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs index a92283b7..aba09f59 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs @@ -1383,6 +1383,54 @@ _____'propertyName': NaN }", stringWriter.ToString()); } } + + [Test] + public void WriteComments() + { + string json = @"//comment*//*hi*/ +{//comment +Name://comment +true//comment after true" + StringUtils.CarriageReturn + @" +,//comment after comma" + StringUtils.CarriageReturnLineFeed + @" +""ExpiryDate""://comment" + StringUtils.LineFeed + @" +new +" + StringUtils.LineFeed + + @"Constructor +(//comment +null//comment +), + ""Price"": 3.99, + ""Sizes"": //comment +[//comment + + ""Small""//comment +]//comment +}//comment +//comment 1 "; + + JsonTextReader r = new JsonTextReader(new StringReader(json)); + + StringWriter sw = new StringWriter(); + JsonTextWriter w = new JsonTextWriter(sw); + w.Formatting = Formatting.Indented; + + w.WriteToken(r, true); + + Assert.AreEqual(@"/*comment*//*hi*/*/{/*comment*/ + ""Name"": /*comment*/ true/*comment after true*//*comment after comma*/, + ""ExpiryDate"": /*comment*/ new Constructor( + /*comment*/, + null + /*comment*/ + ), + ""Price"": 3.99, + ""Sizes"": /*comment*/ [ + /*comment*/ + ""Small"" + /*comment*/ + ]/*comment*/ +}/*comment *//*comment 1 */", sw.ToString()); + } } public class CustomJsonTextWriter : JsonTextWriter diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 6a34fb83..8148c450 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("6.0.1.16909")] +[assembly: AssemblyFileVersion("6.0.1.16919")] diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index fb9c25de..6aba0c74 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -1310,10 +1310,19 @@ namespace Newtonsoft.Json // should have already parsed / character before reaching this method _charPos++; - if (!EnsureChars(1, false) || _chars[_charPos] != '*') - throw JsonReaderException.Create(this, "Error parsing comment. Expected: *, got {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); + if (!EnsureChars(1, false)) + throw JsonReaderException.Create(this, "Unexpected end while parsing comment."); + + bool singlelineComment; + + if (_chars[_charPos] == '*') + singlelineComment = false; + else if (_chars[_charPos] == '/') + singlelineComment = true; else - _charPos++; + throw JsonReaderException.Create(this, "Error parsing comment. Expected: *, got {0}.".FormatWith(CultureInfo.InvariantCulture, _chars[_charPos])); + + _charPos++; int initialPosition = _charPos; @@ -1327,7 +1336,13 @@ namespace Newtonsoft.Json if (_charsUsed == _charPos) { if (ReadData(true) == 0) - throw JsonReaderException.Create(this, "Unexpected end while parsing comment."); + { + if (!singlelineComment) + throw JsonReaderException.Create(this, "Unexpected end while parsing comment."); + + _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition); + commentFinished = true; + } } else { @@ -1337,21 +1352,34 @@ namespace Newtonsoft.Json case '*': _charPos++; - if (EnsureChars(0, true)) + if (!singlelineComment) { - if (_chars[_charPos] == '/') + if (EnsureChars(0, true)) { - _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition - 1); + if (_chars[_charPos] == '/') + { + _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition - 1); - _charPos++; - commentFinished = true; + _charPos++; + commentFinished = true; + } } } break; case StringUtils.CarriageReturn: + if (singlelineComment) + { + _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition); + commentFinished = true; + } ProcessCarriageReturn(true); break; case StringUtils.LineFeed: + if (singlelineComment) + { + _stringReference = new StringReference(_chars, initialPosition, _charPos - initialPosition); + commentFinished = true; + } ProcessLineFeed(); break; default: @@ -1410,7 +1438,9 @@ namespace Newtonsoft.Json if (!EnsureChars(1, false)) return false; - return (_chars[_charPos + 1] == '*'); + var nextChart = _chars[_charPos + 1]; + + return (nextChart == '*' || nextChart == '/'); case ')': if (CurrentState == State.Constructor || CurrentState == State.ConstructorStart) return true; diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 08253f81..8bfddb03 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -91,5 +91,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("6.0.1.16909")] +[assembly: AssemblyFileVersion("6.0.1.16919")] [assembly: CLSCompliant(true)] From 4793c7b5da18c5d15f37c3bfa9faa1c58fc94fe9 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 29 Jan 2014 00:47:01 +1300 Subject: [PATCH 12/13] -Fixed TypeNameHandling.Auto with some JsonConverters -Fixed TypeNameHandling.Auto with JsonSerializer.Serialize inside a JsonConverter -Changed assembly version to 6.0.0.0 -Marked async methods on JsonConverter as obsolete --- Build/build.ps1 | 2 +- .../Properties/AssemblyInfo.cs | 4 +- .../Serialization/JsonSerializerTest.cs | 4 +- .../Serialization/TypeNameHandlingTests.cs | 55 +++++++++++++++++++ .../Serialization/WebApiIntegrationTests.cs | 2 +- .../Converters/KeyValuePairConverter.cs | 8 ++- Src/Newtonsoft.Json/JsonConvert.cs | 7 +++ .../Properties/AssemblyInfo.cs | 4 +- .../Serialization/DefaultContractResolver.cs | 2 +- .../JsonSerializerInternalWriter.cs | 14 +++-- 10 files changed, 87 insertions(+), 15 deletions(-) diff --git a/Build/build.ps1 b/Build/build.ps1 index 633e88c5..d5f94049 100644 --- a/Build/build.ps1 +++ b/Build/build.ps1 @@ -1,6 +1,6 @@ properties { $zipFileName = "Json60r1.zip" - $majorVersion = "4.5" + $majorVersion = "6.0" $majorWithReleaseVersion = "6.0.1" $version = GetVersion $majorWithReleaseVersion $signAssemblies = $false diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 8148c450..ed21e90e 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -74,5 +74,5 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("6.0.1.16919")] +[assembly: AssemblyVersion("6.0.0.0")] +[assembly: AssemblyFileVersion("6.0.1.16929")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 917f1f89..5a654f6d 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -7341,7 +7341,7 @@ Parameter name: value", JsonSerializer serializer = new JsonSerializer(); MultipleItemsClass foo = serializer.Deserialize(reader); - + values.Add(foo); } @@ -8775,7 +8775,7 @@ Parameter name: value", ExtensionDataDeserializeWithNonDefaultConstructor c = new ExtensionDataDeserializeWithNonDefaultConstructor("Name!"); c._extensionData = new Dictionary { - {"Key!", "Value!"} + { "Key!", "Value!" } }; string json = JsonConvert.SerializeObject(c, Formatting.Indented); diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs index c5a894c9..5684dbbc 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -54,6 +54,61 @@ namespace Newtonsoft.Json.Tests.Serialization [TestFixture] public class TypeNameHandlingTests : TestFixtureBase { + [Test] + public void DictionaryAuto() + { + Dictionary dic = new Dictionary + { + { "movie", new Movie { Name = "Die Hard"}} + }; + + string json = JsonConvert.SerializeObject(dic, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto + }); + + Assert.AreEqual(@"{ + ""movie"": { + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.Movie, Newtonsoft.Json.Tests"", + ""Name"": ""Die Hard"", + ""Description"": null, + ""Classification"": null, + ""Studio"": null, + ""ReleaseDate"": null, + ""ReleaseCountries"": null + } +}", json); + } + + [Test] + public void KeyValuePairAuto() + { + IList> dic = new List> + { + new KeyValuePair("movie", new Movie { Name = "Die Hard"}) + }; + + string json = JsonConvert.SerializeObject(dic, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto + }); + + Assert.AreEqual(@"[ + { + ""Key"": ""movie"", + ""Value"": { + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.Movie, Newtonsoft.Json.Tests"", + ""Name"": ""Die Hard"", + ""Description"": null, + ""Classification"": null, + ""Studio"": null, + ""ReleaseDate"": null, + ""ReleaseCountries"": null + } + } +]", json); + } + [Test] public void NestedValueObjects() { diff --git a/Src/Newtonsoft.Json.Tests/Serialization/WebApiIntegrationTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/WebApiIntegrationTests.cs index d9b190ad..1f77c184 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/WebApiIntegrationTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/WebApiIntegrationTests.cs @@ -143,7 +143,7 @@ namespace Newtonsoft.Json.Tests.Serialization #else [JsonIgnore] #endif - public string nonSerializedField; + public string nonSerializedField; public bool Equals(SerializableType other) { diff --git a/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs b/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs index d76c05ef..fa0b157c 100644 --- a/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs +++ b/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs @@ -48,6 +48,10 @@ namespace Newtonsoft.Json.Converters public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { Type t = value.GetType(); + IList genericArguments = t.GetGenericArguments(); + Type keyType = genericArguments[0]; + Type valueType = genericArguments[1]; + PropertyInfo keyProperty = t.GetProperty(KeyName); PropertyInfo valueProperty = t.GetProperty(ValueName); @@ -56,9 +60,9 @@ namespace Newtonsoft.Json.Converters writer.WriteStartObject(); writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(KeyName) : KeyName); - serializer.Serialize(writer, ReflectionUtils.GetMemberValue(keyProperty, value)); + serializer.Serialize(writer, ReflectionUtils.GetMemberValue(keyProperty, value), keyType); writer.WritePropertyName((resolver != null) ? resolver.GetResolvedPropertyName(ValueName) : ValueName); - serializer.Serialize(writer, ReflectionUtils.GetMemberValue(valueProperty, value)); + serializer.Serialize(writer, ReflectionUtils.GetMemberValue(valueProperty, value), valueType); writer.WriteEndObject(); } diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 20d60d17..0da0461e 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -642,6 +642,7 @@ namespace Newtonsoft.Json /// /// A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. /// + [ObsoleteAttribute("SerializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to serialize JSON asynchronously: Task.Factory.StartNew(() => SerializeObject(value))")] public static Task SerializeObjectAsync(object value) { return SerializeObjectAsync(value, Formatting.None, null); @@ -656,6 +657,7 @@ namespace Newtonsoft.Json /// /// A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. /// + [ObsoleteAttribute("SerializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to serialize JSON asynchronously: Task.Factory.StartNew(() => SerializeObject(value, formatting))")] public static Task SerializeObjectAsync(object value, Formatting formatting) { return SerializeObjectAsync(value, formatting, null); @@ -672,6 +674,7 @@ namespace Newtonsoft.Json /// /// A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. /// + [ObsoleteAttribute("SerializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to serialize JSON asynchronously: Task.Factory.StartNew(() => SerializeObject(value, formatting, settings))")] public static Task SerializeObjectAsync(object value, Formatting formatting, JsonSerializerSettings settings) { return Task.Factory.StartNew(() => SerializeObject(value, formatting, settings)); @@ -841,6 +844,7 @@ namespace Newtonsoft.Json /// /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. /// + [ObsoleteAttribute("DeserializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to deserialize JSON asynchronously: Task.Factory.StartNew(() => DeserializeObject(value))")] public static Task DeserializeObjectAsync(string value) { return DeserializeObjectAsync(value, null); @@ -859,6 +863,7 @@ namespace Newtonsoft.Json /// /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. /// + [ObsoleteAttribute("DeserializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to deserialize JSON asynchronously: Task.Factory.StartNew(() => DeserializeObject(value, settings))")] public static Task DeserializeObjectAsync(string value, JsonSerializerSettings settings) { return Task.Factory.StartNew(() => DeserializeObject(value, settings)); @@ -872,6 +877,7 @@ namespace Newtonsoft.Json /// /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. /// + [ObsoleteAttribute("DeserializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to deserialize JSON asynchronously: Task.Factory.StartNew(() => DeserializeObject(value))")] public static Task DeserializeObjectAsync(string value) { return DeserializeObjectAsync(value, null, null); @@ -890,6 +896,7 @@ namespace Newtonsoft.Json /// /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. /// + [ObsoleteAttribute("DeserializeObjectAsync is obsolete. Use the Task.Factory.StartNew method to deserialize JSON asynchronously: Task.Factory.StartNew(() => DeserializeObject(value, type, settings))")] public static Task DeserializeObjectAsync(string value, Type type, JsonSerializerSettings settings) { return Task.Factory.StartNew(() => DeserializeObject(value, type, settings)); diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 8bfddb03..c0e3a986 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -90,6 +90,6 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("6.0.1.16919")] +[assembly: AssemblyVersion("6.0.0.0")] +[assembly: AssemblyFileVersion("6.0.1.16929")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 777b0abf..74c30563 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -143,7 +143,7 @@ namespace Newtonsoft.Json.Serialization /// Gets or sets the default members search flags. /// /// The default members search flags. - [ObsoleteAttribute("DefaultMembersSearchFlags is obsolete. Inherit from DefaultContractResolver and override the GetSerializableMembers method instead.")] + [ObsoleteAttribute("DefaultMembersSearchFlags is obsolete. To modify the members serialized inherit from DefaultContractResolver and override the GetSerializableMembers method instead.")] public BindingFlags DefaultMembersSearchFlags { get; set; } #else private BindingFlags DefaultMembersSearchFlags = BindingFlags.Instance | BindingFlags.Public; diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 33466430..81f4aaa3 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -49,6 +49,7 @@ namespace Newtonsoft.Json.Serialization internal class JsonSerializerInternalWriter : JsonSerializerInternalBase { private JsonContract _rootContract; + private int _rootLevel; private readonly List _serializeStack = new List(); private JsonSerializerProxy _internalSerializer; @@ -62,14 +63,13 @@ namespace Newtonsoft.Json.Serialization if (jsonWriter == null) throw new ArgumentNullException("jsonWriter"); - if (objectType != null) - _rootContract = Serializer._contractResolver.ResolveContract(objectType); + _rootContract = (objectType != null) ? Serializer._contractResolver.ResolveContract(objectType) : null; + _rootLevel = _serializeStack.Count + 1; JsonContract contract = GetContractSafe(value); try { - SerializeValue(jsonWriter, value, contract, null, null, null); } catch (Exception ex) @@ -87,6 +87,12 @@ namespace Newtonsoft.Json.Serialization throw; } } + finally + { + // clear root contract to ensure that if level was > 1 then it won't + // accidently be used for non root values + _rootContract = null; + } } private JsonSerializerProxy GetInternalSerializer() @@ -869,7 +875,7 @@ To fix this error either change the environment to be fully trusted, change the if (containerContract.ItemContract == null || contract.UnderlyingType != containerContract.ItemContract.CreatedType) return true; } - else if (_rootContract != null && _serializeStack.Count == 1) + else if (_rootContract != null && _serializeStack.Count == _rootLevel) { if (contract.UnderlyingType != _rootContract.CreatedType) return true; From b3369a45cd4da50a06c10f0bb85244d2ab748f2a Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 30 Jan 2014 00:36:23 +1300 Subject: [PATCH 13/13] -Fixed compiler warnings -Changed PopulateObjectAsync to be obsolete --- Src/Newtonsoft.Json.Tests/JsonConvertTest.cs | 16 ++++++++++++++++ Src/Newtonsoft.Json/JsonConvert.cs | 1 + 2 files changed, 17 insertions(+) diff --git a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs index fa08bf0b..34f4d5f2 100644 --- a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs @@ -879,12 +879,16 @@ now brown cow?", '"', true); { Task task = null; +#pragma warning disable 612,618 task = JsonConvert.SerializeObjectAsync(42); +#pragma warning restore 612,618 task.Wait(); Assert.AreEqual("42", task.Result); +#pragma warning disable 612,618 task = JsonConvert.SerializeObjectAsync(new[] { 1, 2, 3, 4, 5 }, Formatting.Indented); +#pragma warning restore 612,618 task.Wait(); Assert.AreEqual(@"[ @@ -895,23 +899,29 @@ now brown cow?", '"', true); 5 ]", task.Result); +#pragma warning disable 612,618 task = JsonConvert.SerializeObjectAsync(DateTime.MaxValue, Formatting.None, new JsonSerializerSettings { DateFormatHandling = DateFormatHandling.MicrosoftDateFormat }); +#pragma warning restore 612,618 task.Wait(); Assert.AreEqual(@"""\/Date(253402300799999)\/""", task.Result); +#pragma warning disable 612,618 var taskObject = JsonConvert.DeserializeObjectAsync("[]"); +#pragma warning restore 612,618 taskObject.Wait(); CollectionAssert.AreEquivalent(new JArray(), (JArray)taskObject.Result); +#pragma warning disable 612,618 Task taskVersionArray = JsonConvert.DeserializeObjectAsync("['2.0']", typeof(Version[]), new JsonSerializerSettings { Converters = { new VersionConverter() } }); +#pragma warning restore 612,618 taskVersionArray.Wait(); Version[] versionArray = (Version[])taskVersionArray.Result; @@ -919,15 +929,19 @@ now brown cow?", '"', true); Assert.AreEqual(1, versionArray.Length); Assert.AreEqual(2, versionArray[0].Major); +#pragma warning disable 612,618 Task taskInt = JsonConvert.DeserializeObjectAsync("5"); +#pragma warning restore 612,618 taskInt.Wait(); Assert.AreEqual(5, taskInt.Result); +#pragma warning disable 612,618 var taskVersion = JsonConvert.DeserializeObjectAsync("'2.0'", new JsonSerializerSettings { Converters = { new VersionConverter() } }); +#pragma warning restore 612,618 taskVersion.Wait(); Assert.AreEqual(2, taskVersion.Result.Major); @@ -935,10 +949,12 @@ now brown cow?", '"', true); Movie p = new Movie(); p.Name = "Existing,"; +#pragma warning disable 612,618 Task taskVoid = JsonConvert.PopulateObjectAsync("{'Name':'Appended'}", p, new JsonSerializerSettings { Converters = new List { new JsonSerializerTest.StringAppenderConverter() } }); +#pragma warning restore 612,618 taskVoid.Wait(); diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 0da0461e..3090bfd6 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -949,6 +949,7 @@ namespace Newtonsoft.Json /// /// A task that represents the asynchronous populate operation. /// + [ObsoleteAttribute("PopulateObjectAsync is obsolete. Use the Task.Factory.StartNew method to populate an object with JSON values asynchronously: Task.Factory.StartNew(() => PopulateObject(value, target, settings))")] public static Task PopulateObjectAsync(string value, object target, JsonSerializerSettings settings) { return Task.Factory.StartNew(() => PopulateObject(value, target, settings));