From b313445e01f77c6aa52697f522f53709d2389ce3 Mon Sep 17 00:00:00 2001 From: JamesNK Date: Sun, 11 Oct 2009 04:23:35 +0000 Subject: [PATCH] -Documentation -Added Error event to JsonSerializer --- Src/Doc/SerializationErrorHandling.html | 172 ++++++++++++++++++ Src/Doc/SerializingJSON.html | 7 +- Src/Doc/doc.shfb | 2 + Src/Doc/doc.sitemap | 1 + .../Newtonsoft.Json.Tests.Compact.csproj | 1 + .../Newtonsoft.Json.Tests.Net20.csproj | 1 + .../Newtonsoft.Json.Tests.Silverlight.csproj | 1 + .../Newtonsoft.Json.Tests.csproj | 1 + .../SerializationErrorHandlingTests.cs | 133 ++++++++++++++ .../TestObjects/PersonError.cs | 35 ++++ Src/Newtonsoft.Json.sln | 2 +- Src/Newtonsoft.Json/JsonSerializer.cs | 16 ++ Src/Newtonsoft.Json/JsonSerializerSettings.cs | 6 + .../ComponentModel/JPropertyDescriptor.cs | 17 ++ Src/Newtonsoft.Json/Linq/JContainer.cs | 4 +- Src/Newtonsoft.Json/Linq/JObject.cs | 4 +- .../Newtonsoft.Json.Compact.csproj | 1 + .../Newtonsoft.Json.Net20.csproj | 1 + .../Newtonsoft.Json.Silverlight.csproj | 1 + Src/Newtonsoft.Json/Newtonsoft.Json.csproj | 1 + .../Serialization/ErrorEventArgs.cs | 35 ++++ .../JsonSerializerInternalBase.cs | 24 ++- .../JsonSerializerInternalReader.cs | 59 +++--- .../JsonSerializerInternalWriter.cs | 69 +++---- .../Serialization/JsonSerializerProxy.cs | 10 +- 25 files changed, 516 insertions(+), 88 deletions(-) create mode 100644 Src/Doc/SerializationErrorHandling.html create mode 100644 Src/Newtonsoft.Json.Tests/TestObjects/PersonError.cs create mode 100644 Src/Newtonsoft.Json/Serialization/ErrorEventArgs.cs diff --git a/Src/Doc/SerializationErrorHandling.html b/Src/Doc/SerializationErrorHandling.html new file mode 100644 index 00000000..9ec8acbf --- /dev/null +++ b/Src/Doc/SerializationErrorHandling.html @@ -0,0 +1,172 @@ + + + + Serialization Error Handling + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Serialization Error Handling
+ +
+ + +

Json.NET supports error handling during serialization and deserialization. Error handling lets + you catch an error and choose whether to handle it and continue with serialization or let the error + bubble up and be thrown in your application.

+

Error handling is defined through two methods: + the Error event on JsonSerializer and the OnErrorAttribute.

+

Error Event

+ +

+ The Error event is an event handler found on JsonSerializer. The error event is raised whenever an + exception is thrown while serializing or deserialing JSON. Like all settings found on JsonSerializer + it can also be set on JsonSerializerSettings and passed to the serialization methods on JsonConvert.

+ +
+
+
List<string> errors = new List<string>();
+
 
+
List<DateTime> c = JsonConvert.DeserializeObject<List<DateTime>>(@"[
+
  ""2009-09-09T00:00:00Z"",
+
  ""I am not a date and will error!"",
+
  [
+
    1
+
  ],
+
  ""1977-02-20T00:00:00Z"",
+
  null,
+
  ""2000-12-01T00:00:00Z""
+
]",
+
  new JsonSerializerSettings
+
    {
+
      Error = delegate(object sender, ErrorEventArgs args)
+
        {
+
          errors.Add(args.ErrorContext.Error.Message);
+
          args.ErrorContext.Handled = true;
+
        },
+
      Converters = { new IsoDateTimeConverter() }
+
    });
+
 
+
// 2009-09-09T00:00:00Z
+
// 1977-02-20T00:00:00Z
+
// 2000-12-01T00:00:00Z
+
 
+
// The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.
+
// Unexpected token parsing date. Expected String, got StartArray.
+
// Cannot convert null value to System.DateTime.
+
+
+
+

+ In this example we are deserializing a JSON array to a collection of DateTimes. On the JsonSerializerSettings + a handler has been assigned to the Error event which will log the error message and mark the error as handled. +

+

+ The result of deserializing the JSON is three successfully deserialized dates and three error messages: + one for the badly formatted string, "I am not a date and will error!", one for the nested JSON array and one + for the null value since the list doesn't allow nullable DateTimes. The event handler has logged these messages + and Json.NET has continued on deserializing the JSON because the errors were marked as handled. +

+

+ One thing to note with error handling in Json.NET is that an unhandled error will bubble up and raise the event + on each of its parents, e.g. an unhandled error when serializing a collection of objects will be raised twice, + once against the object and then again on the collection. This will let you handle an error either where it + occurred or on one of its parents. +

