diff --git a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs index e7b59df5..2b4ef3b3 100644 --- a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs @@ -241,7 +241,7 @@ now brown cow?", '"', true); } [Test] - [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "After parsing a value an unexpected character was encoutered: t")] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "After parsing a value an unexpected character was encoutered: t. Line 1, position 20.")] public void TestInvalidStrings() { string orig = @"this is a string ""that has quotes"" "; diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index da144e71..aac7f58f 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -29,6 +29,7 @@ using System.Text; using NUnit.Framework; using Newtonsoft.Json; using System.IO; +using System.Xml; namespace Newtonsoft.Json.Tests { @@ -80,43 +81,63 @@ namespace Newtonsoft.Json.Tests StringReader sr = new StringReader(input); - using (JsonReader jsonReader = new JsonTextReader(sr)) + using (JsonTextReader jsonReader = new JsonTextReader(sr)) { Assert.AreEqual(jsonReader.TokenType, JsonToken.None); + Assert.AreEqual(0, jsonReader.LineNumber); + Assert.AreEqual(0, jsonReader.LinePosition); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject); + Assert.AreEqual(1, jsonReader.LineNumber); + Assert.AreEqual(1, jsonReader.LinePosition); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); Assert.AreEqual(jsonReader.Value, "CPU"); + Assert.AreEqual(2, jsonReader.LineNumber); + Assert.AreEqual(6, jsonReader.LinePosition); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.String); Assert.AreEqual(jsonReader.Value, "Intel"); + Assert.AreEqual(2, jsonReader.LineNumber); + Assert.AreEqual(14, jsonReader.LinePosition); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); Assert.AreEqual(jsonReader.Value, "Drives"); + Assert.AreEqual(3, jsonReader.LineNumber); + Assert.AreEqual(9, jsonReader.LinePosition); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.StartArray); + Assert.AreEqual(3, jsonReader.LineNumber); + Assert.AreEqual(11, jsonReader.LinePosition); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.String); Assert.AreEqual(jsonReader.Value, "DVD read/writer"); Assert.AreEqual(jsonReader.QuoteChar, '\''); + Assert.AreEqual(4, jsonReader.LineNumber); + Assert.AreEqual(21, jsonReader.LinePosition); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.String); Assert.AreEqual(jsonReader.Value, "500 gigabyte hard drive"); Assert.AreEqual(jsonReader.QuoteChar, '"'); + Assert.AreEqual(5, jsonReader.LineNumber); + Assert.AreEqual(29, jsonReader.LinePosition); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.EndArray); + Assert.AreEqual(6, jsonReader.LineNumber); + Assert.AreEqual(3, jsonReader.LinePosition); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject); + Assert.AreEqual(7, jsonReader.LineNumber); + Assert.AreEqual(1, jsonReader.LinePosition); } } diff --git a/Src/Newtonsoft.Json.Tests/JsonTokenReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTokenReaderTest.cs index efb61485..b9004b9f 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTokenReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTokenReaderTest.cs @@ -46,18 +46,25 @@ namespace Newtonsoft.Json.Tests new JProperty("Test4", null) ); - using (JsonReader jsonReader = new JsonTokenReader(o)) + using (JsonTokenReader jsonReader = new JsonTokenReader(o)) { + IJsonLineInfo lineInfo = jsonReader; + jsonReader.Read(); Assert.AreEqual(JsonToken.StartObject, jsonReader.TokenType); + Assert.AreEqual(false, lineInfo.HasLineInfo()); jsonReader.Read(); Assert.AreEqual(JsonToken.PropertyName, jsonReader.TokenType); Assert.AreEqual("Test1", jsonReader.Value); + Assert.AreEqual(false, lineInfo.HasLineInfo()); jsonReader.Read(); Assert.AreEqual(JsonToken.Date, jsonReader.TokenType); Assert.AreEqual(new DateTime(2000, 10, 15, 5, 5, 5, DateTimeKind.Utc), jsonReader.Value); + Assert.AreEqual(false, lineInfo.HasLineInfo()); + Assert.AreEqual(0, lineInfo.LinePosition); + Assert.AreEqual(0, lineInfo.LineNumber); jsonReader.Read(); Assert.AreEqual(JsonToken.PropertyName, jsonReader.TokenType); @@ -104,7 +111,7 @@ namespace Newtonsoft.Json.Tests } [Test] - public void ReadingIndented() + public void ReadLineInfo() { string input = @"{ CPU: 'Intel', @@ -116,140 +123,76 @@ namespace Newtonsoft.Json.Tests StringReader sr = new StringReader(input); - using (JsonReader jsonReader = new JsonTextReader(sr)) + JObject o = JObject.Parse(input); + + using (JsonTokenReader jsonReader = new JsonTokenReader(o)) { + IJsonLineInfo lineInfo = jsonReader; + Assert.AreEqual(jsonReader.TokenType, JsonToken.None); + Assert.AreEqual(0, lineInfo.LineNumber); + Assert.AreEqual(0, lineInfo.LinePosition); + Assert.AreEqual(false, lineInfo.HasLineInfo()); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject); + Assert.AreEqual(1, lineInfo.LineNumber); + Assert.AreEqual(1, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); Assert.AreEqual(jsonReader.Value, "CPU"); + Assert.AreEqual(2, lineInfo.LineNumber); + Assert.AreEqual(6, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.String); Assert.AreEqual(jsonReader.Value, "Intel"); + Assert.AreEqual(2, lineInfo.LineNumber); + Assert.AreEqual(14, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); Assert.AreEqual(jsonReader.Value, "Drives"); + Assert.AreEqual(3, lineInfo.LineNumber); + Assert.AreEqual(9, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.StartArray); + Assert.AreEqual(3, lineInfo.LineNumber); + Assert.AreEqual(11, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.String); Assert.AreEqual(jsonReader.Value, "DVD read/writer"); - Assert.AreEqual(jsonReader.QuoteChar, '\''); + Assert.AreEqual(4, lineInfo.LineNumber); + Assert.AreEqual(21, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.String); Assert.AreEqual(jsonReader.Value, "500 gigabyte hard drive"); - Assert.AreEqual(jsonReader.QuoteChar, '"'); + Assert.AreEqual(5, lineInfo.LineNumber); + Assert.AreEqual(29, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.EndArray); + Assert.AreEqual(0, lineInfo.LineNumber); + Assert.AreEqual(0, lineInfo.LinePosition); + Assert.AreEqual(false, lineInfo.HasLineInfo()); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject); + Assert.AreEqual(0, lineInfo.LineNumber); + Assert.AreEqual(0, lineInfo.LinePosition); + Assert.AreEqual(false, lineInfo.HasLineInfo()); } } - - [Test] - public void ReadingEscapedStrings() - { - string input = "{value:'Purple\\r \\n monkey\\'s:\\tdishwasher'}"; - - StringReader sr = new StringReader(input); - - using (JsonReader jsonReader = new JsonTextReader(sr)) - { - jsonReader.Read(); - Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject); - - jsonReader.Read(); - Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); - - jsonReader.Read(); - Assert.AreEqual(jsonReader.TokenType, JsonToken.String); - Assert.AreEqual(jsonReader.Value, "Purple\r \n monkey's:\tdishwasher"); - Assert.AreEqual(jsonReader.QuoteChar, '\''); - - jsonReader.Read(); - Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject); - } - } - - [Test] - public void ReadNewlineLastCharacter() - { - string input = @"{ - CPU: 'Intel', - Drives: [ - 'DVD read/writer', - ""500 gigabyte hard drive"" - ] -}" + '\n'; - - object o = JsonConvert.DeserializeObject(input); - } - - [Test] - public void WriteReadWrite() - { - StringBuilder sb = new StringBuilder(); - StringWriter sw = new StringWriter(sb); - - using (JsonWriter jsonWriter = new JsonTextWriter(sw)) - { - jsonWriter.WriteStartArray(); - jsonWriter.WriteValue(true); - - jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("integer"); - jsonWriter.WriteValue(99); - jsonWriter.WritePropertyName("string"); - jsonWriter.WriteValue("how now brown cow?"); - jsonWriter.WritePropertyName("array"); - - jsonWriter.WriteStartArray(); - for (int i = 0; i < 5; i++) - { - jsonWriter.WriteValue(i); - } - - jsonWriter.WriteStartObject(); - jsonWriter.WritePropertyName("decimal"); - jsonWriter.WriteValue(990.00990099m); - jsonWriter.WriteEndObject(); - - jsonWriter.WriteValue(5); - jsonWriter.WriteEndArray(); - - jsonWriter.WriteEndObject(); - - jsonWriter.WriteValue("This is a string."); - jsonWriter.WriteNull(); - jsonWriter.WriteNull(); - jsonWriter.WriteEndArray(); - } - - string json = sb.ToString(); - - JsonSerializer serializer = new JsonSerializer(); - - object jsonObject = serializer.Deserialize(new JsonTextReader(new StringReader(json))); - - sb = new StringBuilder(); - sw = new StringWriter(sb); - - using (JsonWriter jsonWriter = new JsonTextWriter(sw)) - { - serializer.Serialize(sw, jsonObject); - } - - Assert.AreEqual(json, sb.ToString()); - } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/JsonValidatingReaderTests.cs b/Src/Newtonsoft.Json.Tests/JsonValidatingReaderTests.cs index c757c352..31a484b5 100644 --- a/Src/Newtonsoft.Json.Tests/JsonValidatingReaderTests.cs +++ b/Src/Newtonsoft.Json.Tests/JsonValidatingReaderTests.cs @@ -184,7 +184,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.String, reader.TokenType); - Assert.AreEqual("String 'pie' is less than minimum length of 5.", validationEventArgs.Message); + Assert.AreEqual("String 'pie' is less than minimum length of 5. Line 1, position 5.", validationEventArgs.Message); Assert.IsNotNull(validationEventArgs); } @@ -208,7 +208,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.String, reader.TokenType); - Assert.AreEqual("String 'The quick brown fox jumps over the lazy dog.' exceeds maximum length of 10.", validationEventArgs.Message); + Assert.AreEqual("String 'The quick brown fox jumps over the lazy dog.' exceeds maximum length of 10. Line 1, position 46.", validationEventArgs.Message); Assert.IsNotNull(validationEventArgs); } @@ -245,7 +245,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.String, reader.TokenType); - Assert.AreEqual(@"Value ""THREE"" is not defined in enum.", validationEventArgs.Message); + Assert.AreEqual(@"Value ""THREE"" is not defined in enum. Line 1, position 20.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndArray, reader.TokenType); @@ -271,7 +271,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.String, reader.TokenType); - Assert.AreEqual("String 'The quick brown fox jumps over the lazy dog.' does not match regex pattern 'foo'.", validationEventArgs.Message); + Assert.AreEqual("String 'The quick brown fox jumps over the lazy dog.' does not match regex pattern 'foo'. Line 1, position 46.", validationEventArgs.Message); Assert.IsNotNull(validationEventArgs); } @@ -294,13 +294,13 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Integer, reader.TokenType); - Assert.AreEqual("Integer 10 exceeds maximum value of 5.", validationEventArgs.Message); + Assert.AreEqual("Integer 10 exceeds maximum value of 5. Line 1, position 2.", validationEventArgs.Message); Assert.IsNotNull(validationEventArgs); } [Test] - [ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Integer 10 exceeds maximum value of 5.")] + [ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Integer 10 exceeds maximum value of 5. Line 1, position 2.")] public void ThrowExceptionWhenNoValidationEventHandler() { string schemaJson = @"{ @@ -332,7 +332,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Integer, reader.TokenType); - Assert.AreEqual("Integer 1 is less than minimum value of 5.", validationEventArgs.Message); + Assert.AreEqual("Integer 1 is less than minimum value of 5. Line 1, position 1.", validationEventArgs.Message); Assert.IsNotNull(validationEventArgs); } @@ -369,7 +369,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Integer, reader.TokenType); - Assert.AreEqual(@"Value 3 is not defined in enum.", validationEventArgs.Message); + Assert.AreEqual(@"Value 3 is not defined in enum. Line 1, position 7.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndArray, reader.TokenType); @@ -395,7 +395,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Float, reader.TokenType); - Assert.AreEqual("Float 10.0 exceeds maximum value of 5.", validationEventArgs.Message); + Assert.AreEqual("Float 10.0 exceeds maximum value of 5. Line 1, position 4.", validationEventArgs.Message); Assert.IsNotNull(validationEventArgs); } @@ -418,7 +418,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Float, reader.TokenType); - Assert.AreEqual("Float 1.1 is less than minimum value of 5.", validationEventArgs.Message); + Assert.AreEqual("Float 1.1 is less than minimum value of 5. Line 1, position 3.", validationEventArgs.Message); Assert.IsNotNull(validationEventArgs); } @@ -455,7 +455,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Float, reader.TokenType); - Assert.AreEqual(@"Value 3.0 is not defined in enum.", validationEventArgs.Message); + Assert.AreEqual(@"Value 3.0 is not defined in enum. Line 1, position 13.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndArray, reader.TokenType); @@ -494,7 +494,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Float, reader.TokenType); - Assert.AreEqual(@"Float 4.001 exceeds the maximum allowed number decimal places of 2.", validationEventArgs.Message); + Assert.AreEqual(@"Float 4.001 exceeds the maximum allowed number decimal places of 2. Line 1, position 15.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndArray, reader.TokenType); @@ -527,7 +527,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Null, reader.TokenType); - Assert.AreEqual(@"Value null is not defined in enum.", validationEventArgs.Message); + Assert.AreEqual(@"Value null is not defined in enum. Line 1, position 6.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndArray, reader.TokenType); @@ -564,7 +564,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Boolean, reader.TokenType); - Assert.AreEqual(@"Value false is not defined in enum.", validationEventArgs.Message); + Assert.AreEqual(@"Value false is not defined in enum. Line 1, position 12.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndArray, reader.TokenType); @@ -603,7 +603,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndArray, reader.TokenType); - Assert.AreEqual("Array item count 4 exceeds maximum count of 3.", validationEventArgs.Message); + Assert.AreEqual("Array item count 4 exceeds maximum count of 3. Line 1, position 21.", validationEventArgs.Message); Assert.IsNotNull(validationEventArgs); } @@ -633,7 +633,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndArray, reader.TokenType); - Assert.AreEqual("Array item count 1 is less than minimum count of 2.", validationEventArgs.Message); + Assert.AreEqual("Array item count 1 is less than minimum count of 2. Line 1, position 6.", validationEventArgs.Message); Assert.IsNotNull(validationEventArgs); } @@ -658,7 +658,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.StartArray, reader.TokenType); - Assert.AreEqual(@"Invalid type. Expected String but got Array.", validationEventArgs.Message); + Assert.AreEqual(@"Invalid type. Expected String but got Array. Line 1, position 1.", validationEventArgs.Message); Assert.IsNotNull(validationEventArgs); } @@ -691,7 +691,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Float, reader.TokenType); - Assert.AreEqual(@"Type Float is disallowed.", validationEventArgs.Message); + Assert.AreEqual(@"Type Float is disallowed. Line 1, position 11.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndArray, reader.TokenType); @@ -735,7 +735,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndObject, reader.TokenType); - Assert.AreEqual("Non-optional properties are missing from object: hobbies, age", validationEventArgs.Message); + Assert.AreEqual("Non-optional properties are missing from object: hobbies, age. Line 1, position 16.", validationEventArgs.Message); Assert.IsNotNull(validationEventArgs); } @@ -820,7 +820,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Null, reader.TokenType); Assert.AreEqual(null, reader.Value); - Assert.AreEqual("Property 'additionalProperty1' has not been defined and schema does not allow additional properties.", validationEventArgs.Message); + Assert.AreEqual("Property 'additionalProperty1' has not been defined and the schema does not allow additional properties. Line 1, position 38.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); @@ -829,7 +829,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Null, reader.TokenType); Assert.AreEqual(null, reader.Value); - Assert.AreEqual("Property 'additionalProperty2' has not been defined and schema does not allow additional properties.", validationEventArgs.Message); + Assert.AreEqual("Property 'additionalProperty2' has not been defined and the schema does not allow additional properties. Line 1, position 65.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndObject, reader.TokenType); @@ -861,7 +861,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.String, reader.TokenType); Assert.AreEqual(1, errors.Count); - Assert.AreEqual("String 'The quick brown fox jumps over the lazy dog.' exceeds maximum length of 9.", errors[0]); + Assert.AreEqual("String 'The quick brown fox jumps over the lazy dog.' exceeds maximum length of 9. Line 1, position 46.", errors[0]); Assert.IsNotNull(validationEventArgs); } @@ -933,7 +933,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); Assert.AreEqual("additional", reader.Value.ToString()); - Assert.AreEqual("Property 'additional' has not been defined and schema does not allow additional properties.", validationEventArgs.Message); + Assert.AreEqual("Property 'additional' has not been defined and the schema does not allow additional properties. Line 1, position 62.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.String, reader.TokenType); @@ -942,7 +942,7 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); Assert.AreEqual("additional2", reader.Value.ToString()); - Assert.AreEqual("Property 'additional2' has not been defined and schema does not allow additional properties.", validationEventArgs.Message); + Assert.AreEqual("Property 'additional2' has not been defined and the schema does not allow additional properties. Line 1, position 84.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.String, reader.TokenType); @@ -972,7 +972,7 @@ namespace Newtonsoft.Json.Tests Assert.AreEqual(JsonToken.EndObject, reader.TokenType); Assert.AreEqual(1, errors.Count); - Assert.AreEqual("Non-optional properties are missing from object: secondproperty, firstproperty", errors[0]); + Assert.AreEqual("Non-optional properties are missing from object: secondproperty, firstproperty. Line 1, position 2.", errors[0]); } [Test] @@ -997,15 +997,15 @@ namespace Newtonsoft.Json.Tests Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Integer, reader.TokenType); - Assert.AreEqual("Invalid type. Expected String but got Integer.", validationEventArgs.Message); + Assert.AreEqual("Invalid type. Expected String but got Integer. Line 1, position 3.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.String, reader.TokenType); - Assert.AreEqual("Invalid type. Expected Integer but got String.", validationEventArgs.Message); + Assert.AreEqual("Invalid type. Expected Integer but got String. Line 1, position 7.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.Null, reader.TokenType); - Assert.AreEqual("Index 3 has not been defined and schema does not allow additional items.", validationEventArgs.Message); + Assert.AreEqual("Index 3 has not been defined and the schema does not allow additional items. Line 1, position 14.", validationEventArgs.Message); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndArray, reader.TokenType); @@ -1103,7 +1103,7 @@ namespace Newtonsoft.Json.Tests Assert.AreEqual(JsonToken.String, reader.TokenType); Assert.AreEqual("secasecasecasecaseca", reader.Value.ToString()); Assert.AreEqual(1, errors.Count); - Assert.AreEqual("String 'secasecasecasecaseca' exceeds maximum length of 10.", errors[0]); + Assert.AreEqual("String 'secasecasecasecaseca' exceeds maximum length of 10. Line 3, position 41.", errors[0]); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); @@ -1123,9 +1123,9 @@ namespace Newtonsoft.Json.Tests Assert.AreEqual(JsonToken.String, reader.TokenType); Assert.AreEqual("aaa", reader.Value.ToString()); Assert.AreEqual(4, errors.Count); - Assert.AreEqual("String 'aaa' is less than minimum length of 7.", errors[1]); - Assert.AreEqual("String 'aaa' does not match regex pattern 'hi'.", errors[2]); - Assert.AreEqual("String 'aaa' does not match regex pattern 'hi2u'.", errors[3]); + Assert.AreEqual("String 'aaa' is less than minimum length of 7. Line 5, position 39.", errors[1]); + Assert.AreEqual("String 'aaa' does not match regex pattern 'hi'. Line 5, position 39.", errors[2]); + Assert.AreEqual("String 'aaa' does not match regex pattern 'hi2u'. Line 5, position 39.", errors[3]); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); @@ -1136,7 +1136,7 @@ namespace Newtonsoft.Json.Tests Assert.AreEqual(JsonToken.String, reader.TokenType); Assert.AreEqual("three", reader.Value.ToString()); Assert.AreEqual(5, errors.Count); - Assert.AreEqual("String 'three' is less than minimum length of 6.", errors[4]); + Assert.AreEqual("String 'three' is less than minimum length of 6. Line 6, position 24.", errors[4]); Assert.IsTrue(reader.Read()); Assert.AreEqual(JsonToken.EndObject, reader.TokenType); diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index 602c090a..fd527a54 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -67,6 +67,7 @@ 3.5 + 3.5 diff --git a/Src/Newtonsoft.Json.Tests/Schema/ExtensionsTests.cs b/Src/Newtonsoft.Json.Tests/Schema/ExtensionsTests.cs index bad8accd..1eb0961e 100644 --- a/Src/Newtonsoft.Json.Tests/Schema/ExtensionsTests.cs +++ b/Src/Newtonsoft.Json.Tests/Schema/ExtensionsTests.cs @@ -80,5 +80,31 @@ namespace Newtonsoft.Json.Tests.Schema JToken stringToken = JToken.FromObject("pie lol"); stringToken.Validate(schema); } + + [Test] + public void ValidateFailureWithOutLineInfoBecauseOfEndToken() + { + JsonSchema schema = JsonSchema.Parse("{'properties':{'lol':{}}}"); + JObject o = JObject.Parse("{}"); + + List errors = new List(); + o.Validate(schema, (sender, args) => errors.Add(args.Message)); + + Assert.AreEqual("Non-optional properties are missing from object: lol.", errors[0]); + Assert.AreEqual(1, errors.Count); + } + + [Test] + public void ValidateFailureWithLineInfo() + { + JsonSchema schema = JsonSchema.Parse("{'properties':{'lol':{'type':'string'}}}"); + JObject o = JObject.Parse("{'lol':1}"); + + List errors = new List(); + o.Validate(schema, (sender, args) => errors.Add(args.Message)); + + Assert.AreEqual("Invalid type. Expected String but got Integer. Line 1, position 9.", errors[0]); + Assert.AreEqual(1, errors.Count); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs index 7b34c5fd..3c6bcb7a 100644 --- a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs +++ b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs @@ -24,11 +24,18 @@ #endregion using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Tests.TestObjects; +using Newtonsoft.Json.Utilities; using NUnit.Framework; using Newtonsoft.Json.Schema; +using System.IO; +using System.Linq; +using Newtonsoft.Json.Linq; namespace Newtonsoft.Json.Tests.Schema { @@ -65,6 +72,7 @@ namespace Newtonsoft.Json.Tests.Schema Assert.AreEqual(@"{ ""description"": ""DefaultValueAttributeTestClass description!"", ""type"": ""object"", + ""additionalProperties"": false, ""properties"": { ""TestField1"": { ""type"": ""integer"", @@ -261,5 +269,113 @@ namespace Newtonsoft.Json.Tests.Schema Assert.AreEqual(JsonSchemaType.Object, schema.Properties["Child"].Type); Assert.AreEqual(schema, schema.Properties["Child"]); } + + [Test] + public void GenerateSchemaForType() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + + JsonSchema schema = generator.Generate(typeof(Type)); + + Assert.AreEqual(JsonSchemaType.String, schema.Type); + } + + public class CustomDirectoryInfoSerializer : JsonSerializer + { + protected override Newtonsoft.Json.Serialization.JsonMemberMappingCollection GetMemberMappings(Type objectType) + { + JsonMemberMappingCollection mappings = base.GetMemberMappings(objectType); + + JsonMemberMappingCollection c = new JsonMemberMappingCollection(); + CollectionUtils.AddRange(c, (IEnumerable) mappings.Where(m => m.MappingName != "Root")); + + return c; + } + } + + [Test] + public void GenerateSchemaForDirectoryInfo() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + + JsonSchema schema = generator.Generate(typeof(DirectoryInfo), true); + + string json = schema.ToString(); + + Assert.AreEqual(@"{ + ""id"": ""System.IO.DirectoryInfo"", + ""type"": [ + ""object"", + ""null"" + ], + ""additionalProperties"": false, + ""properties"": { + ""Name"": { + ""type"": [ + ""string"", + ""null"" + ] + }, + ""Parent"": { + ""$ref"": ""System.IO.DirectoryInfo"" + }, + ""Exists"": { + ""type"": ""boolean"" + }, + ""Root"": { + ""$ref"": ""System.IO.DirectoryInfo"" + }, + ""FullName"": { + ""type"": [ + ""string"", + ""null"" + ] + }, + ""Extension"": { + ""type"": [ + ""string"", + ""null"" + ] + }, + ""CreationTime"": { + ""type"": ""string"" + }, + ""CreationTimeUtc"": { + ""type"": ""string"" + }, + ""LastAccessTime"": { + ""type"": ""string"" + }, + ""LastAccessTimeUtc"": { + ""type"": ""string"" + }, + ""LastWriteTime"": { + ""type"": ""string"" + }, + ""LastWriteTimeUtc"": { + ""type"": ""string"" + }, + ""Attributes"": { + ""type"": ""integer"" + } + } +}", json); + + DirectoryInfo temp = new DirectoryInfo(@"c:\temp"); + + JsonTokenWriter jsonWriter = new JsonTokenWriter(); + CustomDirectoryInfoSerializer customSerializer = new CustomDirectoryInfoSerializer(); + customSerializer.Converters.Add(new IsoDateTimeConverter()); + customSerializer.Serialize(jsonWriter, temp); + + List errors = new List(); + jsonWriter.Token.Validate(schema, (sender, args) => errors.Add(args.Message)); + + Assert.AreEqual(2, errors.Count); + Assert.AreEqual("Non-optional properties are missing from object: Root.", errors[0]); + Assert.AreEqual("Non-optional properties are missing from object: Root.", errors[1]); + } } } diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/DefaultValueAttributeTestClass.cs b/Src/Newtonsoft.Json.Tests/TestObjects/DefaultValueAttributeTestClass.cs index 861c709a..eeefa5e3 100644 --- a/Src/Newtonsoft.Json.Tests/TestObjects/DefaultValueAttributeTestClass.cs +++ b/Src/Newtonsoft.Json.Tests/TestObjects/DefaultValueAttributeTestClass.cs @@ -30,7 +30,7 @@ namespace Newtonsoft.Json.Tests.TestObjects #if !PocketPC [Description("DefaultValueAttributeTestClass description!")] #endif - public class DefaultValueAttributeTestClass + public sealed class DefaultValueAttributeTestClass { [DefaultValue("TestProperty1Value")] public string TestProperty1 { get; set; } diff --git a/Src/Newtonsoft.Json/IJsonLineInfo.cs b/Src/Newtonsoft.Json/IJsonLineInfo.cs new file mode 100644 index 00000000..6c821adb --- /dev/null +++ b/Src/Newtonsoft.Json/IJsonLineInfo.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + public interface IJsonLineInfo + { + bool HasLineInfo(); + + int LineNumber { get; } + int LinePosition { get; } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonReaderException.cs b/Src/Newtonsoft.Json/JsonReaderException.cs index 9940c670..3d6efe44 100644 --- a/Src/Newtonsoft.Json/JsonReaderException.cs +++ b/Src/Newtonsoft.Json/JsonReaderException.cs @@ -34,6 +34,9 @@ namespace Newtonsoft.Json /// public class JsonReaderException : Exception { + public int LineNumber { get; private set; } + public int LinePosition { get; private set; } + /// /// Initializes a new instance of the class. /// @@ -61,5 +64,12 @@ namespace Newtonsoft.Json : base(message, innerException) { } + + internal JsonReaderException(string message, Exception innerException, int lineNumber, int linePosition) + : base(message, innerException) + { + LineNumber = lineNumber; + LinePosition = linePosition; + } } } diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index db67b261..6cd83fb2 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -29,19 +29,21 @@ using System.Text; using System.IO; using System.Xml; using System.Globalization; +using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json { /// /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. /// - public class JsonTextReader : JsonReader + public class JsonTextReader : JsonReader, IJsonLineInfo { - private TextReader _reader; + private readonly TextReader _reader; + private readonly StringBuffer _buffer; private char _currentChar; - - // current Token data - private StringBuffer _buffer; + private int _currentLinePosition; + private int _currentLineNumber; + private bool _currentCharCarriageReturn; /// /// Initializes a new instance of the class with the specified . @@ -54,6 +56,7 @@ namespace Newtonsoft.Json _reader = reader; _buffer = new StringBuffer(4096); + _currentLineNumber = 1; } private void ParseString(char quote) @@ -100,7 +103,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Unterminated string. Expected delimiter: " + quote); + throw CreateJsonReaderException("Unterminated string. Expected delimiter: {0}. Line {1}, position {2}.", quote, _currentLineNumber, _currentLinePosition); } break; case '"': @@ -130,7 +133,7 @@ namespace Newtonsoft.Json } if (!stringTerminated) - throw new JsonReaderException("Unterminated string. Expected delimiter: " + quote); + throw CreateJsonReaderException("Unterminated string. Expected delimiter: {0}. Line {1}, position {2}.", quote, _currentLineNumber, _currentLinePosition); ClearCurrentChar(); string text = _buffer.ToString(); @@ -147,6 +150,12 @@ namespace Newtonsoft.Json } } + private JsonReaderException CreateJsonReaderException(string format, params object[] args) + { + string message = format.FormatWith(CultureInfo.InvariantCulture, args); + return new JsonReaderException(message, null, _currentLineNumber, _currentLinePosition); + } + /// /// Sets the current token and value. /// @@ -218,20 +227,37 @@ namespace Newtonsoft.Json SetToken(JsonToken.Date, dateTime); } + private const int LineFeedValue = StringUtils.LineFeed; + private const int CarriageReturnValue = StringUtils.CarriageReturn; + private bool MoveNext() { int value = _reader.Read(); - if (value != -1) + switch (value) { - _currentChar = (char)value; - //_testBuffer.Append(_currentChar); - return true; - } - else - { - return false; + case -1: + return false; + case CarriageReturnValue: + _currentLineNumber++; + _currentLinePosition = 0; + _currentCharCarriageReturn = true; + break; + case LineFeedValue: + if (!_currentCharCarriageReturn) + _currentLineNumber++; + + _currentLinePosition = 0; + _currentCharCarriageReturn = false; + break; + default: + _currentLinePosition++; + _currentCharCarriageReturn = false; + break; } + + _currentChar = (char)value; + return true; } private bool HasNext() @@ -300,7 +326,7 @@ namespace Newtonsoft.Json case State.Error: break; default: - throw new JsonReaderException("Unexpected state: " + CurrentState); + throw CreateJsonReaderException("Unexpected state: {0}. Line {1}, position {2}.", CurrentState, _currentLineNumber, _currentLinePosition); } } } @@ -339,7 +365,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("After parsing a value an unexpected character was encoutered: " + _currentChar); + throw CreateJsonReaderException("After parsing a value an unexpected character was encoutered: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); } break; } @@ -395,7 +421,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Invalid property identifier character: " + _currentChar); + throw CreateJsonReaderException("Invalid property identifier character: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); } // finished property. move to colon @@ -426,7 +452,7 @@ namespace Newtonsoft.Json } } - throw new JsonReaderException("Unclosed quoted property. Expected: " + quoteChar); + throw CreateJsonReaderException("Unclosed quoted property. Expected: {0}. Line {1}, position {2}.", quoteChar, _currentLineNumber, _currentLinePosition); } private bool ValidIdentifierChar(char value) @@ -451,7 +477,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Invalid JavaScript property identifier character: " + _currentChar); + throw CreateJsonReaderException("Invalid JavaScript property identifier character: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); } } } @@ -482,11 +508,11 @@ namespace Newtonsoft.Json else if (next == 'e') ParseConstructor(); else - throw new JsonReaderException("Unexpected character encountered while parsing value: " + _currentChar); + throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); } else { - throw new JsonReaderException("Unexpected end"); + throw CreateJsonReaderException("Unexpected end. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); } return true; case 'N': @@ -538,7 +564,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Unexpected character encountered while parsing value: " + _currentChar); + throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); } break; } @@ -574,7 +600,7 @@ namespace Newtonsoft.Json EatWhitespace(false); if (_currentChar != '(') - throw new JsonReaderException("Unexpected character while parsing constructor: " + _currentChar); + throw CreateJsonReaderException("Unexpected character while parsing constructor: {0}. Line {1}, position {2}.", _currentChar, _currentLineNumber, _currentLinePosition); string constructorName = _buffer.ToString(); _buffer.Position = 0; @@ -655,7 +681,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Error parsing comment. Expected: *"); + throw CreateJsonReaderException("Error parsing comment. Expected: *. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); } SetToken(JsonToken.Comment, _buffer.ToString()); @@ -735,7 +761,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Error parsing boolean value."); + throw CreateJsonReaderException("Error parsing boolean value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); } } @@ -747,7 +773,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Error parsing null value."); + throw CreateJsonReaderException("Error parsing null value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); } } @@ -759,7 +785,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Error parsing undefined value."); + throw CreateJsonReaderException("Error parsing undefined value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); } } @@ -771,7 +797,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Error parsing boolean value."); + throw CreateJsonReaderException("Error parsing boolean value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); } } @@ -783,7 +809,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Error parsing negative infinity value."); + throw CreateJsonReaderException("Error parsing negative infinity value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); } } @@ -795,7 +821,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Error parsing positive infinity value."); + throw CreateJsonReaderException("Error parsing positive infinity value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); } } @@ -807,7 +833,7 @@ namespace Newtonsoft.Json } else { - throw new JsonReaderException("Error parsing NaN value."); + throw CreateJsonReaderException("Error parsing NaN value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); } } @@ -824,5 +850,26 @@ namespace Newtonsoft.Json if (_buffer != null) _buffer.Clear(); } + + public bool HasLineInfo() + { + return true; + } + + public int LineNumber + { + get + { + if (CurrentState == State.Start) + return 0; + + return _currentLineNumber; + } + } + + public int LinePosition + { + get { return _currentLinePosition; } + } } } diff --git a/Src/Newtonsoft.Json/JsonValidatingReader.cs b/Src/Newtonsoft.Json/JsonValidatingReader.cs index faa2b62e..d0021689 100644 --- a/Src/Newtonsoft.Json/JsonValidatingReader.cs +++ b/Src/Newtonsoft.Json/JsonValidatingReader.cs @@ -35,7 +35,7 @@ using System.Text.RegularExpressions; namespace Newtonsoft.Json { - public class JsonValidatingReader : JsonReader + public class JsonValidatingReader : JsonReader, IJsonLineInfo { private class SchemaScope { @@ -186,7 +186,13 @@ namespace Newtonsoft.Json private void RaiseError(string message, JsonSchemaModel schema) { - OnValidationEvent(new JsonSchemaException(message)); + IJsonLineInfo lineInfo = this; + + string exceptionMessage = (lineInfo.HasLineInfo()) + ? message + " Line {0}, position {1}.".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition) + : message; + + OnValidationEvent(new JsonSchemaException(exceptionMessage, null, lineInfo.LineNumber, lineInfo.LinePosition)); } private void OnValidationEvent(JsonSchemaException exception) @@ -362,7 +368,7 @@ namespace Newtonsoft.Json requiredProperties.Where(kv => !kv.Value).Select(kv => kv.Key).ToList(); if (unmatchedRequiredProperties.Count > 0) - RaiseError("Non-optional properties are missing from object: {0}".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", unmatchedRequiredProperties.ToArray())), schema); + RaiseError("Non-optional properties are missing from object: {0}.".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", unmatchedRequiredProperties.ToArray())), schema); } } @@ -456,7 +462,7 @@ namespace Newtonsoft.Json _currentScope.ArrayItemCount++; if (CurrentSchema != null && CurrentSchema.Items != null && CurrentSchema.Items.Count > 1 && _currentScope.ArrayItemCount >= CurrentSchema.Items.Count) - RaiseError("Index {0} has not been defined and schema does not allow additional items.".FormatWith(CultureInfo.InvariantCulture, _currentScope.ArrayItemCount), CurrentSchema); + RaiseError("Index {0} has not been defined and the schema does not allow additional items.".FormatWith(CultureInfo.InvariantCulture, _currentScope.ArrayItemCount), CurrentSchema); } } @@ -498,7 +504,7 @@ namespace Newtonsoft.Json if (!schema.AllowAdditionalProperties && !definedProperties.Contains(propertyName)) { - RaiseError("Property '{0}' has not been defined and schema does not allow additional properties.".FormatWith(CultureInfo.InvariantCulture, propertyName), schema); + RaiseError("Property '{0}' has not been defined and the schema does not allow additional properties.".FormatWith(CultureInfo.InvariantCulture, propertyName), schema); } } @@ -531,5 +537,29 @@ namespace Newtonsoft.Json return true; } + + bool IJsonLineInfo.HasLineInfo() + { + IJsonLineInfo lineInfo = _reader as IJsonLineInfo; + return (lineInfo != null) ? lineInfo.HasLineInfo() : false; + } + + int IJsonLineInfo.LineNumber + { + get + { + IJsonLineInfo lineInfo = _reader as IJsonLineInfo; + return (lineInfo != null) ? lineInfo.LineNumber : 0; + } + } + + int IJsonLineInfo.LinePosition + { + get + { + IJsonLineInfo lineInfo = _reader as IJsonLineInfo; + return (lineInfo != null) ? lineInfo.LinePosition : 0; + } + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JArray.cs b/Src/Newtonsoft.Json/Linq/JArray.cs index 5466efe9..6e7426ac 100644 --- a/Src/Newtonsoft.Json/Linq/JArray.cs +++ b/Src/Newtonsoft.Json/Linq/JArray.cs @@ -105,15 +105,14 @@ namespace Newtonsoft.Json.Linq throw new Exception("Error reading JArray from JsonReader."); } if (reader.TokenType != JsonToken.StartArray) - throw new Exception( - "Error reading JArray from JsonReader. Current JsonReader item is not an array: {0}".FormatWith( - CultureInfo.InvariantCulture, reader.TokenType)); + throw new Exception("Error reading JArray from JsonReader. Current JsonReader item is not an array: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + JArray a = new JArray(); + a.SetLineInfo(reader as IJsonLineInfo); if (!reader.Read()) throw new Exception("Error reading JArray from JsonReader."); - - JArray a = new JArray(); a.ReadContentFrom(reader); return a; diff --git a/Src/Newtonsoft.Json/Linq/JConstructor.cs b/Src/Newtonsoft.Json/Linq/JConstructor.cs index dba14b85..069048bd 100644 --- a/Src/Newtonsoft.Json/Linq/JConstructor.cs +++ b/Src/Newtonsoft.Json/Linq/JConstructor.cs @@ -175,11 +175,10 @@ namespace Newtonsoft.Json.Linq } if (reader.TokenType != JsonToken.StartConstructor) - throw new Exception( - "Error reading JConstructor from JsonReader. Current JsonReader item is not a constructor: {0}".FormatWith( - CultureInfo.InvariantCulture, reader.TokenType)); + throw new Exception("Error reading JConstructor from JsonReader. Current JsonReader item is not a constructor: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); JConstructor c = new JConstructor((string)reader.Value); + c.SetLineInfo(reader as IJsonLineInfo); if (!reader.Read()) throw new Exception("Error reading JConstructor from JsonReader."); diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 043c6d98..031fb0b0 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -365,6 +365,7 @@ namespace Newtonsoft.Json.Linq internal void ReadContentFrom(JsonReader r) { ValidationUtils.ArgumentNotNull(r, "r"); + IJsonLineInfo lineInfo = r as IJsonLineInfo; JContainer parent = this; @@ -385,6 +386,7 @@ namespace Newtonsoft.Json.Linq break; case JsonToken.StartArray: JArray a = new JArray(); + a.SetLineInfo(lineInfo); parent.AddObjectSkipNotify(a); parent = a; break; @@ -397,6 +399,7 @@ namespace Newtonsoft.Json.Linq break; case JsonToken.StartObject: JObject o = new JObject(); + o.SetLineInfo(lineInfo); parent.AddObjectSkipNotify(o); parent = o; break; @@ -408,6 +411,7 @@ namespace Newtonsoft.Json.Linq break; case JsonToken.StartConstructor: JConstructor constructor = new JConstructor(r.Value.ToString()); + constructor.SetLineInfo(constructor); parent.AddObjectSkipNotify(constructor); parent = constructor; break; @@ -422,19 +426,28 @@ namespace Newtonsoft.Json.Linq case JsonToken.Float: case JsonToken.Date: case JsonToken.Boolean: - parent.AddObjectSkipNotify(new JValue(r.Value)); + JValue v = new JValue(r.Value); + v.SetLineInfo(lineInfo); + parent.AddObjectSkipNotify(v); break; case JsonToken.Comment: - parent.AddObjectSkipNotify(JValue.CreateComment(r.Value.ToString())); + v = JValue.CreateComment(r.Value.ToString()); + v.SetLineInfo(lineInfo); + parent.AddObjectSkipNotify(v); break; case JsonToken.Null: - parent.AddObjectSkipNotify(new JValue(null, JsonTokenType.Null)); + v = new JValue(null, JsonTokenType.Null); + v.SetLineInfo(lineInfo); + parent.AddObjectSkipNotify(v); break; case JsonToken.Undefined: - parent.AddObjectSkipNotify(new JValue(null, JsonTokenType.Undefined)); + v = new JValue(null, JsonTokenType.Undefined); + v.SetLineInfo(lineInfo); + parent.AddObjectSkipNotify(v); break; case JsonToken.PropertyName: JProperty property = new JProperty(r.Value.ToString()); + property.SetLineInfo(lineInfo); parent.AddObjectSkipNotify(property); parent = property; break; diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 99315540..3798f59e 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -196,10 +196,12 @@ namespace Newtonsoft.Json.Linq "Error reading JObject from JsonReader. Current JsonReader item is not an object: {0}".FormatWith( CultureInfo.InvariantCulture, reader.TokenType)); + JObject o = new JObject(); + o.SetLineInfo(reader as IJsonLineInfo); + if (!reader.Read()) throw new Exception("Error reading JObject from JsonReader."); - JObject o = new JObject(); o.ReadContentFrom(reader); return o; diff --git a/Src/Newtonsoft.Json/Linq/JProperty.cs b/Src/Newtonsoft.Json/Linq/JProperty.cs index 61c01101..4ac2ccdc 100644 --- a/Src/Newtonsoft.Json/Linq/JProperty.cs +++ b/Src/Newtonsoft.Json/Linq/JProperty.cs @@ -186,6 +186,7 @@ namespace Newtonsoft.Json.Linq CultureInfo.InvariantCulture, reader.TokenType)); JProperty p = new JProperty((string)reader.Value); + p.SetLineInfo(reader as IJsonLineInfo); if (!reader.Read()) throw new Exception("Error reading JProperty from JsonReader."); diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index beb7201b..ff963b99 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -38,12 +38,15 @@ namespace Newtonsoft.Json.Linq /// /// Represents an abstract JSON token. /// - public abstract class JToken : IJEnumerable + public abstract class JToken : IJEnumerable, IJsonLineInfo { private JContainer _parent; internal JToken _next; private static JTokenEqualityComparer _equalityComparer; + private int? _lineNumber; + private int? _linePosition; + /// /// Gets a comparer that can compare two tokens for value equality. /// @@ -797,5 +800,34 @@ namespace Newtonsoft.Json.Linq // TODO: loading constructor and parameters? throw new Exception("Error reading JToken from JsonReader. Unexpected token: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); } + + internal void SetLineInfo(IJsonLineInfo lineInfo) + { + if (lineInfo == null || !lineInfo.HasLineInfo()) + return; + + SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); + } + + internal void SetLineInfo(int lineNumber, int linePosition) + { + _lineNumber = lineNumber; + _linePosition = linePosition; + } + + bool IJsonLineInfo.HasLineInfo() + { + return (_lineNumber != null && _linePosition != null); + } + + int IJsonLineInfo.LineNumber + { + get { return _lineNumber ?? 0; } + } + + int IJsonLineInfo.LinePosition + { + get { return _linePosition ?? 0; } + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JsonTokenReader.cs b/Src/Newtonsoft.Json/Linq/JsonTokenReader.cs index 6cb6bae7..ce1814a6 100644 --- a/Src/Newtonsoft.Json/Linq/JsonTokenReader.cs +++ b/Src/Newtonsoft.Json/Linq/JsonTokenReader.cs @@ -9,7 +9,7 @@ namespace Newtonsoft.Json.Linq /// /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. /// - public class JsonTokenReader : JsonReader + public class JsonTokenReader : JsonReader, IJsonLineInfo { private readonly JToken _root; private JToken _parent; @@ -75,6 +75,11 @@ namespace Newtonsoft.Json.Linq return false; } + private bool IsEndElement + { + get { return (_current == _parent); } + } + private JsonToken? GetEndToken(JContainer c) { switch (c.Type) @@ -171,5 +176,44 @@ namespace Newtonsoft.Json.Linq throw MiscellaneousUtils.CreateArgumentOutOfRangeException("Type", token.Type, "Unexpected JsonTokenType."); } } + + bool IJsonLineInfo.HasLineInfo() + { + if (CurrentState == State.Start) + return false; + + IJsonLineInfo info = IsEndElement ? null : _current; + return (info != null && info.HasLineInfo()); + } + + int IJsonLineInfo.LineNumber + { + get + { + if (CurrentState == State.Start) + return 0; + + IJsonLineInfo info = IsEndElement ? null : _current; + if (info != null) + return info.LineNumber; + + return 0; + } + } + + int IJsonLineInfo.LinePosition + { + get + { + if (CurrentState == State.Start) + return 0; + + IJsonLineInfo info = IsEndElement ? null : _current; + if (info != null) + return info.LinePosition; + + return 0; + } + } } } diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj index fe3dc84a..a656114a 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj @@ -56,6 +56,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index f49c490a..9c4d5ae0 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -55,6 +55,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index c3ea762b..a9e265b0 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -74,6 +74,7 @@ + diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs index 7afaf942..881cbac4 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs @@ -32,6 +32,9 @@ namespace Newtonsoft.Json.Schema /// public class JsonSchemaException : Exception { + public int LineNumber { get; private set; } + public int LinePosition { get; private set; } + /// /// Initializes a new instance of the class. /// @@ -59,5 +62,12 @@ namespace Newtonsoft.Json.Schema : base(message, innerException) { } + + internal JsonSchemaException(string message, Exception innerException, int lineNumber, int linePosition) + : base(message, innerException) + { + LineNumber = lineNumber; + LinePosition = linePosition; + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs index 71fd4db8..6d142fff 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs @@ -253,8 +253,11 @@ namespace Newtonsoft.Json.Schema CurrentSchema.Properties.Add(mapping.MappingName, propertySchema); } } + + if (type.IsSealed) + CurrentSchema.AllowAdditionalProperties = false; } - else if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum) + else if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum && !type.IsDefined(typeof(FlagsAttribute), true)) { CurrentSchema.Enum = new List(); CurrentSchema.Options = new Dictionary(); diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index d7d1dc4e..02b53ff2 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -59,11 +59,14 @@ namespace Newtonsoft.Json.Utilities if (initialType == typeof(Guid) && (targetType == typeof(Guid) || targetType == typeof(string))) return true; + if (initialType == typeof(Type) && targetType == typeof(string)) + return true; + #if !PocketPC // see if source or target types have a TypeConverter that converts between the two TypeConverter toConverter = GetConverter(initialType); - if (toConverter != null && toConverter.CanConvertTo(targetType)) + if (toConverter != null && !IsComponentConverter(toConverter) && toConverter.CanConvertTo(targetType)) { if (allowTypeNameToString || toConverter.GetType() != typeof(TypeConverter)) return true; @@ -71,7 +74,7 @@ namespace Newtonsoft.Json.Utilities TypeConverter fromConverter = GetConverter(targetType); - if (fromConverter != null && fromConverter.CanConvertFrom(initialType)) + if (fromConverter != null && !IsComponentConverter(fromConverter) && fromConverter.CanConvertFrom(initialType)) return true; #endif @@ -85,6 +88,15 @@ namespace Newtonsoft.Json.Utilities return false; } + private static bool IsComponentConverter(TypeConverter converter) + { +#if !SILVERLIGHT && !PocketPC + return (converter is ComponentConverter); +#else + return false; +#endif + } + #region Convert /// /// Converts the value to the specified type. diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index a79bb489..f67bda32 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -231,7 +231,7 @@ namespace Newtonsoft.Json.Utilities return genericListType.GetGenericArguments()[0]; } - else if (typeof(IList).IsAssignableFrom(type)) + else if (typeof(ICollection).IsAssignableFrom(type)) { return null; }