Merge pull request #56 from xpaulbettsx/call-base-class-ondeserialized
Call base classes' "On[De]Serializ[ing|ed]" as well as derived class
This commit is contained in:
@@ -48,41 +48,53 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
[Test]
|
||||
public void ObjectEvents()
|
||||
{
|
||||
SerializationEventTestObject obj = new SerializationEventTestObject();
|
||||
SerializationEventTestObject[] objs = new[] { new SerializationEventTestObject(), new DerivedSerializationEventTestObject() };
|
||||
|
||||
Assert.AreEqual(11, obj.Member1);
|
||||
Assert.AreEqual("Hello World!", obj.Member2);
|
||||
Assert.AreEqual("This is a nonserialized value", obj.Member3);
|
||||
Assert.AreEqual(null, obj.Member4);
|
||||
Assert.AreEqual(null, obj.Member5);
|
||||
foreach(SerializationEventTestObject current in objs)
|
||||
{
|
||||
SerializationEventTestObject obj = current;
|
||||
|
||||
string json = JsonConvert.SerializeObject(obj, Formatting.Indented);
|
||||
Assert.AreEqual(@"{
|
||||
""Member1"": 11,
|
||||
""Member2"": ""This value went into the data file during serialization."",
|
||||
""Member4"": null
|
||||
}", json);
|
||||
Assert.AreEqual(11, obj.Member1);
|
||||
Assert.AreEqual("Hello World!", obj.Member2);
|
||||
Assert.AreEqual("This is a nonserialized value", obj.Member3);
|
||||
Assert.AreEqual(null, obj.Member4);
|
||||
Assert.AreEqual(null, obj.Member5);
|
||||
|
||||
Assert.AreEqual(11, obj.Member1);
|
||||
Assert.AreEqual("This value was reset after serialization.", obj.Member2);
|
||||
Assert.AreEqual("This is a nonserialized value", obj.Member3);
|
||||
Assert.AreEqual(null, obj.Member4);
|
||||
Assert.AreEqual("Error message for member Member6 = Error getting value from 'Member6' on 'Newtonsoft.Json.Tests.TestObjects.SerializationEventTestObject'.", obj.Member5);
|
||||
string json = JsonConvert.SerializeObject(obj, Formatting.Indented);
|
||||
Assert.AreEqual(@"{
|
||||
""Member1"": 11,
|
||||
""Member2"": ""This value went into the data file during serialization."",
|
||||
""Member4"": null
|
||||
}", json);
|
||||
|
||||
JObject o = JObject.Parse(@"{
|
||||
""Member1"": 11,
|
||||
""Member2"": ""This value went into the data file during serialization."",
|
||||
""Member4"": null
|
||||
}");
|
||||
o["Member6"] = "Dummy text for error";
|
||||
Assert.AreEqual(11, obj.Member1);
|
||||
Assert.AreEqual("This value was reset after serialization.", obj.Member2);
|
||||
Assert.AreEqual("This is a nonserialized value", obj.Member3);
|
||||
Assert.AreEqual(null, obj.Member4);
|
||||
|
||||
obj = JsonConvert.DeserializeObject<SerializationEventTestObject>(o.ToString());
|
||||
string expectedError = String.Format("Error message for member Member6 = Error getting value from 'Member6' on '{0}'.", obj.GetType().FullName);
|
||||
Assert.AreEqual(expectedError, obj.Member5);
|
||||
|
||||
Assert.AreEqual(11, obj.Member1);
|
||||
Assert.AreEqual("This value went into the data file during serialization.", obj.Member2);
|
||||
Assert.AreEqual("This value was set during deserialization", obj.Member3);
|
||||
Assert.AreEqual("This value was set after deserialization.", obj.Member4);
|
||||
Assert.AreEqual("Error message for member Member6 = Error setting value to 'Member6' on 'Newtonsoft.Json.Tests.TestObjects.SerializationEventTestObject'.", obj.Member5);
|
||||
JObject o = JObject.Parse(@"{
|
||||
""Member1"": 11,
|
||||
""Member2"": ""This value went into the data file during serialization."",
|
||||
""Member4"": null
|
||||
}");
|
||||
o["Member6"] = "Dummy text for error";
|
||||
|
||||
obj = (SerializationEventTestObject) JsonConvert.DeserializeObject(o.ToString(), obj.GetType());
|
||||
|
||||
Assert.AreEqual(11, obj.Member1);
|
||||
Assert.AreEqual("This value went into the data file during serialization.", obj.Member2);
|
||||
Assert.AreEqual("This value was set during deserialization", obj.Member3);
|
||||
Assert.AreEqual("This value was set after deserialization.", obj.Member4);
|
||||
|
||||
expectedError = String.Format("Error message for member Member6 = Error setting value to 'Member6' on '{0}'.", obj.GetType());
|
||||
Assert.AreEqual(expectedError, obj.Member5);
|
||||
|
||||
DerivedSerializationEventTestObject derivedObj = obj as DerivedSerializationEventTestObject;
|
||||
if (derivedObj != null) Assert.AreEqual("This value was set after deserialization.", derivedObj.Member7);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -335,4 +347,4 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,4 +96,40 @@ namespace Newtonsoft.Json.Tests.TestObjects
|
||||
errorContext.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DerivedSerializationEventTestObject : SerializationEventTestObject
|
||||
{
|
||||
// This field is set to null, but populated after deserialization, only
|
||||
// in the derived class
|
||||
[JsonIgnore]
|
||||
public string Member7 { get; set; }
|
||||
|
||||
// These empty methods exist to make sure we're not covering up the base
|
||||
// methods
|
||||
[OnSerializing]
|
||||
internal void OnDerivedSerializingMethod(StreamingContext context)
|
||||
{
|
||||
}
|
||||
|
||||
[OnSerialized]
|
||||
internal void OnDerivedSerializedMethod(StreamingContext context)
|
||||
{
|
||||
}
|
||||
|
||||
[OnDeserializing]
|
||||
internal void OnDerivedDeserializingMethod(StreamingContext context)
|
||||
{
|
||||
}
|
||||
|
||||
[OnDeserialized]
|
||||
internal void OnDerivedDeserializedMethod(StreamingContext context)
|
||||
{
|
||||
Member7 = "This value was set after deserialization.";
|
||||
}
|
||||
|
||||
[OnError]
|
||||
internal void OnDerivedErrorMethod(StreamingContext context, ErrorContext errorContext)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,11 +550,11 @@ namespace Newtonsoft.Json.Serialization
|
||||
if (t.BaseType() != null)
|
||||
ResolveCallbackMethods(contract, t.BaseType());
|
||||
|
||||
MethodInfo onSerializing;
|
||||
MethodInfo onSerialized;
|
||||
MethodInfo onDeserializing;
|
||||
MethodInfo onDeserialized;
|
||||
MethodInfo onError;
|
||||
List<MethodInfo> onSerializing;
|
||||
List<MethodInfo> onSerialized;
|
||||
List<MethodInfo> onDeserializing;
|
||||
List<MethodInfo> onDeserialized;
|
||||
List<MethodInfo> onError;
|
||||
|
||||
GetCallbackMethodsForType(t, out onSerializing, out onSerialized, out onDeserializing, out onDeserialized, out onError);
|
||||
|
||||
@@ -562,34 +562,34 @@ namespace Newtonsoft.Json.Serialization
|
||||
{
|
||||
#if NETFX_CORE
|
||||
if (!t.IsGenericType() || (t.GetGenericTypeDefinition() != typeof(ConcurrentDictionary<,>)))
|
||||
contract.OnSerializing = onSerializing;
|
||||
contract.OnSerializingMethods = onSerializing;
|
||||
#else
|
||||
contract.OnSerializing = onSerializing;
|
||||
contract.OnSerializingMethods = onSerializing;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (onSerialized != null)
|
||||
contract.OnSerialized = onSerialized;
|
||||
contract.OnSerializedMethods = onSerialized;
|
||||
|
||||
if (onDeserializing != null)
|
||||
contract.OnDeserializing = onDeserializing;
|
||||
contract.OnDeserializingMethods = onDeserializing;
|
||||
|
||||
if (onDeserialized != null)
|
||||
{
|
||||
// ConcurrentDictionary throws an error here so don't use its OnDeserialized - http://json.codeplex.com/discussions/257093
|
||||
#if !(NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE || PORTABLE)
|
||||
if (!t.IsGenericType() || (t.GetGenericTypeDefinition() != typeof(ConcurrentDictionary<,>)))
|
||||
contract.OnDeserialized = onDeserialized;
|
||||
contract.OnDeserializedMethods = onDeserialized;
|
||||
#else
|
||||
contract.OnDeserialized = onDeserialized;
|
||||
contract.OnDeserializedMethods = onDeserialized;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (onError != null)
|
||||
contract.OnError = onError;
|
||||
contract.OnErrorMethods = onError;
|
||||
}
|
||||
|
||||
private void GetCallbackMethodsForType(Type type, out MethodInfo onSerializing, out MethodInfo onSerialized, out MethodInfo onDeserializing, out MethodInfo onDeserialized, out MethodInfo onError)
|
||||
private void GetCallbackMethodsForType(Type type, out List<MethodInfo> onSerializing, out List<MethodInfo> onSerialized, out List<MethodInfo> onDeserializing, out List<MethodInfo> onDeserialized, out List<MethodInfo> onError)
|
||||
{
|
||||
onSerializing = null;
|
||||
onSerialized = null;
|
||||
@@ -597,39 +597,80 @@ namespace Newtonsoft.Json.Serialization
|
||||
onDeserialized = null;
|
||||
onError = null;
|
||||
|
||||
foreach (MethodInfo method in type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
|
||||
foreach (Type baseType in GetClassHierarchyForType(type))
|
||||
{
|
||||
// compact framework errors when getting parameters for a generic method
|
||||
// lame, but generic methods should not be callbacks anyway
|
||||
if (method.ContainsGenericParameters)
|
||||
continue;
|
||||
// while we allow more than one OnSerialized total, only one can be defined
|
||||
// per class
|
||||
MethodInfo currentOnSerializing = null;
|
||||
MethodInfo currentOnSerialized = null;
|
||||
MethodInfo currentOnDeserializing = null;
|
||||
MethodInfo currentOnDeserialized = null;
|
||||
MethodInfo currentOnError = null;
|
||||
|
||||
Type prevAttributeType = null;
|
||||
ParameterInfo[] parameters = method.GetParameters();
|
||||
foreach (MethodInfo method in baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
// compact framework errors when getting parameters for a generic method
|
||||
// lame, but generic methods should not be callbacks anyway
|
||||
if (method.ContainsGenericParameters)
|
||||
continue;
|
||||
|
||||
if (IsValidCallback(method, parameters, typeof(OnSerializingAttribute), onSerializing, ref prevAttributeType))
|
||||
{
|
||||
onSerializing = method;
|
||||
}
|
||||
if (IsValidCallback(method, parameters, typeof(OnSerializedAttribute), onSerialized, ref prevAttributeType))
|
||||
{
|
||||
onSerialized = method;
|
||||
}
|
||||
if (IsValidCallback(method, parameters, typeof(OnDeserializingAttribute), onDeserializing, ref prevAttributeType))
|
||||
{
|
||||
onDeserializing = method;
|
||||
}
|
||||
if (IsValidCallback(method, parameters, typeof(OnDeserializedAttribute), onDeserialized, ref prevAttributeType))
|
||||
{
|
||||
onDeserialized = method;
|
||||
}
|
||||
if (IsValidCallback(method, parameters, typeof(OnErrorAttribute), onError, ref prevAttributeType))
|
||||
{
|
||||
onError = method;
|
||||
Type prevAttributeType = null;
|
||||
ParameterInfo[] parameters = method.GetParameters();
|
||||
|
||||
if (IsValidCallback(method, parameters, typeof(OnSerializingAttribute), currentOnSerializing, ref prevAttributeType))
|
||||
{
|
||||
onSerializing = onSerializing ?? new List<MethodInfo>();
|
||||
onSerializing.Add(method);
|
||||
currentOnSerializing = method;
|
||||
}
|
||||
if (IsValidCallback(method, parameters, typeof(OnSerializedAttribute), currentOnSerialized, ref prevAttributeType))
|
||||
{
|
||||
onSerialized = onSerialized ?? new List<MethodInfo>();
|
||||
onSerialized.Add(method);
|
||||
currentOnSerialized = method;
|
||||
}
|
||||
if (IsValidCallback(method, parameters, typeof(OnDeserializingAttribute), currentOnDeserializing, ref prevAttributeType))
|
||||
{
|
||||
onDeserializing = onDeserializing ?? new List<MethodInfo>();
|
||||
onDeserializing.Add(method);
|
||||
currentOnDeserializing = method;
|
||||
}
|
||||
if (IsValidCallback(method, parameters, typeof(OnDeserializedAttribute), currentOnDeserialized, ref prevAttributeType))
|
||||
{
|
||||
onDeserialized = onDeserialized ?? new List<MethodInfo>();
|
||||
onDeserialized.Add(method);
|
||||
currentOnDeserialized = method;
|
||||
}
|
||||
if (IsValidCallback(method, parameters, typeof(OnErrorAttribute), currentOnError, ref prevAttributeType))
|
||||
{
|
||||
onError = onError ?? new List<MethodInfo>();
|
||||
onError.Add(method);
|
||||
currentOnError = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Type> GetClassHierarchyForType(Type type)
|
||||
{
|
||||
List<Type> ret = new List<Type>();
|
||||
|
||||
Type current = type;
|
||||
while (current != null && current != typeof(object))
|
||||
{
|
||||
ret.Add(current);
|
||||
#if NETFX_CORE
|
||||
current = current.BaseType();
|
||||
#else
|
||||
current = current.BaseType;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return the class list in order of simple => complex
|
||||
ret.Reverse();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="JsonDictionaryContract"/> for the given type.
|
||||
/// </summary>
|
||||
@@ -1108,4 +1149,4 @@ namespace Newtonsoft.Json.Serialization
|
||||
return ResolvePropertyName(propertyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
@@ -89,29 +90,85 @@ namespace Newtonsoft.Json.Serialization
|
||||
// checked for after passed in converters and attribute specified converters
|
||||
internal JsonConverter InternalConverter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets all methods called immediately after deserialization of the object.
|
||||
/// </summary>
|
||||
/// <value>The methods called immediately after deserialization of the object.</value>
|
||||
public List<MethodInfo> OnDeserializedMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets all methods called during deserialization of the object.
|
||||
/// </summary>
|
||||
/// <value>The methods called during deserialization of the object.</value>
|
||||
public List<MethodInfo> OnDeserializingMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets all methods called after serialization of the object graph.
|
||||
/// </summary>
|
||||
/// <value>The methods called after serialization of the object graph.</value>
|
||||
public List<MethodInfo> OnSerializedMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets all methods called before serialization of the object.
|
||||
/// </summary>
|
||||
/// <value>The methods called before serialization of the object.</value>
|
||||
public List<MethodInfo> OnSerializingMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method called immediately after deserialization of the object.
|
||||
/// </summary>
|
||||
/// <value>The method called immediately after deserialization of the object.</value>
|
||||
public MethodInfo OnDeserialized { get; set; }
|
||||
public MethodInfo OnDeserialized
|
||||
{
|
||||
get { return (OnDeserializedMethods != null && OnDeserializedMethods.Count > 0 ? OnDeserializedMethods[0] : null); }
|
||||
set
|
||||
{
|
||||
OnDeserializedMethods = new List<MethodInfo>();
|
||||
OnDeserializedMethods.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method called during deserialization of the object.
|
||||
/// </summary>
|
||||
/// <value>The method called during deserialization of the object.</value>
|
||||
public MethodInfo OnDeserializing { get; set; }
|
||||
public MethodInfo OnDeserializing
|
||||
{
|
||||
get { return (OnDeserializingMethods != null && OnDeserializingMethods.Count > 0 ? OnDeserializingMethods[0] : null); }
|
||||
set
|
||||
{
|
||||
OnDeserializingMethods = new List<MethodInfo>();
|
||||
OnDeserializingMethods.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method called after serialization of the object graph.
|
||||
/// </summary>
|
||||
/// <value>The method called after serialization of the object graph.</value>
|
||||
public MethodInfo OnSerialized { get; set; }
|
||||
public MethodInfo OnSerialized
|
||||
{
|
||||
get { return (OnSerializedMethods != null && OnSerializedMethods.Count > 0 ? OnSerializedMethods[0] : null); }
|
||||
set
|
||||
{
|
||||
OnSerializedMethods = new List<MethodInfo>();
|
||||
OnSerializedMethods.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method called before serialization of the object.
|
||||
/// </summary>
|
||||
/// <value>The method called before serialization of the object.</value>
|
||||
public MethodInfo OnSerializing { get; set; }
|
||||
public MethodInfo OnSerializing
|
||||
{
|
||||
get { return (OnSerializingMethods != null && OnSerializingMethods.Count > 0 ? OnSerializingMethods[0] : null); }
|
||||
set
|
||||
{
|
||||
OnSerializingMethods = new List<MethodInfo>();
|
||||
OnSerializingMethods.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default creator method used to create the object.
|
||||
@@ -125,40 +182,73 @@ namespace Newtonsoft.Json.Serialization
|
||||
/// <value><c>true</c> if the default object creator is non-public; otherwise, <c>false</c>.</value>
|
||||
public bool DefaultCreatorNonPublic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets all method called when an error is thrown during the serialization of the object.
|
||||
/// </summary>
|
||||
/// <value>The methods called when an error is thrown during the serialization of the object.</value>
|
||||
public List<MethodInfo> OnErrorMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method called when an error is thrown during the serialization of the object.
|
||||
/// </summary>
|
||||
/// <value>The method called when an error is thrown during the serialization of the object.</value>
|
||||
public MethodInfo OnError { get; set; }
|
||||
public MethodInfo OnError
|
||||
{
|
||||
get { return (OnErrorMethods != null && OnErrorMethods.Count > 0 ? OnErrorMethods[0] : null); }
|
||||
set
|
||||
{
|
||||
OnErrorMethods = new List<MethodInfo>();
|
||||
OnErrorMethods.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
internal void InvokeOnSerializing(object o, StreamingContext context)
|
||||
{
|
||||
if (OnSerializing != null)
|
||||
OnSerializing.Invoke(o, new object[] {context});
|
||||
if (OnSerializingMethods == null) return;
|
||||
|
||||
foreach (MethodInfo mi in OnSerializingMethods)
|
||||
{
|
||||
mi.Invoke(o, new object[] {context});
|
||||
}
|
||||
}
|
||||
|
||||
internal void InvokeOnSerialized(object o, StreamingContext context)
|
||||
{
|
||||
if (OnSerialized != null)
|
||||
OnSerialized.Invoke(o, new object[] {context});
|
||||
if (OnSerializedMethods == null) return;
|
||||
|
||||
foreach (MethodInfo mi in OnSerializedMethods)
|
||||
{
|
||||
mi.Invoke(o, new object[] {context});
|
||||
}
|
||||
}
|
||||
|
||||
internal void InvokeOnDeserializing(object o, StreamingContext context)
|
||||
{
|
||||
if (OnDeserializing != null)
|
||||
OnDeserializing.Invoke(o, new object[] {context});
|
||||
if (OnDeserializingMethods == null) return;
|
||||
|
||||
foreach (MethodInfo mi in OnDeserializingMethods)
|
||||
{
|
||||
mi.Invoke(o, new object[] {context});
|
||||
}
|
||||
}
|
||||
|
||||
internal void InvokeOnDeserialized(object o, StreamingContext context)
|
||||
{
|
||||
if (OnDeserialized != null)
|
||||
OnDeserialized.Invoke(o, new object[] {context});
|
||||
if (OnDeserializedMethods == null) return;
|
||||
|
||||
foreach (MethodInfo mi in OnDeserializedMethods)
|
||||
{
|
||||
mi.Invoke(o, new object[] {context});
|
||||
}
|
||||
}
|
||||
|
||||
internal void InvokeOnError(object o, StreamingContext context, ErrorContext errorContext)
|
||||
{
|
||||
if (OnError != null)
|
||||
OnError.Invoke(o, new object[] {context, errorContext});
|
||||
if (OnErrorMethods == null) return;
|
||||
foreach (MethodInfo mi in OnErrorMethods)
|
||||
{
|
||||
mi.Invoke(o, new object[] {context, errorContext});
|
||||
}
|
||||
}
|
||||
|
||||
internal JsonContract(Type underlyingType)
|
||||
@@ -206,4 +296,4 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,10 +561,10 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
|
||||
if (id != null && isTemporaryListReference)
|
||||
throw JsonSerializationException.Create(reader, "Cannot preserve reference to array or readonly list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
|
||||
|
||||
if (contract.OnSerializing != null && isTemporaryListReference)
|
||||
if (contract.OnSerializingMethods != null && isTemporaryListReference)
|
||||
throw JsonSerializationException.Create(reader, "Cannot call OnSerializing on an array or readonly list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
|
||||
|
||||
if (contract.OnError != null && isTemporaryListReference)
|
||||
if (contract.OnErrorMethods != null && isTemporaryListReference)
|
||||
throw JsonSerializationException.Create(reader, "Cannot call OnError on an array or readonly list: {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType));
|
||||
|
||||
if (!arrayContract.IsMultidimensionalArray)
|
||||
|
||||
Reference in New Issue
Block a user