+ + +
+
+
JsonSerializer serializer = new JsonSerializer();
+
serializer.Error += delegate(object sender, ErrorEventArgs args)
+
  {
+
    // only log an error once
+
    if (args.CurrentObject == args.ErrorContext.OriginalObject)
+
      errors.Add(args.ErrorContext.Error.Message);
+
  };
+
+
+ +

If you aren't immediately handling an error and only want to perform an action against it once then +you can check to see whether the ErrorEventArg's CurrentObject is equal to the OriginalObject. +OriginalObject is the object that threw the error and CurrentObject is the object that the event is being raised +against. They will only equal the first time the event is raised against the OriginalObject.

+ +

OnErrorAttribute

+ +

+ The OnErrorAttribute works much like the other .NET serialization attributes that Json.NET supports. + To use it you simply place the attribute on a method which takes the correct parameters: a StreamingContext and a ErrorContext. + The name of the method doesn't matter.

+
+ +
+
public class PersonError
+
{
+
  private List<string> _roles;
+
 
+
  public string Name { get; set; }
+
  public int Age { get; set; }
+
  public List<string> Roles
+
  {
+
    get
+
    {
+
      if (_roles == null)
+
        throw new Exception("Roles not loaded!");
+
 
+
      return _roles;
+
    }
+
    set { _roles = value; }
+
  }
+
  public string Title { get; set; }
+
 
+
  [OnError]
+
  internal void OnError(StreamingContext context, ErrorContext errorContext)
+
  {
+
    errorContext.Handled = true;
+
  }
+
}
+
+
+

+ In this example accessing the the Roles property will throw an exception when no roles have + been set. The HandleError method will set the error when serializing Roles as handled and allow Json.NET to continue + serializing the class. +

+ +
+
+
PersonError person = new PersonError
+
  {
+
    Name = "George Michael Bluth",
+
    Age = 16,
+
    Roles = null,
+
    Title = "Mister Manager"
+
  };
+
 
+
string json = JsonConvert.SerializeObject(person, Formatting.Indented);
+
 
+
Console.WriteLine(json);
+
//{
+
//  "Name": "George Michael Bluth",
+
//  "Age": 16,
+
//  "Title": "Mister Manager"
+
//}
+
+
+ + + +
+ + + + \ No newline at end of file diff --git a/Src/Doc/SerializingJSON.html b/Src/Doc/SerializingJSON.html index 807eaf52..3842eb95 100644 --- a/Src/Doc/SerializingJSON.html +++ b/Src/Doc/SerializingJSON.html @@ -92,12 +92,13 @@ the member's DefaultValueAttribute. Include or ignore.

ObjectCreationHandling

Controls how objects are created during deserialization. Auto, reuse, replace.

+
TypeNameHandling
+

Controls whether .NET type names are included in serialized JSON and read during deserialization when creating objects. None, Objects, Arrays or All.

+
ConstructorHandling
+

Controls how constructors are used when initializing objects during deserialization. Default or AllowNonPublicDefaultConstructor.

Converters

A collection of JsonConverters that will be used during serialization and deserialization.

-
TypeNameHandling
-

Controls how .NET type names are used when serializing and deserializing JSON. If - type names are SDFSDFSDF

JsonConverters

