From 89883dc57be514194cc54dd2847f208e1f499721 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 9 Nov 2012 17:10:32 +1300 Subject: [PATCH] -Added ITraceWriter -Fixed .NET 2.0 build using DateTimeOffset --- Build/build.ps1 | 2 +- .../Newtonsoft.Json.Tests.Net20.csproj | 1 + .../Newtonsoft.Json.Tests.Net35.csproj | 1 + .../Newtonsoft.Json.Tests.Portable.csproj | 1 + .../Newtonsoft.Json.Tests.Silverlight.csproj | 1 + .../Newtonsoft.Json.Tests.WinRT.csproj | 1 + .../Newtonsoft.Json.Tests.csproj | 1 + Src/Newtonsoft.Json.Tests/PerformanceTests.cs | 4 +- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/TraceWriterTests.cs | 510 ++++++++++++++++++ Src/Newtonsoft.Json/JsonException.cs | 23 - Src/Newtonsoft.Json/JsonPosition.cs | 26 + Src/Newtonsoft.Json/JsonReader.cs | 10 +- Src/Newtonsoft.Json/JsonReaderException.cs | 2 +- .../JsonSerializationException.cs | 2 +- Src/Newtonsoft.Json/JsonSerializer.cs | 19 + Src/Newtonsoft.Json/JsonSerializerSettings.cs | 6 + Src/Newtonsoft.Json/JsonWriterException.cs | 2 +- Src/Newtonsoft.Json/Linq/JValue.cs | 2 + .../Newtonsoft.Json.Net20.csproj | 1 + .../Newtonsoft.Json.Net35.csproj | 2 + .../Newtonsoft.Json.Portable.csproj | 2 + .../Newtonsoft.Json.Silverlight.csproj | 2 + .../Newtonsoft.Json.WinRT.csproj | 2 + Src/Newtonsoft.Json/Newtonsoft.Json.csproj | 2 + .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/ErrorContext.cs | 2 + .../Serialization/ITraceWriter.cs | 27 + .../JsonSerializerInternalBase.cs | 29 +- .../JsonSerializerInternalReader.cs | 130 +++-- .../JsonSerializerInternalWriter.cs | 113 ++-- .../Serialization/JsonSerializerProxy.cs | 6 + Src/Newtonsoft.Json/TraceLevel.cs | 33 ++ Src/Newtonsoft.Json/Utilities/ConvertUtils.cs | 13 +- Src/Newtonsoft.Json/Utilities/LinqBridge.cs | 3 +- .../Utilities/ReflectionUtils.cs | 2 + 36 files changed, 870 insertions(+), 117 deletions(-) create mode 100644 Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs create mode 100644 Src/Newtonsoft.Json/Serialization/ITraceWriter.cs create mode 100644 Src/Newtonsoft.Json/TraceLevel.cs diff --git a/Build/build.ps1 b/Build/build.ps1 index 8d64dea3..4602aee4 100644 --- a/Build/build.ps1 +++ b/Build/build.ps1 @@ -20,7 +20,7 @@ @{Name = "Newtonsoft.Json.Portable"; TestsName = "Newtonsoft.Json.Tests.Portable"; Constants="PORTABLE"; FinalDir="Portable"; NuGetDir = "portable-net40+sl4+wp7+win8"; Framework="net-4.0"; Sign=$true}, @{Name = "Newtonsoft.Json.WinRT"; TestsName = $null; Constants="NETFX_CORE"; FinalDir="WinRT"; NuGetDir = "winrt45"; Framework="net-4.5"; Sign=$true}, - @{Name = "Newtonsoft.Json.WindowsPhone"; TestsName = $null; Constants="SILVERLIGHT;WINDOWS_PHONE"; FinalDir="WindowsPhone"; NuGetDir = "sl3-wp,sl4-windowsphone71"; Framework="net-4.0"; Sign=$true}, + #@{Name = "Newtonsoft.Json.WindowsPhone"; TestsName = $null; Constants="SILVERLIGHT;WINDOWS_PHONE"; FinalDir="WindowsPhone"; NuGetDir = "sl3-wp,sl4-windowsphone71"; Framework="net-4.0"; Sign=$true}, @{Name = "Newtonsoft.Json.Silverlight"; TestsName = "Newtonsoft.Json.Tests.Silverlight"; Constants="SILVERLIGHT"; FinalDir="Silverlight"; NuGetDir = "sl4"; 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.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj index 1e1aeb83..9fc726da 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj @@ -99,6 +99,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj index 7135f3fe..0ff3fca1 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj @@ -141,6 +141,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj index 311db840..0f9a2ec9 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj @@ -134,6 +134,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index f3c761dd..d4256caa 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -139,6 +139,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WinRT.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WinRT.csproj index 2803ba5c..ed469769 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WinRT.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WinRT.csproj @@ -173,6 +173,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index eddf8a79..f89253c0 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -153,6 +153,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index 94901795..7b5c016c 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -64,8 +64,8 @@ namespace Newtonsoft.Json.Tests [TestFixture] public class PerformanceTests : TestFixtureBase { - private const int Iterations = 100; - //private const int Iterations = 5000; + //private const int Iterations = 100; + private const int Iterations = 5000; #region Data diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index e2e3fbf2..bcbdae61 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -76,5 +76,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.10.15504")] +[assembly: AssemblyFileVersion("4.5.10.15509")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs new file mode 100644 index 00000000..f024b476 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Serialization/TraceWriterTests.cs @@ -0,0 +1,510 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters; +using System.Text; +#if !NETFX_CORE +using NUnit.Framework; +#else +using Microsoft.VisualStudio.TestPlatform.UnitTestFramework; +using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute; +using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute; +#endif +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Tests.TestObjects; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif + +namespace Newtonsoft.Json.Tests.Serialization +{ + [TestFixture] + public class TraceWriterTests : TestFixtureBase + { + [Test] + public void Serialize() + { + var traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Info + }; + + string json = + JsonConvert.SerializeObject( + new TraceTestObject + { + StringArray = new[] {"1", "2"}, + IntList = new List {1, 2}, + Version = new Version(1, 2, 3, 4), + StringDictionary = + new Dictionary + { + {"1", "!"}, + {"Two", "!!"}, + {"III", "!!!"} + } + }, + new JsonSerializerSettings + { + TraceWriter = traceWriter, + Formatting = Formatting.Indented + }); + + Assert.AreEqual("Started serializing Newtonsoft.Json.Tests.Serialization.TraceTestObject. Path ''.", traceWriter.TraceRecords[0].Message); + Assert.AreEqual("Started serializing System.Collections.Generic.List`1[System.Int32]. Path 'IntList'.", traceWriter.TraceRecords[1].Message); + Assert.AreEqual("Finished serializing System.Collections.Generic.List`1[System.Int32]. Path 'IntList'.", traceWriter.TraceRecords[2].Message); + Assert.AreEqual("Started serializing System.String[]. Path 'StringArray'.", traceWriter.TraceRecords[3].Message); + Assert.AreEqual("Finished serializing System.String[]. Path 'StringArray'.", traceWriter.TraceRecords[4].Message); + Assert.AreEqual("Started serializing System.Version. Path 'Version'.", traceWriter.TraceRecords[5].Message); + Assert.AreEqual("Finished serializing System.Version. Path 'Version'.", traceWriter.TraceRecords[6].Message); + Assert.AreEqual("Started serializing System.Collections.Generic.Dictionary`2[System.String,System.String]. Path 'StringDictionary'.", traceWriter.TraceRecords[7].Message); + Assert.AreEqual("Finished serializing System.Collections.Generic.Dictionary`2[System.String,System.String]. Path 'StringDictionary'.", traceWriter.TraceRecords[8].Message); + Assert.AreEqual("Finished serializing Newtonsoft.Json.Tests.Serialization.TraceTestObject. Path ''.", traceWriter.TraceRecords[9].Message); + } + + [Test] + public void Deserialize() + { + InMemoryTraceWriter traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Info + }; + + TraceTestObject o2 = JsonConvert.DeserializeObject( + @"{ + ""IntList"": [ + 1, + 2 + ], + ""StringArray"": [ + ""1"", + ""2"" + ], + ""Version"": { + ""Major"": 1, + ""Minor"": 2, + ""Build"": 3, + ""Revision"": 4, + ""MajorRevision"": 0, + ""MinorRevision"": 4 + }, + ""StringDictionary"": { + ""1"": ""!"", + ""Two"": ""!!"", + ""III"": ""!!!"" + } +}", + new JsonSerializerSettings + { + TraceWriter = traceWriter + }); + + Assert.AreEqual(2, o2.IntList.Count); + Assert.AreEqual(2, o2.StringArray.Length); + Assert.AreEqual(1, o2.Version.Major); + Assert.AreEqual(2, o2.Version.Minor); + Assert.AreEqual(3, o2.StringDictionary.Count); + + Assert.AreEqual("Started deserializing Newtonsoft.Json.Tests.Serialization.TraceTestObject. Path 'IntList', line 2, position 13.", traceWriter.TraceRecords[0].Message); + Assert.AreEqual("Started deserializing System.Collections.Generic.IList`1[System.Int32]. Path 'IntList', line 2, position 15.", traceWriter.TraceRecords[1].Message); + Assert.AreEqual("Finished deserializing System.Collections.Generic.IList`1[System.Int32]. Path 'IntList', line 5, position 4.", traceWriter.TraceRecords[2].Message); + Assert.AreEqual("Started deserializing System.String[]. Path 'StringArray', line 6, position 19.", traceWriter.TraceRecords[3].Message); + Assert.AreEqual("Finished deserializing System.String[]. Path 'StringArray', line 9, position 4.", traceWriter.TraceRecords[4].Message); + Assert.AreEqual("Deserializing System.Version using a non-default constructor 'Void .ctor(Int32, Int32, Int32, Int32)'. Path 'Version.Major', line 11, position 13.", traceWriter.TraceRecords[5].Message); + Assert.AreEqual("Started deserializing System.Version. Path 'Version', line 17, position 4.", traceWriter.TraceRecords[6].Message); + Assert.AreEqual("Finished deserializing System.Version. Path 'Version', line 17, position 4.", traceWriter.TraceRecords[7].Message); + Assert.AreEqual("Started deserializing System.Collections.Generic.IDictionary`2[System.String,System.String]. Path 'StringDictionary.1', line 19, position 9.", traceWriter.TraceRecords[8].Message); + Assert.AreEqual("Finished deserializing System.Collections.Generic.IDictionary`2[System.String,System.String]. Path 'StringDictionary', line 22, position 4.", traceWriter.TraceRecords[9].Message); + Assert.AreEqual("Finished deserializing Newtonsoft.Json.Tests.Serialization.TraceTestObject. Path '', line 23, position 2.", traceWriter.TraceRecords[10].Message); + } + + [Test] + public void ErrorDeserializing() + { + string json = @"{""Integer"":""hi""}"; + + var traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Info + }; + + ExceptionAssert.Throws( + "Could not convert string to integer: hi. Path 'Integer', line 1, position 15.", + () => + { + JsonConvert.DeserializeObject( + json, + new JsonSerializerSettings + { + TraceWriter = traceWriter + }); + }); + + Assert.AreEqual(2, traceWriter.TraceRecords.Count); + + Assert.AreEqual(TraceLevel.Info, traceWriter.TraceRecords[0].Level); + Assert.AreEqual("Started deserializing Newtonsoft.Json.Tests.Serialization.IntegerTestClass. Path 'Integer', line 1, position 11.", traceWriter.TraceRecords[0].Message); + + Assert.AreEqual(TraceLevel.Error, traceWriter.TraceRecords[1].Level); + Assert.AreEqual("Error deserializing Newtonsoft.Json.Tests.Serialization.IntegerTestClass. Could not convert string to integer: hi. Path 'Integer', line 1, position 15.", traceWriter.TraceRecords[1].Message); + } + + [Test] + public void ErrorDeserializingNested() + { + string json = @"{""IntList"":[1, ""two""]}"; + + var traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Info + }; + + ExceptionAssert.Throws( + "Could not convert string to integer: two. Path 'IntList[1]', line 1, position 20.", + () => + { + JsonConvert.DeserializeObject( + json, + new JsonSerializerSettings + { + TraceWriter = traceWriter + }); + }); + + Assert.AreEqual(3, traceWriter.TraceRecords.Count); + + Assert.AreEqual(TraceLevel.Info, traceWriter.TraceRecords[0].Level); + Assert.AreEqual("Started deserializing Newtonsoft.Json.Tests.Serialization.TraceTestObject. Path 'IntList', line 1, position 11.", traceWriter.TraceRecords[0].Message); + + Assert.AreEqual(TraceLevel.Info, traceWriter.TraceRecords[1].Level); + Assert.AreEqual("Started deserializing System.Collections.Generic.IList`1[System.Int32]. Path 'IntList', line 1, position 12.", traceWriter.TraceRecords[1].Message); + + Assert.AreEqual(TraceLevel.Error, traceWriter.TraceRecords[2].Level); + Assert.AreEqual("Error deserializing System.Collections.Generic.IList`1[System.Int32]. Could not convert string to integer: two. Path 'IntList[1]', line 1, position 20.", traceWriter.TraceRecords[2].Message); + } + + [Test] + public void SerializeDictionarysWithPreserveObjectReferences() + { + PreserveReferencesHandlingTests.CircularDictionary circularDictionary = new PreserveReferencesHandlingTests.CircularDictionary(); + circularDictionary.Add("other", new PreserveReferencesHandlingTests.CircularDictionary { { "blah", null } }); + circularDictionary.Add("self", circularDictionary); + + InMemoryTraceWriter traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Verbose + }; + + JsonConvert.SerializeObject( + circularDictionary, + Formatting.Indented, + new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All, + TraceWriter = traceWriter + }); + + Assert.IsTrue(traceWriter.TraceRecords.Any(r => r.Message == "Writing object reference Id '1' for Newtonsoft.Json.Tests.Serialization.PreserveReferencesHandlingTests+CircularDictionary. Path ''.")); + Assert.IsTrue(traceWriter.TraceRecords.Any(r => r.Message == "Writing object reference Id '2' for Newtonsoft.Json.Tests.Serialization.PreserveReferencesHandlingTests+CircularDictionary. Path 'other'.")); + Assert.IsTrue(traceWriter.TraceRecords.Any(r => r.Message == "Writing object reference to Id '1' for Newtonsoft.Json.Tests.Serialization.PreserveReferencesHandlingTests+CircularDictionary. Path 'self'.")); + } + + [Test] + public void DeserializeDictionarysWithPreserveObjectReferences() + { + string json = @"{ + ""$id"": ""1"", + ""other"": { + ""$id"": ""2"", + ""blah"": null + }, + ""self"": { + ""$ref"": ""1"" + } +}"; + + InMemoryTraceWriter traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Verbose + }; + + JsonConvert.DeserializeObject(json, + new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All, + TraceWriter = traceWriter + }); + + Assert.IsTrue(traceWriter.TraceRecords.Any(r => r.Message == "Read object reference Id '1' for Newtonsoft.Json.Tests.Serialization.PreserveReferencesHandlingTests+CircularDictionary. Path 'other', line 3, position 11.")); + Assert.IsTrue(traceWriter.TraceRecords.Any(r => r.Message == "Read object reference Id '2' for Newtonsoft.Json.Tests.Serialization.PreserveReferencesHandlingTests+CircularDictionary. Path 'other.blah', line 5, position 12.")); + Assert.IsTrue(traceWriter.TraceRecords.Any(r => r.Message == "Resolved object reference '1' to Newtonsoft.Json.Tests.Serialization.PreserveReferencesHandlingTests+CircularDictionary. Path 'self', line 9, position 4.")); + } + + [Test] + public void WriteTypeNameForObjects() + { + InMemoryTraceWriter traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Verbose + }; + + IList l = new List + { + new Dictionary { {"key!", "value!"}}, + new Version(1, 2, 3, 4) + }; + + JsonConvert.SerializeObject(l, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All, + TraceWriter = traceWriter + }); + + Assert.AreEqual("Started serializing System.Collections.Generic.List`1[System.Object]. Path ''.", traceWriter.TraceRecords[0].Message); + Assert.AreEqual("Writing type name 'System.Collections.Generic.List`1[[System.Object, mscorlib]], mscorlib' for System.Collections.Generic.List`1[System.Object]. Path ''.", traceWriter.TraceRecords[1].Message); + Assert.AreEqual("Started serializing System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values'.", traceWriter.TraceRecords[2].Message); + Assert.AreEqual("Writing type name 'System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib' for System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values[0]'.", traceWriter.TraceRecords[3].Message); + Assert.AreEqual("Finished serializing System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values[0]'.", traceWriter.TraceRecords[4].Message); + Assert.AreEqual("Started serializing System.Version. Path '$values[0]'.", traceWriter.TraceRecords[5].Message); + Assert.AreEqual("Writing type name 'System.Version, mscorlib' for System.Version. Path '$values[1]'.", traceWriter.TraceRecords[6].Message); + Assert.AreEqual("Finished serializing System.Version. Path '$values[1]'.", traceWriter.TraceRecords[7].Message); + Assert.AreEqual("Finished serializing System.Collections.Generic.List`1[System.Object]. Path ''.", traceWriter.TraceRecords[8].Message); + } + + [Test] + public void SerializeConverter() + { + InMemoryTraceWriter traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Verbose + }; + + IList d = new List + { + new DateTime(2000, 12, 12, 12, 12, 12, DateTimeKind.Utc) + }; + + string json = JsonConvert.SerializeObject(d, Formatting.Indented, new JsonSerializerSettings + { + Converters = { new JavaScriptDateTimeConverter() }, + TraceWriter = traceWriter + }); + + Assert.AreEqual("Started serializing System.Collections.Generic.List`1[System.DateTime]. Path ''.", traceWriter.TraceRecords[0].Message); + Assert.AreEqual("Started serializing System.DateTime with converter Newtonsoft.Json.Converters.JavaScriptDateTimeConverter. Path ''.", traceWriter.TraceRecords[1].Message); + Assert.AreEqual("Finished serializing System.DateTime with converter Newtonsoft.Json.Converters.JavaScriptDateTimeConverter. Path '[0]'.", traceWriter.TraceRecords[2].Message); + Assert.AreEqual("Finished serializing System.Collections.Generic.List`1[System.DateTime]. Path ''.", traceWriter.TraceRecords[3].Message); + } + + [Test] + public void DeserializeConverter() + { + string json = @"[new Date(976623132000)]"; + + InMemoryTraceWriter traceWriter = + new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Verbose + }; + + JsonConvert.DeserializeObject>( + json, + new JsonSerializerSettings + { + Converters = {new JavaScriptDateTimeConverter()}, + TraceWriter = traceWriter + }); + + Assert.AreEqual("Started deserializing System.Collections.Generic.List`1[System.DateTime]. Path '', line 1, position 1.", traceWriter.TraceRecords[0].Message); + Assert.AreEqual("Started deserializing System.DateTime with converter Newtonsoft.Json.Converters.JavaScriptDateTimeConverter. Path '[0]', line 1, position 10.", traceWriter.TraceRecords[1].Message); + Assert.AreEqual("Finished deserializing System.DateTime with converter Newtonsoft.Json.Converters.JavaScriptDateTimeConverter. Path '[0]', line 1, position 23.", traceWriter.TraceRecords[2].Message); + Assert.AreEqual("Finished deserializing System.Collections.Generic.List`1[System.DateTime]. Path '', line 1, position 24.", traceWriter.TraceRecords[3].Message); + } + + [Test] + public void DeserializeTypeName() + { + InMemoryTraceWriter traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Verbose + }; + + string json = @"{ + ""$type"": ""System.Collections.Generic.List`1[[System.Object, mscorlib]], mscorlib"", + ""$values"": [ + { + ""$type"": ""System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib"", + ""key!"": ""value!"" + }, + { + ""$type"": ""System.Version, mscorlib"", + ""Major"": 1, + ""Minor"": 2, + ""Build"": 3, + ""Revision"": 4, + ""MajorRevision"": 0, + ""MinorRevision"": 4 + } + ] +}"; + + JsonConvert.DeserializeObject(json, null, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All, + TraceWriter = traceWriter + }); + + Assert.AreEqual("Resolved type 'System.Collections.Generic.List`1[[System.Object, mscorlib]], mscorlib' to System.Collections.Generic.List`1[System.Object]. Path '$type', line 2, position 84.", traceWriter.TraceRecords[0].Message); + Assert.AreEqual("Started deserializing System.Collections.Generic.List`1[System.Object]. Path '$values', line 3, position 15.", traceWriter.TraceRecords[1].Message); + Assert.AreEqual("Resolved type 'System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.String, mscorlib]], mscorlib' to System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values[0].$type', line 5, position 120.", traceWriter.TraceRecords[2].Message); + Assert.AreEqual("Started deserializing System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values[0].key!', line 6, position 14.", traceWriter.TraceRecords[3].Message); + Assert.AreEqual("Finished deserializing System.Collections.Generic.Dictionary`2[System.String,System.String]. Path '$values[0]', line 7, position 6.", traceWriter.TraceRecords[4].Message); + Assert.AreEqual("Resolved type 'System.Version, mscorlib' to System.Version. Path '$values[1].$type', line 9, position 42.", traceWriter.TraceRecords[5].Message); + Assert.AreEqual("Deserializing System.Version using a non-default constructor 'Void .ctor(Int32, Int32, Int32, Int32)'. Path '$values[1].Major', line 10, position 15.", traceWriter.TraceRecords[6].Message); + Assert.AreEqual("Started deserializing System.Version. Path '$values[1]', line 16, position 6.", traceWriter.TraceRecords[7].Message); + Assert.AreEqual("Finished deserializing System.Version. Path '$values[1]', line 16, position 6.", traceWriter.TraceRecords[8].Message); + Assert.AreEqual("Finished deserializing System.Collections.Generic.List`1[System.Object]. Path '$values', line 17, position 4.", traceWriter.TraceRecords[9].Message); + } + +#if !(NETFX_CORE || PORTABLE || SILVERLIGHT) + [Test] + public void DeserializeISerializable() + { + InMemoryTraceWriter traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Verbose + }; + + ExceptionAssert.Throws( + "Member 'ClassName' was not found.", + () => + { + JsonConvert.DeserializeObject( + "{}", + new JsonSerializerSettings + { + TraceWriter = traceWriter + }); + }); + + Assert.AreEqual("Deserializing System.Exception using ISerializable constructor. Path '', line 1, position 2.", traceWriter.TraceRecords[0].Message); + Assert.AreEqual(TraceLevel.Info, traceWriter.TraceRecords[0].Level); + Assert.AreEqual("Error deserializing System.Exception. Member 'ClassName' was not found. Path '', line 1, position 2.", traceWriter.TraceRecords[1].Message); + Assert.AreEqual(TraceLevel.Error, traceWriter.TraceRecords[1].Level); + } +#endif + + [Test] + public void DeserializeMissingMember() + { + InMemoryTraceWriter traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Verbose + }; + + JsonConvert.DeserializeObject( + "{'MissingMemberProperty':'!!'}", + new JsonSerializerSettings + { + TraceWriter = traceWriter + }); + + Assert.AreEqual("Started deserializing Newtonsoft.Json.Tests.TestObjects.Person. Path 'MissingMemberProperty', line 1, position 25.", traceWriter.TraceRecords[0].Message); + Assert.AreEqual("Could not find member 'MissingMemberProperty' on Newtonsoft.Json.Tests.TestObjects.Person. Path 'MissingMemberProperty', line 1, position 25.", traceWriter.TraceRecords[1].Message); + Assert.AreEqual("Finished deserializing Newtonsoft.Json.Tests.TestObjects.Person. Path '', line 1, position 30.", traceWriter.TraceRecords[2].Message); + } + + [Test] + public void DeserializeMissingMemberConstructor() + { + InMemoryTraceWriter traceWriter = new InMemoryTraceWriter + { + LevelFilter = TraceLevel.Verbose + }; + + string json = @"{ + ""Major"": 1, + ""Minor"": 2, + ""Build"": 3, + ""Revision"": 4, + ""MajorRevision"": 0, + ""MinorRevision"": 4, + ""MissingMemberProperty"": null +}"; + + JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + TraceWriter = traceWriter + }); + + Assert.AreEqual("Deserializing System.Version using a non-default constructor 'Void .ctor(Int32, Int32, Int32, Int32)'. Path 'Major', line 2, position 11.", traceWriter.TraceRecords[0].Message); + Assert.AreEqual("Could not find member 'MissingMemberProperty' on System.Version. Path 'MissingMemberProperty', line 8, position 32.", traceWriter.TraceRecords[1].Message); + Assert.AreEqual("Started deserializing System.Version. Path '', line 9, position 2.", traceWriter.TraceRecords[2].Message); + Assert.AreEqual("Finished deserializing System.Version. Path '', line 9, position 2.", traceWriter.TraceRecords[3].Message); + } + } + + public class TraceRecord + { + public string Message { get; set; } + public TraceLevel Level { get; set; } + public Exception Exception { get; set; } + + public override string ToString() + { + return Level + " - " + Message; + } + } + + public class InMemoryTraceWriter : ITraceWriter + { + public TraceLevel LevelFilter { get; set; } + public IList TraceRecords { get; set; } + + public InMemoryTraceWriter() + { + LevelFilter = TraceLevel.Verbose; + TraceRecords = new List(); + } + + public void Trace(TraceLevel level, string message, Exception ex) + { + TraceRecords.Add( + new TraceRecord + { + Level = level, + Message = message, + Exception = ex + }); + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + foreach (var traceRecord in TraceRecords) + { + sb.AppendLine(traceRecord.Message); + } + + return sb.ToString(); + } + } + + public class TraceTestObject + { + public IList IntList { get; set; } + public string[] StringArray { get; set; } + public Version Version { get; set; } + public IDictionary StringDictionary { get; set; } + } + + public class IntegerTestClass + { + public int Integer { get; set; } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonException.cs b/Src/Newtonsoft.Json/JsonException.cs index 2f0691ec..05434c2b 100644 --- a/Src/Newtonsoft.Json/JsonException.cs +++ b/Src/Newtonsoft.Json/JsonException.cs @@ -81,28 +81,5 @@ namespace Newtonsoft.Json { } #endif - - internal static string FormatExceptionMessage(IJsonLineInfo lineInfo, string path, string message) - { - // don't add a fullstop and space when message ends with a new line - if (!message.EndsWith(Environment.NewLine)) - { - message = message.Trim(); - - if (!message.EndsWith(".")) - message += "."; - - message += " "; - } - - message += "Path '{0}'".FormatWith(CultureInfo.InvariantCulture, path); - - if (lineInfo != null && lineInfo.HasLineInfo()) - message += ", line {0}, position {1}".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition); - - message += "."; - - return message; - } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonPosition.cs b/Src/Newtonsoft.Json/JsonPosition.cs index 57ac43aa..784f825e 100644 --- a/Src/Newtonsoft.Json/JsonPosition.cs +++ b/Src/Newtonsoft.Json/JsonPosition.cs @@ -23,8 +23,11 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion +using System; using System.Collections.Generic; +using System.Globalization; using System.Text; +using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json { @@ -85,5 +88,28 @@ namespace Newtonsoft.Json return sb.ToString(); } + + internal static string FormatMessage(IJsonLineInfo lineInfo, string path, string message) + { + // don't add a fullstop and space when message ends with a new line + if (!message.EndsWith(Environment.NewLine)) + { + message = message.Trim(); + + if (!message.EndsWith(".")) + message += "."; + + message += " "; + } + + message += "Path '{0}'".FormatWith(CultureInfo.InvariantCulture, path); + + if (lineInfo != null && lineInfo.HasLineInfo()) + message += ", line {0}, position {1}".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition); + + message += "."; + + return message; + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index 1e4ab185..e10763cf 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -786,14 +786,8 @@ namespace Newtonsoft.Json private void UpdateScopeWithFinishedValue() { - if (_currentPosition.Type == JsonContainerType.Array - || _currentPosition.Type == JsonContainerType.Constructor) - { - if (_currentPosition.Position == null) - _currentPosition.Position = 0; - else - _currentPosition.Position++; - } + if (_currentPosition.HasIndex) + _currentPosition.Position++; } private void ValidateEnd(JsonToken endToken) diff --git a/Src/Newtonsoft.Json/JsonReaderException.cs b/Src/Newtonsoft.Json/JsonReaderException.cs index ca6d3c70..9aefd25d 100644 --- a/Src/Newtonsoft.Json/JsonReaderException.cs +++ b/Src/Newtonsoft.Json/JsonReaderException.cs @@ -118,7 +118,7 @@ namespace Newtonsoft.Json internal static JsonReaderException Create(IJsonLineInfo lineInfo, string path, string message, Exception ex) { - message = FormatExceptionMessage(lineInfo, path, message); + message = JsonPosition.FormatMessage(lineInfo, path, message); int lineNumber; int linePosition; diff --git a/Src/Newtonsoft.Json/JsonSerializationException.cs b/Src/Newtonsoft.Json/JsonSerializationException.cs index 0ec50c76..3d132ddc 100644 --- a/Src/Newtonsoft.Json/JsonSerializationException.cs +++ b/Src/Newtonsoft.Json/JsonSerializationException.cs @@ -92,7 +92,7 @@ namespace Newtonsoft.Json internal static JsonSerializationException Create(IJsonLineInfo lineInfo, string path, string message, Exception ex) { - message = FormatExceptionMessage(lineInfo, path, message); + message = JsonPosition.FormatMessage(lineInfo, path, message); return new JsonSerializationException(message, ex); } diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 9486b0c0..c8813a73 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -55,6 +55,7 @@ namespace Newtonsoft.Json private JsonConverterCollection _converters; private IContractResolver _contractResolver; private IReferenceResolver _referenceResolver; + private ITraceWriter _traceWriter; private SerializationBinder _binder; private StreamingContext _context; private Formatting? _formatting; @@ -110,6 +111,22 @@ namespace Newtonsoft.Json } } + /// + /// Gets or sets the used by the serializer when writing trace messages. + /// + /// The trace writer. + public virtual ITraceWriter TraceWriter + { + get + { + return _traceWriter; + } + set + { + _traceWriter = value; + } + } + /// /// Gets or sets how type name writing and reading is handled by the serializer. /// @@ -430,6 +447,8 @@ namespace Newtonsoft.Json jsonSerializer.ContractResolver = settings.ContractResolver; if (settings.ReferenceResolver != null) jsonSerializer.ReferenceResolver = settings.ReferenceResolver; + if (settings.TraceWriter != null) + jsonSerializer.TraceWriter = settings.TraceWriter; if (settings.Binder != null) jsonSerializer.Binder = settings.Binder; } diff --git a/Src/Newtonsoft.Json/JsonSerializerSettings.cs b/Src/Newtonsoft.Json/JsonSerializerSettings.cs index 43deb5cf..c02872a9 100644 --- a/Src/Newtonsoft.Json/JsonSerializerSettings.cs +++ b/Src/Newtonsoft.Json/JsonSerializerSettings.cs @@ -137,6 +137,12 @@ namespace Newtonsoft.Json /// The reference resolver. public IReferenceResolver ReferenceResolver { get; set; } + /// + /// Gets or sets the used by the serializer when writing trace messages. + /// + /// The trace writer. + public ITraceWriter TraceWriter { get; set; } + /// /// Gets or sets the used by the serializer when resolving type names. /// diff --git a/Src/Newtonsoft.Json/JsonWriterException.cs b/Src/Newtonsoft.Json/JsonWriterException.cs index 4f614b87..fdccc546 100644 --- a/Src/Newtonsoft.Json/JsonWriterException.cs +++ b/Src/Newtonsoft.Json/JsonWriterException.cs @@ -99,7 +99,7 @@ namespace Newtonsoft.Json internal static JsonWriterException Create(string path, string message, Exception ex) { - message = FormatExceptionMessage(null, path, message); + message = JsonPosition.FormatMessage(null, path, message); return new JsonWriterException(message, ex, path); } diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 5742e3d9..95c41cfc 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -219,9 +219,11 @@ namespace Newtonsoft.Json.Linq DateTime date1 = (DateTime)objA; DateTime date2; +#if !NET20 if (objB is DateTimeOffset) date2 = ((DateTimeOffset)objB).DateTime; else +#endif date2 = Convert.ToDateTime(objB, CultureInfo.InvariantCulture); return date1.CompareTo(date2); diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index e00b47a1..0626850a 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -103,6 +103,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj index 149bdd92..3fdc02bf 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -116,8 +116,10 @@ + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj index c5b7e552..4d548881 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj @@ -148,6 +148,7 @@ + @@ -172,6 +173,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index af030abe..6846a387 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -171,6 +171,7 @@ + @@ -194,6 +195,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.WinRT.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.WinRT.csproj index 61373b84..6851d875 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.WinRT.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.WinRT.csproj @@ -217,6 +217,7 @@ + @@ -241,6 +242,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 442194f5..65f266fb 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -67,6 +67,7 @@ 4 bin\Release\Net40\Newtonsoft.Json.xml AllRules.ruleset + true @@ -112,6 +113,7 @@ + diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 847575fc..cad32025 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -90,7 +90,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.10.15504")] +[assembly: AssemblyFileVersion("4.5.10.15509")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/ErrorContext.cs b/Src/Newtonsoft.Json/Serialization/ErrorContext.cs index ae1b33e9..1c8e54c5 100644 --- a/Src/Newtonsoft.Json/Serialization/ErrorContext.cs +++ b/Src/Newtonsoft.Json/Serialization/ErrorContext.cs @@ -40,6 +40,8 @@ namespace Newtonsoft.Json.Serialization Path = path; } + internal bool Traced { get; set; } + /// /// Gets or sets the error. /// diff --git a/Src/Newtonsoft.Json/Serialization/ITraceWriter.cs b/Src/Newtonsoft.Json/Serialization/ITraceWriter.cs new file mode 100644 index 00000000..3f4f7bf5 --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/ITraceWriter.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +namespace Newtonsoft.Json.Serialization +{ + /// + /// Represents a trace writer. + /// + public interface ITraceWriter + { + /// + /// Gets the that will be used to filter the trace messages passed to the writer. + /// For example a filter level of Info will exclude Verbose messages and include Info, + /// Warning and Error messages. + /// + /// The that will be used to filter the trace messages passed to the writer. + TraceLevel LevelFilter { get; } + + /// + /// Writes the specified trace level, message and optional exception. + /// + /// The at which to write this trace. + /// The trace message. + /// The trace exception. This parameter is optional. + void Trace(TraceLevel level, string message, Exception ex); + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs index dda63122..9f7f51c3 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs @@ -25,6 +25,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Net; using System.Runtime.CompilerServices; using Newtonsoft.Json.Utilities; @@ -54,14 +55,20 @@ namespace Newtonsoft.Json.Serialization private ErrorContext _currentErrorContext; private BidirectionalDictionary _mappings; + private bool _serializing; internal readonly JsonSerializer Serializer; + internal readonly ITraceWriter TraceWriter; protected JsonSerializerInternalBase(JsonSerializer serializer) { ValidationUtils.ArgumentNotNull(serializer, "serializer"); Serializer = serializer; + TraceWriter = serializer.TraceWriter; + + // kind of a hack but meh. might clean this up later + _serializing = (GetType() == typeof (JsonSerializerInternalWriter)); } internal BidirectionalDictionary DefaultReferenceMappings @@ -81,7 +88,7 @@ namespace Newtonsoft.Json.Serialization } } - protected ErrorContext GetErrorContext(object currentObject, object member, string path, Exception error) + private ErrorContext GetErrorContext(object currentObject, object member, string path, Exception error) { if (_currentErrorContext == null) _currentErrorContext = new ErrorContext(currentObject, member, path, error); @@ -100,9 +107,27 @@ namespace Newtonsoft.Json.Serialization _currentErrorContext = null; } - protected bool IsErrorHandled(object currentObject, JsonContract contract, object keyValue, string path, Exception ex) + protected bool IsErrorHandled(object currentObject, JsonContract contract, object keyValue, IJsonLineInfo lineInfo, string path, Exception ex) { ErrorContext errorContext = GetErrorContext(currentObject, keyValue, path, ex); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Error && !errorContext.Traced) + { + // only write error once + errorContext.Traced = true; + + string message = (_serializing) ? "Error serializing" : "Error deserializing"; + if (contract != null) + message += " " + contract.UnderlyingType; + message += ". " + ex.Message; + + // add line information to non-json.net exception message + if (!(ex is JsonException)) + message = JsonPosition.FormatMessage(lineInfo, path, message); + + TraceWriter.Trace(TraceLevel.Error, message, ex); + } + if (contract != null) contract.InvokeOnError(currentObject, Serializer.Context, errorContext); diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index b559f9ae..7e662115 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -31,6 +31,7 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.Dynamic; #endif +using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.Serialization; @@ -46,6 +47,13 @@ namespace Newtonsoft.Json.Serialization { internal class JsonSerializerInternalReader : JsonSerializerInternalBase { + internal enum PropertyPresence + { + None, + Null, + Value + } + private JsonSerializerProxy _internalSerializer; #if !(SILVERLIGHT || NETFX_CORE || PORTABLE) private JsonFormatterConverter _formatterConverter; @@ -129,7 +137,7 @@ namespace Newtonsoft.Json.Serialization object deserializedValue; if (converter != null && converter.CanRead) - deserializedValue = converter.ReadJson(reader, objectType, null, GetInternalSerializer()); + deserializedValue = DeserializeConvertable(converter, reader, objectType, null); else deserializedValue = CreateValueInternal(reader, objectType, contract, null, null, null, null); @@ -143,7 +151,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - if (IsErrorHandled(null, contract, null, reader.Path, ex)) + if (IsErrorHandled(null, contract, null, reader as IJsonLineInfo, reader.Path, ex)) { HandleError(reader, false, 0); return null; @@ -430,10 +438,12 @@ To fix this error either change the JSON to a {1} or change the deserialized typ if (reader.TokenType == JsonToken.PropertyName) throw JsonSerializationException.Create(reader, "Additional content found in JSON reference object. A JSON reference object should only have a {0} property.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName)); - { - newValue = Serializer.ReferenceResolver.ResolveReference(this, reference); - return true; - } + newValue = Serializer.ReferenceResolver.ResolveReference(this, reference); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Resolved object reference '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, reference, newValue.GetType())), null); + + return true; } else { @@ -470,6 +480,9 @@ To fix this error either change the JSON to a {1} or change the deserialized typ if (specifiedType == null) throw JsonSerializationException.Create(reader, "Type specified in JSON '{0}' was not resolved.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName)); + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Resolved type '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName, specifiedType)), null); + if (objectType != null #if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE) && objectType != typeof (IDynamicMetaObjectProvider) @@ -672,7 +685,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ if (!gottenCurrentValue && target != null && property.Readable) currentValue = property.ValueProvider.GetValue(target); - value = propertyConverter.ReadJson(reader, property.PropertyType, currentValue, GetInternalSerializer()); + value = DeserializeConvertable(propertyConverter, reader, property.PropertyType, currentValue); } else { @@ -765,6 +778,9 @@ To fix this error either change the JSON to a {1} or change the deserialized typ { try { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Read object reference Id '{0}' for {1}.".FormatWith(CultureInfo.InvariantCulture, id, value.GetType())), null); + Serializer.ReferenceResolver.AddReference(this, id, value); } catch (Exception ex) @@ -806,6 +822,22 @@ To fix this error either change the JSON to a {1} or change the deserialized typ return dictionary; } + private void OnDeserializing(JsonReader reader, JsonContract contract, object value) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Started deserializing {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null); + + contract.InvokeOnDeserializing(value, Serializer.Context); + } + + private void OnDeserialized(JsonReader reader, JsonContract contract, object value) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Finished deserializing {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null); + + contract.InvokeOnDeserialized(value, Serializer.Context); + } + private object PopulateDictionary(IWrappedDictionary wrappedDictionary, JsonReader reader, JsonDictionaryContract contract, JsonProperty containerProperty, string id) { object dictionary = wrappedDictionary.UnderlyingDictionary; @@ -813,7 +845,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ if (id != null) AddReference(reader, id, dictionary); - contract.InvokeOnDeserializing(dictionary, Serializer.Context); + OnDeserializing(reader, contract, dictionary); int initialDepth = reader.Depth; @@ -848,7 +880,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ object itemValue; if (dictionaryValueConverter != null && dictionaryValueConverter.CanRead) - itemValue = dictionaryValueConverter.ReadJson(reader, contract.DictionaryValueType, null, GetInternalSerializer()); + itemValue = DeserializeConvertable(dictionaryValueConverter, reader, contract.DictionaryValueType, null); else itemValue = CreateValueInternal(reader, contract.DictionaryValueType, contract.ItemContract, null, contract, containerProperty, null); @@ -856,7 +888,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ } catch (Exception ex) { - if (IsErrorHandled(dictionary, contract, keyValue, reader.Path, ex)) + if (IsErrorHandled(dictionary, contract, keyValue, reader as IJsonLineInfo, reader.Path, ex)) HandleError(reader, true, initialDepth); else throw; @@ -875,7 +907,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ if (!finished) ThrowUnexpectedEndException(reader, contract, dictionary, "Unexpected end when deserializing object."); - contract.InvokeOnDeserialized(dictionary, Serializer.Context); + OnDeserialized(reader, contract, dictionary); return dictionary; } @@ -886,7 +918,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ if (id != null) AddReference(reader, id, list); - contract.InvokeOnDeserializing(list, Serializer.Context); + OnDeserializing(reader, contract, list); JsonContract collectionItemContract = GetContractSafe(contract.CollectionItemType); JsonConverter collectionItemConverter = GetConverter(collectionItemContract, null, contract, containerProperty); @@ -920,7 +952,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ object value; if (collectionItemConverter != null && collectionItemConverter.CanRead) - value = collectionItemConverter.ReadJson(reader, contract.CollectionItemType, null, GetInternalSerializer()); + value = DeserializeConvertable(collectionItemConverter, reader, contract.CollectionItemType, null); else value = CreateValueInternal(reader, contract.CollectionItemType, collectionItemContract, null, contract, containerProperty, null); @@ -937,7 +969,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ { JsonPosition errorPosition = reader.GetPosition(initialDepth); - if (IsErrorHandled(list, contract, errorPosition.Position, reader.Path, ex)) + if (IsErrorHandled(list, contract, errorPosition.Position, reader as IJsonLineInfo, reader.Path, ex)) { HandleError(reader, true, initialDepth); @@ -998,7 +1030,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ if (!finished) ThrowUnexpectedEndException(reader, contract, list, "Unexpected end when deserializing array."); - contract.InvokeOnDeserialized(list, Serializer.Context); + OnDeserialized(reader, contract, list); return list; } @@ -1010,7 +1042,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ } catch (Exception ex) { - if (IsErrorHandled(currentObject, contract, null, reader.Path, ex)) + if (IsErrorHandled(currentObject, contract, null, reader as IJsonLineInfo, reader.Path, ex)) HandleError(reader, false, 0); else throw; @@ -1031,7 +1063,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ return list; } - contract.InvokeOnDeserializing(list, Serializer.Context); + OnDeserializing(reader, contract, list); int initialDepth = reader.Depth; @@ -1058,7 +1090,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ object value; if (collectionItemConverter != null && collectionItemConverter.CanRead) - value = collectionItemConverter.ReadJson(reader, contract.CollectionItemType, null, GetInternalSerializer()); + value = DeserializeConvertable(collectionItemConverter, reader, contract.CollectionItemType, null); else value = CreateValueInternal(reader, contract.CollectionItemType, collectionItemContract, null, contract, containerProperty, null); @@ -1075,7 +1107,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ { JsonPosition errorPosition = reader.GetPosition(initialDepth); - if (IsErrorHandled(list, contract, errorPosition.Position, reader.Path, ex)) + if (IsErrorHandled(list, contract, errorPosition.Position, reader as IJsonLineInfo, reader.Path, ex)) { HandleError(reader, true, initialDepth); @@ -1100,7 +1132,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ if (!finished) ThrowUnexpectedEndException(reader, contract, list, "Unexpected end when deserializing array."); - contract.InvokeOnDeserialized(list, Serializer.Context); + OnDeserialized(reader, contract, list); return list; } @@ -1116,6 +1148,9 @@ To fix this error either change the environment to be fully trusted, change the ".FormatWith(CultureInfo.InvariantCulture, objectType)); } + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Deserializing {0} using ISerializable constructor.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null); + SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, GetFormatterConverter()); bool finished = false; @@ -1152,8 +1187,8 @@ To fix this error either change the environment to be fully trusted, change the AddReference(reader, id, createdObject); // these are together because OnDeserializing takes an object but for an ISerializable the object is fully created in the constructor - contract.InvokeOnDeserializing(createdObject, Serializer.Context); - contract.InvokeOnDeserialized(createdObject, Serializer.Context); + OnDeserializing(reader, contract, createdObject); + OnDeserialized(reader, contract, createdObject); return createdObject; } @@ -1176,7 +1211,7 @@ To fix this error either change the environment to be fully trusted, change the if (id != null) AddReference(reader, id, newObject); - contract.InvokeOnDeserializing(newObject, Serializer.Context); + OnDeserializing(reader, contract, newObject); int initialDepth = reader.Depth; @@ -1214,7 +1249,7 @@ To fix this error either change the environment to be fully trusted, change the object value; if (dynamicMemberConverter != null && dynamicMemberConverter.CanRead) - value = dynamicMemberConverter.ReadJson(reader, t, null, GetInternalSerializer()); + value = DeserializeConvertable(dynamicMemberConverter, reader, t, null); else value = CreateValueInternal(reader, t, dynamicMemberContract, null, null, member, null); @@ -1223,7 +1258,7 @@ To fix this error either change the environment to be fully trusted, change the } catch (Exception ex) { - if (IsErrorHandled(newObject, contract, memberName, reader.Path, ex)) + if (IsErrorHandled(newObject, contract, memberName, reader as IJsonLineInfo, reader.Path, ex)) HandleError(reader, true, initialDepth); else throw; @@ -1240,7 +1275,7 @@ To fix this error either change the environment to be fully trusted, change the if (!finished) ThrowUnexpectedEndException(reader, contract, newObject, "Unexpected end when deserializing object."); - contract.InvokeOnDeserialized(newObject, Serializer.Context); + OnDeserialized(reader, contract, newObject); return newObject; } @@ -1252,6 +1287,9 @@ To fix this error either change the environment to be fully trusted, change the Type objectType = contract.UnderlyingType; + 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 constructorParameters = constructorInfo.GetParameters().ToDictionary(p => p, p => (object) null); @@ -1271,7 +1309,7 @@ To fix this error either change the environment to be fully trusted, change the if (id != null) AddReference(reader, id, createdObject); - contract.InvokeOnDeserializing(createdObject, Serializer.Context); + OnDeserializing(reader, contract, createdObject); // go through unused values and set the newly created object's properties foreach (KeyValuePair remainingPropertyValue in remainingPropertyValues) @@ -1323,10 +1361,23 @@ To fix this error either change the environment to be fully trusted, change the } } - contract.InvokeOnDeserialized(createdObject, Serializer.Context); + OnDeserialized(reader, contract, createdObject); return createdObject; } + private object DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, object existingValue) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Started deserializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, objectType, converter.GetType())), null); + + object value = converter.ReadJson(reader, objectType, existingValue, GetInternalSerializer()); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Finished deserializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, objectType, converter.GetType())), null); + + return value; + } + private IDictionary ResolvePropertyAndConstructorValues(JsonObjectContract contract, JsonProperty containerProperty, JsonReader reader, Type objectType) { IDictionary propertyValues = new Dictionary(); @@ -1360,7 +1411,7 @@ To fix this error either change the environment to be fully trusted, change the object propertyValue; if (propertyConverter != null && propertyConverter.CanRead) - propertyValue = propertyConverter.ReadJson(reader, property.PropertyType, null, GetInternalSerializer()); + propertyValue = DeserializeConvertable(propertyConverter, reader, property.PropertyType, null); else propertyValue = CreateValueInternal(reader, property.PropertyType, property.PropertyContract, property, contract, containerProperty, null); @@ -1376,6 +1427,9 @@ To fix this error either change the environment to be fully trusted, change the if (!reader.Read()) throw JsonSerializationException.Create(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Could not find member '{0}' on {1}.".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType)), null); + 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)); @@ -1482,7 +1536,7 @@ To fix this error either change the environment to be fully trusted, change the private object PopulateObject(object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, string id) { - contract.InvokeOnDeserializing(newObject, Serializer.Context); + OnDeserializing(reader, contract, newObject); // only need to keep a track of properies presence if they are required or a value should be defaulted if missing Dictionary propertiesPresence = (contract.HasRequiredOrDefaultValueProperties || HasFlag(Serializer.DefaultValueHandling, DefaultValueHandling.Populate)) @@ -1511,6 +1565,9 @@ To fix this error either change the environment to be fully trusted, change the if (property == null) { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Could not find member '{0}' on {1}".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType)), null); + if (Serializer.MissingMemberHandling == MissingMemberHandling.Error) throw JsonSerializationException.Create(reader, "Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType.Name)); @@ -1532,7 +1589,7 @@ To fix this error either change the environment to be fully trusted, change the } catch (Exception ex) { - if (IsErrorHandled(newObject, contract, memberName, reader.Path, ex)) + if (IsErrorHandled(newObject, contract, memberName, reader as IJsonLineInfo, reader.Path, ex)) HandleError(reader, true, initialDepth); else throw; @@ -1555,7 +1612,7 @@ To fix this error either change the environment to be fully trusted, change the EndObject(newObject, reader, contract, initialDepth, propertiesPresence); - contract.InvokeOnDeserialized(newObject, Serializer.Context); + OnDeserialized(reader, contract, newObject); return newObject; } @@ -1594,7 +1651,7 @@ To fix this error either change the environment to be fully trusted, change the } catch (Exception ex) { - if (IsErrorHandled(newObject, contract, property.PropertyName, reader.Path, ex)) + if (IsErrorHandled(newObject, contract, property.PropertyName, reader as IJsonLineInfo, reader.Path, ex)) HandleError(reader, true, initialDepth); else throw; @@ -1629,12 +1686,5 @@ To fix this error either change the environment to be fully trusted, change the } } } - - internal enum PropertyPresence - { - None, - Null, - Value - } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 25244b37..c07f52c9 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -30,6 +30,7 @@ using System.ComponentModel; #if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE) using System.Dynamic; #endif +using System.Diagnostics; using System.Globalization; using System.Security; using Newtonsoft.Json.Linq; @@ -229,21 +230,25 @@ namespace Newtonsoft.Json.Serialization if (_serializeStack.IndexOf(value) != -1) { + string message = "Self referencing loop detected"; + if (property != null) + message += " for property '{0}'".FormatWith(CultureInfo.InvariantCulture, property.PropertyName); + message += " with type '{0}'.".FormatWith(CultureInfo.InvariantCulture, value.GetType()); + switch (referenceLoopHandling.GetValueOrDefault(Serializer.ReferenceLoopHandling)) { case ReferenceLoopHandling.Error: - string message = "Self referencing loop detected"; - if (property != null) - message += " for property '{0}'".FormatWith(CultureInfo.InvariantCulture, property.PropertyName); - message += " with type '{0}'.".FormatWith(CultureInfo.InvariantCulture, value.GetType()); - throw JsonSerializationException.Create(null, writer.ContainerPath, message, null); case ReferenceLoopHandling.Ignore: + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, message + ". Skipping serializing self referenced value."), null); + return false; case ReferenceLoopHandling.Serialize: + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, message + ". Serializing self referenced value."), null); + return true; - default: - throw new InvalidOperationException("Unexpected ReferenceLoopHandling value: '{0}'".FormatWith(CultureInfo.InvariantCulture, Serializer.ReferenceLoopHandling)); } } @@ -252,9 +257,14 @@ namespace Newtonsoft.Json.Serialization private void WriteReference(JsonWriter writer, object value) { + string reference = GetReference(writer, value); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Writing object reference to Id '{0}' for {1}.".FormatWith(CultureInfo.InvariantCulture, reference, value.GetType())), null); + writer.WriteStartObject(); writer.WritePropertyName(JsonTypeReflector.RefPropertyName); - writer.WriteValue(GetReference(writer, value)); + writer.WriteValue(reference); writer.WriteEndObject(); } @@ -263,6 +273,7 @@ namespace Newtonsoft.Json.Serialization try { string reference = Serializer.ReferenceResolver.GetReference(this, value); + return reference; } catch (Exception ex) @@ -290,6 +301,7 @@ namespace Newtonsoft.Json.Serialization #else s = converter.ConvertToString(value); #endif + return true; } } @@ -315,18 +327,34 @@ namespace Newtonsoft.Json.Serialization private void SerializeString(JsonWriter writer, object value, JsonStringContract contract) { - contract.InvokeOnSerializing(value, Serializer.Context); + OnSerializing(writer, contract, value); string s; TryConvertToString(value, contract.UnderlyingType, out s); writer.WriteValue(s); + OnSerialized(writer, contract, value); + } + + private void OnSerializing(JsonWriter writer, JsonContract contract, object value) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Started serializing {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null); + + contract.InvokeOnSerializing(value, Serializer.Context); + } + + private void OnSerialized(JsonWriter writer, JsonContract contract, object value) + { + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Finished serializing {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)), null); + contract.InvokeOnSerialized(value, Serializer.Context); } private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) { - contract.InvokeOnSerializing(value, Serializer.Context); + OnSerializing(writer, contract, value); _serializeStack.Add(value); @@ -349,7 +377,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - if (IsErrorHandled(value, contract, property.PropertyName, writer.ContainerPath, ex)) + if (IsErrorHandled(value, contract, property.PropertyName, null, writer.ContainerPath, ex)) HandleError(writer, initialDepth); else throw; @@ -360,7 +388,7 @@ namespace Newtonsoft.Json.Serialization _serializeStack.RemoveAt(_serializeStack.Count - 1); - contract.InvokeOnSerialized(value, Serializer.Context); + OnSerialized(writer, contract, value); } private bool CalculatePropertyValues(JsonWriter writer, object value, JsonContainerContract contract, JsonProperty member, JsonProperty property, out JsonContract memberContract, out object memberValue) @@ -409,8 +437,7 @@ namespace Newtonsoft.Json.Serialization bool isReference = ResolveIsReference(contract, member, collectionContract, containerProperty) ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); if (isReference) { - writer.WritePropertyName(JsonTypeReflector.IdPropertyName); - writer.WriteValue(GetReference(writer, value)); + WriteReferenceIdProperty(writer, contract.UnderlyingType, value); } if (ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionContract, containerProperty)) { @@ -418,10 +445,26 @@ namespace Newtonsoft.Json.Serialization } } + private void WriteReferenceIdProperty(JsonWriter writer, Type type, object value) + { + string reference = GetReference(writer, value); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, "Writing object reference Id '{0}' for {1}.".FormatWith(CultureInfo.InvariantCulture, reference, type)), null); + + writer.WritePropertyName(JsonTypeReflector.IdPropertyName); + writer.WriteValue(reference); + } + private void WriteTypeProperty(JsonWriter writer, Type type) { + string typeName = ReflectionUtils.GetTypeName(type, Serializer.TypeNameAssemblyFormat, Serializer.Binder); + + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Verbose) + TraceWriter.Trace(TraceLevel.Verbose, JsonPosition.FormatMessage(null, writer.Path, "Writing type name '{0}' for {1}.".FormatWith(CultureInfo.InvariantCulture, typeName, type)), null); + writer.WritePropertyName(JsonTypeReflector.TypePropertyName); - writer.WriteValue(ReflectionUtils.GetTypeName(type, Serializer.TypeNameAssemblyFormat, Serializer.Binder)); + writer.WriteValue(typeName); } private bool HasFlag(DefaultValueHandling value, DefaultValueHandling flag) @@ -452,15 +495,21 @@ namespace Newtonsoft.Json.Serialization _serializeStack.Add(value); + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Started serializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, value.GetType(), converter.GetType())), null); + converter.WriteJson(writer, value, GetInternalSerializer()); + if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info) + TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(null, writer.Path, "Finished serializing {0} with converter {1}.".FormatWith(CultureInfo.InvariantCulture, value.GetType(), converter.GetType())), null); + _serializeStack.RemoveAt(_serializeStack.Count - 1); } } private void SerializeList(JsonWriter writer, IWrappedCollection values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) { - contract.InvokeOnSerializing(values.UnderlyingCollection, Serializer.Context); + OnSerializing(writer, contract, values.UnderlyingCollection); _serializeStack.Add(values.UnderlyingCollection); @@ -492,7 +541,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - if (IsErrorHandled(values.UnderlyingCollection, contract, index, writer.ContainerPath, ex)) + if (IsErrorHandled(values.UnderlyingCollection, contract, index, null, writer.ContainerPath, ex)) HandleError(writer, initialDepth); else throw; @@ -510,12 +559,12 @@ namespace Newtonsoft.Json.Serialization _serializeStack.RemoveAt(_serializeStack.Count - 1); - contract.InvokeOnSerialized(values.UnderlyingCollection, Serializer.Context); + OnSerialized(writer, contract, values.UnderlyingCollection); } private void SerializeMultidimensionalArray(JsonWriter writer, Array values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) { - contract.InvokeOnSerializing(values, Serializer.Context); + OnSerializing(writer, contract, values); _serializeStack.Add(values); @@ -528,7 +577,7 @@ namespace Newtonsoft.Json.Serialization _serializeStack.RemoveAt(_serializeStack.Count - 1); - contract.InvokeOnSerialized(values, Serializer.Context); + OnSerialized(writer, contract, values); } private void SerializeMultidimensionalArray(JsonWriter writer, Array values, JsonArrayContract contract, JsonProperty member, int initialDepth, int[] indices) @@ -569,7 +618,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - if (IsErrorHandled(values, contract, i, writer.ContainerPath, ex)) + if (IsErrorHandled(values, contract, i, null, writer.ContainerPath, ex)) HandleError(writer, initialDepth + 1); else throw; @@ -596,8 +645,7 @@ namespace Newtonsoft.Json.Serialization if (isReference) { - writer.WritePropertyName(JsonTypeReflector.IdPropertyName); - writer.WriteValue(GetReference(writer, values)); + WriteReferenceIdProperty(writer, contract.UnderlyingType, values); } if (includeTypeDetails) { @@ -624,7 +672,7 @@ namespace Newtonsoft.Json.Serialization To fix this error either change the environment to be fully trusted, change the application to not deserialize the type, add JsonObjectAttribute to the type or change the JsonSerializer setting ContractResolver to use a new DefaultContractResolver with IgnoreSerializableInterface set to true.".FormatWith(CultureInfo.InvariantCulture, value.GetType()), null); } - contract.InvokeOnSerializing(value, Serializer.Context); + OnSerializing(writer, contract, value); _serializeStack.Add(value); WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty); @@ -641,14 +689,14 @@ To fix this error either change the environment to be fully trusted, change the writer.WriteEndObject(); _serializeStack.RemoveAt(_serializeStack.Count - 1); - contract.InvokeOnSerialized(value, Serializer.Context); + OnSerialized(writer, contract, value); } #endif #if !(NET35 || NET20 || WINDOWS_PHONE || PORTABLE) private void SerializeDynamic(JsonWriter writer, IDynamicMetaObjectProvider value, JsonDynamicContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) { - contract.InvokeOnSerializing(value, Serializer.Context); + OnSerializing(writer, contract, value); _serializeStack.Add(value); WriteObjectStart(writer, value, contract, member, collectionContract, containerProperty); @@ -673,7 +721,7 @@ To fix this error either change the environment to be fully trusted, change the } catch (Exception ex) { - if (IsErrorHandled(value, contract, property.PropertyName, writer.ContainerPath, ex)) + if (IsErrorHandled(value, contract, property.PropertyName, null, writer.ContainerPath, ex)) HandleError(writer, initialDepth); else throw; @@ -697,7 +745,7 @@ To fix this error either change the environment to be fully trusted, change the } catch (Exception ex) { - if (IsErrorHandled(value, contract, memberName, writer.ContainerPath, ex)) + if (IsErrorHandled(value, contract, memberName, null, writer.ContainerPath, ex)) HandleError(writer, initialDepth); else throw; @@ -708,7 +756,7 @@ To fix this error either change the environment to be fully trusted, change the writer.WriteEndObject(); _serializeStack.RemoveAt(_serializeStack.Count - 1); - contract.InvokeOnSerialized(value, Serializer.Context); + OnSerialized(writer, contract, value); } #endif @@ -743,8 +791,7 @@ To fix this error either change the environment to be fully trusted, change the private void SerializeDictionary(JsonWriter writer, IWrappedDictionary values, JsonDictionaryContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty) { - contract.InvokeOnSerializing(values.UnderlyingDictionary, Serializer.Context); - + OnSerializing(writer, contract, values.UnderlyingDictionary); _serializeStack.Add(values.UnderlyingDictionary); WriteObjectStart(writer, values.UnderlyingDictionary, contract, member, collectionContract, containerProperty); @@ -787,7 +834,7 @@ To fix this error either change the environment to be fully trusted, change the } catch (Exception ex) { - if (IsErrorHandled(values.UnderlyingDictionary, contract, propertyName, writer.ContainerPath, ex)) + if (IsErrorHandled(values.UnderlyingDictionary, contract, propertyName, null, writer.ContainerPath, ex)) HandleError(writer, initialDepth); else throw; @@ -798,7 +845,7 @@ To fix this error either change the environment to be fully trusted, change the _serializeStack.RemoveAt(_serializeStack.Count - 1); - contract.InvokeOnSerialized(values.UnderlyingDictionary, Serializer.Context); + OnSerialized(writer, contract, values.UnderlyingDictionary); } private string GetPropertyName(DictionaryEntry entry) diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs index 779f80a7..4e4ce413 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs @@ -49,6 +49,12 @@ namespace Newtonsoft.Json.Serialization set { _serializer.ReferenceResolver = value; } } + public override ITraceWriter TraceWriter + { + get { return _serializer.TraceWriter; } + set { _serializer.TraceWriter = value; } + } + public override JsonConverterCollection Converters { get { return _serializer.Converters; } diff --git a/Src/Newtonsoft.Json/TraceLevel.cs b/Src/Newtonsoft.Json/TraceLevel.cs new file mode 100644 index 00000000..cef37955 --- /dev/null +++ b/Src/Newtonsoft.Json/TraceLevel.cs @@ -0,0 +1,33 @@ +#if (NETFX_CORE || SILVERLIGHT || PORTABLE) +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json +{ + /// + /// Specifies what messages to output for the class. + /// + public enum TraceLevel + { + /// + /// Output no tracing and debugging messages. + /// + Off, + /// + /// Output error-handling messages. + /// + Error, + /// + /// Output warnings and error-handling messages. + /// + Warning, + /// + /// Output informational messages, warnings, and error-handling messages. + /// + Info, + /// + /// Output all debugging and tracing messages. + /// + Verbose + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index 25286a8f..b30219a9 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -332,7 +332,7 @@ namespace Newtonsoft.Json.Utilities if (targetType == typeof (Uri)) return new Uri((string) initialValue, UriKind.RelativeOrAbsolute); if (targetType == typeof (TimeSpan)) -#if !(NET35 || NET20 || SILVERLIGHT || PORTABLE) +#if !(NET35 || NET20 || PORTABLE) return TimeSpan.Parse((string) initialValue, CultureInfo.InvariantCulture); #else return TimeSpan.Parse((string)initialValue); @@ -395,7 +395,16 @@ namespace Newtonsoft.Json.Utilities /// public static bool TryConvert(object initialValue, CultureInfo culture, Type targetType, out object convertedValue) { - return MiscellaneousUtils.TryAction(delegate { return Convert(initialValue, culture, targetType); }, out convertedValue); + try + { + convertedValue = Convert(initialValue, culture, targetType); + return true; + } + catch + { + convertedValue = null; + return false; + } } #endregion diff --git a/Src/Newtonsoft.Json/Utilities/LinqBridge.cs b/Src/Newtonsoft.Json/Utilities/LinqBridge.cs index 92373520..85070fb4 100644 --- a/Src/Newtonsoft.Json/Utilities/LinqBridge.cs +++ b/Src/Newtonsoft.Json/Utilities/LinqBridge.cs @@ -31,6 +31,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using Newtonsoft.Json.Serialization; namespace Newtonsoft.Json.Utilities.LinqBridge @@ -2966,7 +2967,7 @@ namespace Newtonsoft.Json.Utilities.LinqBridge public override string ToString() { - return string.Format(@"{{ First = {0}, Second = {1} }}", First, Second); + return string.Format(CultureInfo.InvariantCulture, @"{{ First = {0}, Second = {1} }}", First, Second); } } } diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index 3d1928fe..74eaa4a0 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -1049,8 +1049,10 @@ namespace Newtonsoft.Json.Utilities if (type == typeof(Guid)) return new Guid(); +#if !NET20 if (type == typeof(DateTimeOffset)) return new DateTimeOffset(); +#endif if (IsNullable(type)) return null;