diff --git a/Src/Doc/doc.shfb b/Src/Doc/doc.shfb index 543b5e5c..c0f4372a 100644 --- a/Src/Doc/doc.shfb +++ b/Src/Doc/doc.shfb @@ -7,6 +7,7 @@ The <b>Newtonsoft.Json</b> namespace provides classes that are used to implement the core services of the framework. The <b>Newtonsoft.Json.Converters</b> namespace provides classes that inherit from <a href="T_Newtonsoft_Json_JsonConverter.htm">JsonConverter</a>. The <b>Newtonsoft.Json.Linq</b> namespace provides classes that are used to implement LINQ to JSON. + The <b>Newtonsoft.Json.Linq.ComponentModel</b> namespace provides classes for LINQ to JSON databinding. The <b>Newtonsoft.Json.Schema</b> namespace provides classes that are used to implement JSON schema. The <b>Newtonsoft.Json.Linq</b> namespace provides classes that are used when serializing and deserializing JSON. @@ -25,6 +26,7 @@ + Summary, Parameter, Returns, AutoDocumentCtors, Namespace, TypeParameter diff --git a/Src/Doc/doc.sitemap b/Src/Doc/doc.sitemap index f00d7646..ea3b2900 100644 --- a/Src/Doc/doc.sitemap +++ b/Src/Doc/doc.sitemap @@ -6,6 +6,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj index 5ed093f4..84fa77bc 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj @@ -120,6 +120,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj index fc9537b0..00e59e60 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj @@ -87,6 +87,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index 2f18bb4c..3f39a17d 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -126,6 +126,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index a5de0bab..14f00162 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -110,6 +110,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs index d36233f3..2cd9f01a 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs @@ -30,6 +30,9 @@ using System.Text; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Tests.TestObjects; using NUnit.Framework; +using Newtonsoft.Json.Serialization; +using System.IO; +using ErrorEventArgs=Newtonsoft.Json.Serialization.ErrorEventArgs; namespace Newtonsoft.Json.Tests.Serialization { @@ -201,5 +204,135 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(new DateTime(1977, 2, 20, 0, 0, 0, DateTimeKind.Utc), c[1]); Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, DateTimeKind.Utc), c[2]); } + + [Test] + public void DeserializingErrorHandlingUsingEvent() + { + List errors = new List(); + + List c = JsonConvert.DeserializeObject>(@"[ + ""2009-09-09T00:00:00Z"", + ""I am not a date and will error!"", + [ + 1 + ], + ""1977-02-20T00:00:00Z"", + null, + ""2000-12-01T00:00:00Z"" + ]", + new JsonSerializerSettings + { + Error = delegate(object sender, ErrorEventArgs args) + { + errors.Add(args.ErrorContext.Error.Message); + args.ErrorContext.Handled = true; + }, + Converters = { new IsoDateTimeConverter() } + }); + + // 2009-09-09T00:00:00Z + // 1977-02-20T00:00:00Z + // 2000-12-01T00:00:00Z + + // The string was not recognized as a valid DateTime. There is a unknown word starting at index 0. + // Unexpected token parsing date. Expected String, got StartArray. + // Cannot convert null value to System.DateTime. + + Assert.AreEqual(3, c.Count); + Assert.AreEqual(new DateTime(2009, 9, 9, 0, 0, 0, DateTimeKind.Utc), c[0]); + Assert.AreEqual(new DateTime(1977, 2, 20, 0, 0, 0, DateTimeKind.Utc), c[1]); + Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, DateTimeKind.Utc), c[2]); + + Assert.AreEqual(3, errors.Count); + Assert.AreEqual("The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.", errors[0]); + Assert.AreEqual("Unexpected token parsing date. Expected String, got StartArray.", errors[1]); + Assert.AreEqual("Cannot convert null value to System.DateTime.", errors[2]); + } + + [Test] + public void DeserializingErrorInDateTimeCollectionWithAttributeWithEventNotCalled() + { + bool eventErrorHandlerCalled = false; + + DateTimeErrorObjectCollection c = JsonConvert.DeserializeObject(@"[ + ""2009-09-09T00:00:00Z"", + ""kjhkjhkjhkjh"", + [ + 1 + ], + ""1977-02-20T00:00:00Z"", + null, + ""2000-12-01T00:00:00Z"" +]", + new JsonSerializerSettings + { + Error = (s, a) => eventErrorHandlerCalled = true, + Converters = + { + new IsoDateTimeConverter() + } + }); + + Assert.AreEqual(3, c.Count); + Assert.AreEqual(new DateTime(2009, 9, 9, 0, 0, 0, DateTimeKind.Utc), c[0]); + Assert.AreEqual(new DateTime(1977, 2, 20, 0, 0, 0, DateTimeKind.Utc), c[1]); + Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, DateTimeKind.Utc), c[2]); + + Assert.AreEqual(false, eventErrorHandlerCalled); + } + + [Test] + public void SerializePerson() + { + PersonError person = new PersonError + { + Name = "George Michael Bluth", + Age = 16, + Roles = null, + Title = "Mister Manager" + }; + + string json = JsonConvert.SerializeObject(person, Formatting.Indented); + + Console.WriteLine(json); + //{ + // "Name": "George Michael Bluth", + // "Age": 16, + // "Title": "Mister Manager" + //} + + Assert.AreEqual(@"{ + ""Name"": ""George Michael Bluth"", + ""Age"": 16, + ""Title"": ""Mister Manager"" +}", json); + } + + [Test] + public void DeserializeNestedUnhandled() + { + List errors = new List(); + + string json = @"[[""kjhkjhkjhkjh""]]"; + + try + { + JsonSerializer serializer = new JsonSerializer(); + serializer.Error += delegate(object sender, ErrorEventArgs args) + { + // only log an error once + if (args.CurrentObject == args.ErrorContext.OriginalObject) + errors.Add(args.ErrorContext.Error.Message); + }; + + serializer.Deserialize(new StringReader(json), typeof(List>)); + } + catch + { + } + + Assert.AreEqual(1, errors.Count); + Assert.AreEqual("Could not cast or convert from System.String to System.DateTime.", errors[0]); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/PersonError.cs b/Src/Newtonsoft.Json.Tests/TestObjects/PersonError.cs new file mode 100644 index 00000000..7a9483bf --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/TestObjects/PersonError.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class PersonError + { + private List _roles; + + public string Name { get; set; } + public int Age { get; set; } + public List Roles + { + get + { + if (_roles == null) + throw new Exception("Roles not loaded!"); + + return _roles; + } + set { _roles = value; } + } + public string Title { get; set; } + + [OnError] + internal void HandleError(StreamingContext context, ErrorContext errorContext) + { + errorContext.Handled = true; + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.sln b/Src/Newtonsoft.Json.sln index 5414e8fa..6f1a67ca 100644 --- a/Src/Newtonsoft.Json.sln +++ b/Src/Newtonsoft.Json.sln @@ -25,7 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Doc", "Doc", "{8F9E13D9-418 Doc\PreserveObjectReferences.html = Doc\PreserveObjectReferences.html Doc\ReadingWritingJSON.html = Doc\ReadingWritingJSON.html Doc\SerializationAttributes.html = Doc\SerializationAttributes.html - Doc\SerializationCallbacks.html = Doc\SerializationCallbacks.html + Doc\SerializationErrorHandling.html = Doc\SerializationErrorHandling.html Doc\SerializingCollections.html = Doc\SerializingCollections.html Doc\SerializingJSON.html = Doc\SerializingJSON.html Doc\styles.css = Doc\styles.css diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index f3babd7d..77bdefa4 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -30,6 +30,7 @@ using Newtonsoft.Json.Converters; using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Utilities; using System.Runtime.Serialization; +using ErrorEventArgs=Newtonsoft.Json.Serialization.ErrorEventArgs; namespace Newtonsoft.Json { @@ -60,6 +61,11 @@ namespace Newtonsoft.Json #endif }; + /// + /// Occurs when the errors during serialization and deserialization. + /// + public virtual event EventHandler Error; + /// /// Gets or sets the used by the serializer when resolving references. /// @@ -292,6 +298,9 @@ namespace Newtonsoft.Json jsonSerializer.DefaultValueHandling = settings.DefaultValueHandling; jsonSerializer.ConstructorHandling = settings.ConstructorHandling; + if (settings.Error != null) + jsonSerializer.Error += settings.Error; + if (settings.ContractResolver != null) jsonSerializer.ContractResolver = settings.ContractResolver; if (settings.ReferenceResolver != null) @@ -445,5 +454,12 @@ namespace Newtonsoft.Json matchingConverter = null; return false; } + + internal void OnError(ErrorEventArgs e) + { + EventHandler error = Error; + if (error != null) + error(this, e); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonSerializerSettings.cs b/Src/Newtonsoft.Json/JsonSerializerSettings.cs index f247ba0f..500ce0b8 100644 --- a/Src/Newtonsoft.Json/JsonSerializerSettings.cs +++ b/Src/Newtonsoft.Json/JsonSerializerSettings.cs @@ -85,6 +85,12 @@ namespace Newtonsoft.Json /// The binder. public SerializationBinder Binder { get; set; } + /// + /// Gets or sets the error handler called during serialization and deserialization. + /// + /// The error handler called during serialization and deserialization. + public EventHandler Error { get; set;} + /// /// Initializes a new instance of the class. /// diff --git a/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs b/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs index b3445523..4f5cff4f 100644 --- a/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs +++ b/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs @@ -153,6 +153,23 @@ namespace Newtonsoft.Json.Linq.ComponentModel { get { return _propertyType; } } + + /// + /// Gets the hash code for the name of the member. + /// + /// + /// + /// The hash code for the name of the member. + /// + protected override int NameHashCode + { + get + { + // override property to fix up an error in its documentation + int nameHashCode = base.NameHashCode; + return nameHashCode; + } + } } } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 13bf662e..fc813665 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -98,7 +98,7 @@ namespace Newtonsoft.Json.Linq #if !SILVERLIGHT /// - /// Raises the event. + /// Raises the event. /// /// The instance containing the event data. protected virtual void OnAddingNew(AddingNewEventArgs e) @@ -109,7 +109,7 @@ namespace Newtonsoft.Json.Linq } /// - /// Raises the event. + /// Raises the event. /// /// The instance containing the event data. protected virtual void OnListChanged(ListChangedEventArgs e) diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index b7a36299..f6a01d16 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -463,7 +463,7 @@ namespace Newtonsoft.Json.Linq } /// - /// Raises the event with the provided arguments. + /// Raises the event with the provided arguments. /// /// Name of the property. protected virtual void OnPropertyChanged(string propertyName) @@ -474,7 +474,7 @@ namespace Newtonsoft.Json.Linq #if !PocketPC && !SILVERLIGHT /// - /// Raises the event with the provided arguments. + /// Raises the event with the provided arguments. /// /// Name of the property. protected virtual void OnPropertyChanging(string propertyName) diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj index 765c1d0d..148bc41c 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj @@ -126,6 +126,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index 160d3cdb..e1cdbd1b 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -144,6 +144,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index e811cb9b..d6f605ff 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -114,6 +114,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 81ff65ee..867c8a07 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -91,6 +91,7 @@ + diff --git a/Src/Newtonsoft.Json/Serialization/ErrorEventArgs.cs b/Src/Newtonsoft.Json/Serialization/ErrorEventArgs.cs new file mode 100644 index 00000000..f4c7c5b8 --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/ErrorEventArgs.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Provides data for the Error event. + /// + public class ErrorEventArgs : EventArgs + { + /// + /// Gets the current object the error event is being raised against. + /// + /// The current object the error event is being raised against. + public object CurrentObject { get; private set; } + /// + /// Gets the error context. + /// + /// The error context. + public ErrorContext ErrorContext { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The current object. + /// The error context. + public ErrorEventArgs(object currentObject, ErrorContext errorContext) + { + CurrentObject = currentObject; + ErrorContext = errorContext; + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs index de9ccb04..83f8232e 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs @@ -33,10 +33,19 @@ namespace Newtonsoft.Json.Serialization { private ErrorContext _currentErrorContext; - protected ErrorContext GetErrorContext(object originalObject, object member, Exception error) + internal JsonSerializer Serializer { get; private set; } + + protected JsonSerializerInternalBase(JsonSerializer serializer) + { + ValidationUtils.ArgumentNotNull(serializer, "serializer"); + + Serializer = serializer; + } + + protected ErrorContext GetErrorContext(object currentObject, object member, Exception error) { if (_currentErrorContext == null) - _currentErrorContext = new ErrorContext(originalObject, member, error); + _currentErrorContext = new ErrorContext(currentObject, member, error); if (_currentErrorContext.Error != error) throw new InvalidOperationException("Current error context error is different to requested error."); @@ -51,5 +60,16 @@ namespace Newtonsoft.Json.Serialization _currentErrorContext = null; } + + protected bool IsErrorHandled(object currentObject, JsonContract contract, object keyValue, Exception ex) + { + ErrorContext errorContext = GetErrorContext(currentObject, keyValue, ex); + contract.InvokeOnError(currentObject, errorContext); + + if (!errorContext.Handled) + Serializer.OnError(new ErrorEventArgs(currentObject, errorContext)); + + return errorContext.Handled; + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 92f72847..38f9f412 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -37,14 +37,10 @@ namespace Newtonsoft.Json.Serialization { internal class JsonSerializerInternalReader : JsonSerializerInternalBase { - internal readonly JsonSerializer _serializer; private JsonSerializerProxy _internalSerializer; - public JsonSerializerInternalReader(JsonSerializer serializer) + public JsonSerializerInternalReader(JsonSerializer serializer) : base(serializer) { - ValidationUtils.ArgumentNotNull(serializer, "serializer"); - - _serializer = serializer; } public void Populate(JsonReader reader, object target) @@ -53,7 +49,7 @@ namespace Newtonsoft.Json.Serialization Type objectType = target.GetType(); - JsonContract contract = _serializer.ContractResolver.ResolveContract(objectType); + JsonContract contract = Serializer.ContractResolver.ResolveContract(objectType); if (reader.TokenType == JsonToken.None) reader.Read(); @@ -145,10 +141,10 @@ namespace Newtonsoft.Json.Serialization if (memberConverter != null) return memberConverter.ReadJson(reader, objectType, GetInternalSerializer()); - if (objectType != null && _serializer.HasClassConverter(objectType, out converter)) + if (objectType != null && Serializer.HasClassConverter(objectType, out converter)) return converter.ReadJson(reader, objectType, GetInternalSerializer()); - if (objectType != null && _serializer.HasMatchingConverter(objectType, out converter)) + if (objectType != null && Serializer.HasMatchingConverter(objectType, out converter)) return converter.ReadJson(reader, objectType, GetInternalSerializer()); if (objectType == typeof (JsonRaw)) @@ -219,7 +215,7 @@ namespace Newtonsoft.Json.Serialization string reference = reader.Value.ToString(); CheckedRead(reader); - return _serializer.ReferenceResolver.ResolveReference(reference); + return Serializer.ReferenceResolver.ResolveReference(reference); } else if (string.Equals(propertyName, JsonTypeReflector.TypePropertyName, StringComparison.Ordinal)) { @@ -228,7 +224,7 @@ namespace Newtonsoft.Json.Serialization CheckedRead(reader); - if (_serializer.TypeNameHandling != TypeNameHandling.None) + if (Serializer.TypeNameHandling != TypeNameHandling.None) { string typeName; string assemblyName; @@ -237,7 +233,7 @@ namespace Newtonsoft.Json.Serialization Type specifiedType; try { - specifiedType = _serializer.Binder.BindToType(assemblyName, typeName); + specifiedType = Serializer.Binder.BindToType(assemblyName, typeName); } catch (Exception ex) { @@ -280,7 +276,7 @@ namespace Newtonsoft.Json.Serialization if (!HasDefinedType(objectType)) return CreateJObject(reader); - JsonContract contract = _serializer.ContractResolver.ResolveContract(objectType); + JsonContract contract = Serializer.ContractResolver.ResolveContract(objectType); if (contract == null) throw new JsonSerializationException("Could not resolve type '{0}' to a JsonContract.".FormatWith(CultureInfo.InvariantCulture, objectType)); @@ -306,7 +302,7 @@ namespace Newtonsoft.Json.Serialization private JsonArrayContract GetArrayContract(Type objectType) { - JsonContract contract = _serializer.ContractResolver.ResolveContract(objectType); + JsonContract contract = Serializer.ContractResolver.ResolveContract(objectType); if (contract == null) throw new JsonSerializationException("Could not resolve type '{0}' to a JsonContract.".FormatWith(CultureInfo.InvariantCulture, objectType)); @@ -377,7 +373,7 @@ namespace Newtonsoft.Json.Serialization } else { - if (_serializer.MissingMemberHandling == MissingMemberHandling.Error) + if (Serializer.MissingMemberHandling == MissingMemberHandling.Error) throw new JsonSerializationException("Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType.Name)); reader.Skip(); @@ -398,7 +394,7 @@ namespace Newtonsoft.Json.Serialization object currentValue = null; bool useExistingValue = false; - if ((_serializer.ObjectCreationHandling == ObjectCreationHandling.Auto || _serializer.ObjectCreationHandling == ObjectCreationHandling.Reuse) + if ((Serializer.ObjectCreationHandling == ObjectCreationHandling.Auto || Serializer.ObjectCreationHandling == ObjectCreationHandling.Reuse) && (reader.TokenType == JsonToken.StartArray || reader.TokenType == JsonToken.StartObject) && property.Readable) { @@ -421,10 +417,10 @@ namespace Newtonsoft.Json.Serialization private bool ShouldSetPropertyValue(JsonProperty property, object value) { - if (property.NullValueHandling.GetValueOrDefault(_serializer.NullValueHandling) == NullValueHandling.Ignore && value == null) + if (property.NullValueHandling.GetValueOrDefault(Serializer.NullValueHandling) == NullValueHandling.Ignore && value == null) return false; - if (property.DefaultValueHandling.GetValueOrDefault(_serializer.DefaultValueHandling) == DefaultValueHandling.Ignore && Equals(value, property.DefaultValue)) + if (property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling) == DefaultValueHandling.Ignore && Equals(value, property.DefaultValue)) return false; if (!property.Writable) @@ -445,7 +441,7 @@ namespace Newtonsoft.Json.Serialization private IDictionary PopulateDictionary(IWrappedDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, string id) { if (id != null) - _serializer.ReferenceResolver.AddReference(id, dictionary.UnderlyingDictionary); + Serializer.ReferenceResolver.AddReference(id, dictionary.UnderlyingDictionary); contract.InvokeOnDeserializing(dictionary.UnderlyingDictionary); @@ -465,10 +461,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - ErrorContext errorContext = GetErrorContext(dictionary.UnderlyingDictionary, keyValue, ex); - contract.InvokeOnError(dictionary.UnderlyingDictionary, errorContext); - - if (errorContext.Handled) + if (IsErrorHandled(dictionary, contract, keyValue, ex)) HandleError(reader, initialDepth); else throw; @@ -509,7 +502,7 @@ namespace Newtonsoft.Json.Serialization object list = wrappedList.UnderlyingCollection; if (reference != null) - _serializer.ReferenceResolver.AddReference(reference, list); + Serializer.ReferenceResolver.AddReference(reference, list); contract.InvokeOnDeserializing(list); @@ -534,10 +527,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - ErrorContext errorContext = GetErrorContext(list, wrappedList.Count, ex); - contract.InvokeOnError(list, errorContext); - - if (errorContext.Handled) + if (IsErrorHandled(list, contract, wrappedList.Count, ex)) HandleError(reader, initialDepth); else throw; @@ -557,7 +547,7 @@ namespace Newtonsoft.Json.Serialization throw new JsonSerializationException("Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); if (contract.DefaultContstructor != null && - (contract.DefaultContstructor.IsPublic || _serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) + (contract.DefaultContstructor.IsPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) { newObject = contract.DefaultContstructor.Invoke(null); @@ -601,7 +591,7 @@ namespace Newtonsoft.Json.Serialization } else { - if (_serializer.MissingMemberHandling == MissingMemberHandling.Error) + if (Serializer.MissingMemberHandling == MissingMemberHandling.Error) throw new JsonSerializationException("Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, objectType.Name)); reader.Skip(); @@ -630,7 +620,7 @@ namespace Newtonsoft.Json.Serialization object createdObject = contract.ParametrizedConstructor.Invoke(constructorParameters.Values.ToArray()); if (id != null) - _serializer.ReferenceResolver.AddReference(id, createdObject); + Serializer.ReferenceResolver.AddReference(id, createdObject); contract.InvokeOnDeserializing(createdObject); @@ -653,7 +643,7 @@ namespace Newtonsoft.Json.Serialization contract.Properties.Where(m => m.Required).ToDictionary(m => m.PropertyName, m => false); if (id != null) - _serializer.ReferenceResolver.AddReference(id, newObject); + Serializer.ReferenceResolver.AddReference(id, newObject); int initialDepth = reader.Depth; @@ -676,10 +666,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - ErrorContext errorContext = GetErrorContext(newObject, memberName, ex); - contract.InvokeOnError(newObject, errorContext); - - if (errorContext.Handled) + if (IsErrorHandled(newObject, contract, memberName, ex)) HandleError(reader, initialDepth); else throw; @@ -730,6 +717,8 @@ namespace Newtonsoft.Json.Serialization { ClearErrorContext(); + reader.Skip(); + while (reader.Depth > (initialDepth + 1)) { reader.Read(); diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 1dd24ee8..9b615bb6 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -38,7 +38,6 @@ namespace Newtonsoft.Json.Serialization { internal class JsonSerializerInternalWriter : JsonSerializerInternalBase { - internal readonly JsonSerializer _serializer; private JsonSerializerProxy _internalSerializer; private List _serializeStack; @@ -53,11 +52,8 @@ namespace Newtonsoft.Json.Serialization } } - public JsonSerializerInternalWriter(JsonSerializer serializer) + public JsonSerializerInternalWriter(JsonSerializer serializer) : base(serializer) { - ValidationUtils.ArgumentNotNull(serializer, "serializer"); - - _serializer = serializer; } public void Serialize(JsonWriter jsonWriter, object value) @@ -85,8 +81,8 @@ namespace Newtonsoft.Json.Serialization writer.WriteNull(); } else if (converter != null - || _serializer.HasClassConverter(value.GetType(), out converter) - || _serializer.HasMatchingConverter(value.GetType(), out converter)) + || Serializer.HasClassConverter(value.GetType(), out converter) + || Serializer.HasMatchingConverter(value.GetType(), out converter)) { SerializeConvertable(writer, converter, value); } @@ -96,7 +92,7 @@ namespace Newtonsoft.Json.Serialization } else if (value is JToken) { - ((JToken)value).WriteTo(writer, (_serializer.Converters != null) ? _serializer.Converters.ToArray() : null); + ((JToken)value).WriteTo(writer, (Serializer.Converters != null) ? Serializer.Converters.ToArray() : null); } else if (value is JsonRaw) { @@ -104,7 +100,7 @@ namespace Newtonsoft.Json.Serialization } else { - JsonContract contract = _serializer.ContractResolver.ResolveContract(value.GetType()); + JsonContract contract = Serializer.ContractResolver.ResolveContract(value.GetType()); if (contract is JsonObjectContract) { @@ -147,7 +143,7 @@ namespace Newtonsoft.Json.Serialization if (isReference == null) { - JsonContract memberTypeContract = _serializer.ContractResolver.ResolveContract(value.GetType()); + JsonContract memberTypeContract = Serializer.ContractResolver.ResolveContract(value.GetType()); if (memberTypeContract != null) isReference = memberTypeContract.IsReference; } @@ -155,19 +151,19 @@ namespace Newtonsoft.Json.Serialization if (isReference == null) { if (value is IList) - isReference = HasFlag(_serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays); + isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays); else if (value is IDictionary) - isReference = HasFlag(_serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); + isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); else if (value is IEnumerable) - isReference = HasFlag(_serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays); + isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays); else - isReference = HasFlag(_serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); + isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); } if (!isReference.Value) return false; - return _serializer.ReferenceResolver.IsReferenced(value); + return Serializer.ReferenceResolver.IsReferenced(value); } private void WriteMemberInfoProperty(JsonWriter writer, object value, JsonProperty property) @@ -181,10 +177,10 @@ namespace Newtonsoft.Json.Serialization { object memberValue = ReflectionUtils.GetMemberValue(member, value); - if (property.NullValueHandling.GetValueOrDefault(_serializer.NullValueHandling) == NullValueHandling.Ignore && memberValue == null) + if (property.NullValueHandling.GetValueOrDefault(Serializer.NullValueHandling) == NullValueHandling.Ignore && memberValue == null) return; - if (property.DefaultValueHandling.GetValueOrDefault(_serializer.DefaultValueHandling) == DefaultValueHandling.Ignore && Equals(memberValue, defaultValue)) + if (property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling) == DefaultValueHandling.Ignore && Equals(memberValue, defaultValue)) return; if (ShouldWriteReference(memberValue, property)) @@ -206,7 +202,7 @@ namespace Newtonsoft.Json.Serialization { if (SerializeStack.IndexOf(value) != -1) { - switch (referenceLoopHandling.GetValueOrDefault(_serializer.ReferenceLoopHandling)) + switch (referenceLoopHandling.GetValueOrDefault(Serializer.ReferenceLoopHandling)) { case ReferenceLoopHandling.Error: throw new JsonSerializationException("Self referencing loop"); @@ -215,7 +211,7 @@ namespace Newtonsoft.Json.Serialization case ReferenceLoopHandling.Serialize: return true; default: - throw new InvalidOperationException("Unexpected ReferenceLoopHandling value: '{0}'".FormatWith(CultureInfo.InvariantCulture, _serializer.ReferenceLoopHandling)); + throw new InvalidOperationException("Unexpected ReferenceLoopHandling value: '{0}'".FormatWith(CultureInfo.InvariantCulture, Serializer.ReferenceLoopHandling)); } } @@ -226,7 +222,7 @@ namespace Newtonsoft.Json.Serialization { writer.WriteStartObject(); writer.WritePropertyName(JsonTypeReflector.RefPropertyName); - writer.WriteValue(_serializer.ReferenceResolver.GetReference(value)); + writer.WriteValue(Serializer.ReferenceResolver.GetReference(value)); writer.WriteEndObject(); } @@ -274,13 +270,13 @@ namespace Newtonsoft.Json.Serialization SerializeStack.Add(value); writer.WriteStartObject(); - bool isReference = contract.IsReference ?? HasFlag(_serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); + bool isReference = contract.IsReference ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); if (isReference) { writer.WritePropertyName(JsonTypeReflector.IdPropertyName); - writer.WriteValue(_serializer.ReferenceResolver.GetReference(value)); + writer.WriteValue(Serializer.ReferenceResolver.GetReference(value)); } - if (HasFlag(_serializer.TypeNameHandling, TypeNameHandling.Objects)) + if (HasFlag(Serializer.TypeNameHandling, TypeNameHandling.Objects)) { WriteTypeProperty(writer, contract.UnderlyingType); } @@ -296,10 +292,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - ErrorContext errorContext = GetErrorContext(value, property.Member.Name, ex); - contract.InvokeOnError(value, errorContext); - - if (errorContext.Handled) + if (IsErrorHandled(value, contract, property.Member.Name, ex)) HandleError(writer, initialDepth); else throw; @@ -358,8 +351,8 @@ namespace Newtonsoft.Json.Serialization SerializeStack.Add(values); - bool isReference = contract.IsReference ?? HasFlag(_serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays); - bool includeTypeDetails = HasFlag(_serializer.TypeNameHandling, TypeNameHandling.Arrays); + bool isReference = contract.IsReference ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays); + bool includeTypeDetails = HasFlag(Serializer.TypeNameHandling, TypeNameHandling.Arrays); if (isReference || includeTypeDetails) { @@ -368,7 +361,7 @@ namespace Newtonsoft.Json.Serialization if (isReference) { writer.WritePropertyName(JsonTypeReflector.IdPropertyName); - writer.WriteValue(_serializer.ReferenceResolver.GetReference(values)); + writer.WriteValue(Serializer.ReferenceResolver.GetReference(values)); } if (includeTypeDetails) { @@ -401,10 +394,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - ErrorContext errorContext = GetErrorContext(values, i, ex); - contract.InvokeOnError(values, errorContext); - - if (errorContext.Handled) + if (IsErrorHandled(values, contract, i, ex)) HandleError(writer, initialDepth); else throw; @@ -430,13 +420,13 @@ namespace Newtonsoft.Json.Serialization SerializeStack.Add(values); writer.WriteStartObject(); - bool isReference = contract.IsReference ?? HasFlag(_serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); + bool isReference = contract.IsReference ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); if (isReference) { writer.WritePropertyName(JsonTypeReflector.IdPropertyName); - writer.WriteValue(_serializer.ReferenceResolver.GetReference(values)); + writer.WriteValue(Serializer.ReferenceResolver.GetReference(values)); } - if (HasFlag(_serializer.TypeNameHandling, TypeNameHandling.Objects)) + if (HasFlag(Serializer.TypeNameHandling, TypeNameHandling.Objects)) { WriteTypeProperty(writer, values.GetType()); } @@ -468,10 +458,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - ErrorContext errorContext = GetErrorContext(values, propertyName, ex); - contract.InvokeOnError(values, errorContext); - - if (errorContext.Handled) + if (IsErrorHandled(values, contract, propertyName, ex)) HandleError(writer, initialDepth); else throw; diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs index 64bf989a..a55bd8d2 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs @@ -35,6 +35,12 @@ namespace Newtonsoft.Json.Serialization private readonly JsonSerializerInternalWriter _serializerWriter; private readonly JsonSerializer _serializer; + public override event EventHandler Error + { + add { _serializer.Error += value; } + remove { _serializer.Error -= value; } + } + public override IReferenceResolver ReferenceResolver { get { return _serializer.ReferenceResolver; } @@ -111,7 +117,7 @@ namespace Newtonsoft.Json.Serialization ValidationUtils.ArgumentNotNull(serializerReader, "serializerReader"); _serializerReader = serializerReader; - _serializer = serializerReader._serializer; + _serializer = serializerReader.Serializer; } public JsonSerializerProxy(JsonSerializerInternalWriter serializerWriter) @@ -119,7 +125,7 @@ namespace Newtonsoft.Json.Serialization ValidationUtils.ArgumentNotNull(serializerWriter, "serializerWriter"); _serializerWriter = serializerWriter; - _serializer = serializerWriter._serializer; + _serializer = serializerWriter.Serializer; } internal override object DeserializeInternal(JsonReader reader, Type objectType)