-Implemented INotifyCollectionChanged on JContainer in Silverlight

-Added StreamingContext to JsonSerializer
-Added CreateMemberValueProvider method to DefaultContractResolver
-Fixed not falling back the original class from the metadata class when attribute not found
This commit is contained in:
JamesNK
2010-01-19 08:22:35 +00:00
parent f9b34390f5
commit 621916f5a6
15 changed files with 233 additions and 41 deletions
@@ -9,6 +9,7 @@ using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
#if !PocketPC && !SILVERLIGHT
using System.Web.UI;
#endif
@@ -1304,6 +1305,44 @@ Parameter name: arrayIndex")]
Assert.AreEqual(index, 0);
Assert.AreEqual(2, (int)o["Test1"]);
}
#else
[Test]
public void ListChanged()
{
JProperty p1 = new JProperty("Test1", 1);
JProperty p2 = new JProperty("Test2", "Two");
JObject o = new JObject(p1, p2);
NotifyCollectionChangedAction? changedType = null;
int? index = null;
o.CollectionChanged += (s, a) =>
{
changedType = a.Action;
index = a.NewStartingIndex;
};
JProperty p3 = new JProperty("Test3", "III");
o.Add(p3);
Assert.AreEqual(changedType, NotifyCollectionChangedAction.Add);
Assert.AreEqual(index, 2);
Assert.AreEqual(p3, ((IList<JToken>)o)[index.Value]);
JProperty p4 = new JProperty("Test4", "IV");
((IList<JToken>)o)[index.Value] = p4;
Assert.AreEqual(changedType, NotifyCollectionChangedAction.Replace);
Assert.AreEqual(index, 2);
Assert.AreEqual(p4, ((IList<JToken>)o)[index.Value]);
Assert.IsFalse(((IList<JToken>)o).Contains(p3));
Assert.IsTrue(((IList<JToken>)o).Contains(p4));
o["Test1"] = 2;
Assert.AreEqual(changedType, NotifyCollectionChangedAction.Replace);
Assert.AreEqual(index, 0);
Assert.AreEqual(2, (int)o["Test1"]);
}
#endif
[Test]
@@ -7,6 +7,8 @@ using Newtonsoft.Json.Tests.LinqToSql;
using NUnit.Framework;
using System.Reflection;
using System.ComponentModel;
using Newtonsoft.Json.Serialization;
using System.Data.Linq.Mapping;
namespace Newtonsoft.Json.Tests.LinqToSql
{
@@ -97,6 +99,12 @@ namespace Newtonsoft.Json.Tests.LinqToSql
Assert.AreEqual(1, person.PersonRoles[0].Role.PersonRoles.Count);
Assert.AreEqual("Name!", person.Department.Name);
TableAttribute tableAttribute = JsonTypeReflector.GetAttribute<TableAttribute>(typeof(Person));
Assert.AreEqual("", tableAttribute.Name);
ColumnAttribute columnAttribute = JsonTypeReflector.GetAttribute<ColumnAttribute>(typeof(Person).GetProperty("FirstName"));
Assert.AreEqual("_FirstName", columnAttribute.Storage);
}
}
}
}
@@ -28,6 +28,7 @@ using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using Newtonsoft.Json.Tests;
using Newtonsoft.Json.Tests.TestObjects;
@@ -230,6 +231,37 @@ namespace Newtonsoft.Json.Tests.Serialization
Console.WriteLine(obj.Member4);
// This value was set after deserialization.
}
#if !SILVERLIGHT
public class SerializationEventContextTestObject
{
public string TestMember { get; set; }
[OnSerializing]
internal void OnSerializingMethod(StreamingContext context)
{
TestMember = context.State + " " + context.Context;
}
}
[Test]
public void SerializationEventContextTest()
{
SerializationEventContextTestObject value = new SerializationEventContextTestObject();
string json = JsonConvert.SerializeObject(value, Formatting.Indented, new JsonSerializerSettings
{
Context =
new StreamingContext(
StreamingContextStates.Remoting,
"ContextValue")
});
Assert.AreEqual(@"{
""TestMember"": ""Remoting ContextValue""
}", json);
}
#endif
}
}
#endif
+14
View File
@@ -53,6 +53,7 @@ namespace Newtonsoft.Json
private IContractResolver _contractResolver;
private IReferenceResolver _referenceResolver;
private SerializationBinder _binder;
private StreamingContext _context;
/// <summary>
/// Occurs when the <see cref="JsonSerializer"/> errors during serialization and deserialization.
@@ -250,6 +251,16 @@ namespace Newtonsoft.Json
}
set { _contractResolver = value; }
}
/// <summary>
/// Gets or sets the <see cref="StreamingContext"/> used by the serializer when invoking serialization callback methods.
/// </summary>
/// <value>The context.</value>
public virtual StreamingContext Context
{
get { return _context; }
set { _context = value; }
}
#endregion
/// <summary>
@@ -265,6 +276,8 @@ namespace Newtonsoft.Json
_preserveReferencesHandling = JsonSerializerSettings.DefaultPreserveReferencesHandling;
_constructorHandling = JsonSerializerSettings.DefaultConstructorHandling;
_typeNameHandling = JsonSerializerSettings.DefaultTypeNameHandling;
_context = JsonSerializerSettings.DefaultContext;
_binder = DefaultSerializationBinder.Instance;
}
@@ -290,6 +303,7 @@ namespace Newtonsoft.Json
jsonSerializer.NullValueHandling = settings.NullValueHandling;
jsonSerializer.DefaultValueHandling = settings.DefaultValueHandling;
jsonSerializer.ConstructorHandling = settings.ConstructorHandling;
jsonSerializer.Context = settings.Context;
if (settings.Error != null)
jsonSerializer.Error += settings.Error;
@@ -21,6 +21,7 @@ namespace Newtonsoft.Json
internal const PreserveReferencesHandling DefaultPreserveReferencesHandling = PreserveReferencesHandling.None;
internal const ConstructorHandling DefaultConstructorHandling = ConstructorHandling.Default;
internal const TypeNameHandling DefaultTypeNameHandling = TypeNameHandling.None;
internal static readonly StreamingContext DefaultContext = new StreamingContext();
/// <summary>
/// Gets or sets how reference loops (e.g. a class referencing itself) is handled.
@@ -89,7 +90,13 @@ namespace Newtonsoft.Json
/// Gets or sets the error handler called during serialization and deserialization.
/// </summary>
/// <value>The error handler called during serialization and deserialization.</value>
public EventHandler<ErrorEventArgs> Error { get; set;}
public EventHandler<ErrorEventArgs> Error { get; set; }
/// <summary>
/// Gets or sets the <see cref="StreamingContext"/> used by the serializer when invoking serialization callback methods.
/// </summary>
/// <value>The context.</value>
public StreamingContext Context { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="JsonSerializerSettings"/> class.
@@ -103,6 +110,7 @@ namespace Newtonsoft.Json
DefaultValueHandling = DefaultDefaultValueHandling;
PreserveReferencesHandling = DefaultPreserveReferencesHandling;
TypeNameHandling = DefaultTypeNameHandling;
Context = DefaultContext;
Converters = new List<JsonConverter>();
}
}
+43 -2
View File
@@ -32,6 +32,7 @@ using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.ComponentModel;
using System.Collections.Specialized;
#if !SILVERLIGHT
using Newtonsoft.Json.Linq.ComponentModel;
#endif
@@ -45,7 +46,7 @@ namespace Newtonsoft.Json.Linq
#if !SILVERLIGHT
, ITypedList, IBindingList
#else
, IList
, IList, INotifyCollectionChanged
#endif
{
#if !SILVERLIGHT
@@ -58,6 +59,11 @@ namespace Newtonsoft.Json.Linq
/// Occurs before an item is added to the collection.
/// </summary>
public event AddingNewEventHandler AddingNew;
#else
/// <summary>
/// Occurs when the items list of the collection has changed, or the collection is reset.
/// </summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endif
private JToken _content;
@@ -93,7 +99,7 @@ namespace Newtonsoft.Json.Linq
internal void CheckReentrancy()
{
if (_busy)
throw new InvalidOperationException("ObservableCollection_CannotChangeObservableCollection");
throw new InvalidOperationException("Cannot change {0} during a collection change event.".FormatWith(CultureInfo.InvariantCulture, GetType()));
}
#if !SILVERLIGHT
@@ -116,6 +122,28 @@ namespace Newtonsoft.Json.Linq
{
ListChangedEventHandler handler = ListChanged;
if (handler != null)
{
_busy = true;
try
{
handler(this, e);
}
finally
{
_busy = false;
}
}
}
#else
/// <summary>
/// Raises the <see cref="CollectionChanged"/> event.
/// </summary>
/// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
NotifyCollectionChangedEventHandler handler = CollectionChanged;
if (handler != null)
{
_busy = true;
@@ -281,6 +309,9 @@ namespace Newtonsoft.Json.Linq
#if !SILVERLIGHT
if (ListChanged != null)
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, IndexOfItem(item)));
#else
if (CollectionChanged != null)
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, IndexOfItem(item)));
#endif
}
@@ -370,6 +401,8 @@ namespace Newtonsoft.Json.Linq
#if !SILVERLIGHT
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index));
#else
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, token, index));
#endif
return;
@@ -414,6 +447,8 @@ namespace Newtonsoft.Json.Linq
#if !SILVERLIGHT
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, itemIndex));
#else
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, itemIndex));
#endif
return true;
@@ -433,6 +468,8 @@ namespace Newtonsoft.Json.Linq
#if !SILVERLIGHT
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index));
#else
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, token, index));
#endif
}
@@ -459,6 +496,8 @@ namespace Newtonsoft.Json.Linq
#if !SILVERLIGHT
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
#else
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
#endif
}
@@ -508,6 +547,8 @@ namespace Newtonsoft.Json.Linq
#if !SILVERLIGHT
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, itemIndex));
#else
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, replacement, existing, itemIndex));
#endif
}
+4
View File
@@ -25,6 +25,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.IO;
@@ -51,6 +52,7 @@ namespace Newtonsoft.Json.Linq
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#if !PocketPC && !SILVERLIGHT && !NET20
/// <summary>
/// Occurs when a property value is changing.
@@ -120,6 +122,8 @@ namespace Newtonsoft.Json.Linq
OnPropertyChanged(childProperty.Name);
#if !SILVERLIGHT
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, IndexOfItem(childProperty)));
#else
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, childProperty, childProperty, IndexOfItem(childProperty)));
#endif
}
@@ -409,6 +409,20 @@ namespace Newtonsoft.Json.Serialization
return properties;
}
/// <summary>
/// Creates the <see cref="IValueProvider"/> used by the serializer to get and set values from a member.
/// </summary>
/// <param name="member">The member.</param>
/// <returns>The <see cref="IValueProvider"/> used by the serializer to get and set values from a member.</returns>
protected virtual IValueProvider CreateMemberValueProvider(MemberInfo member)
{
#if !PocketPC && !SILVERLIGHT
return new DynamicValueProvider(member);
#else
return new ReflectionValueProvider(member);
#endif
}
/// <summary>
/// Creates a <see cref="JsonProperty"/> for the given <see cref="MemberInfo"/>.
/// </summary>
@@ -419,11 +433,7 @@ namespace Newtonsoft.Json.Serialization
{
JsonProperty property = new JsonProperty();
property.PropertyType = ReflectionUtils.GetMemberUnderlyingType(member);
#if !PocketPC && !SILVERLIGHT
property.ValueProvider = new DynamicValueProvider(member);
#else
property.ValueProvider = new ReflectionValueProvider(member);
#endif
property.ValueProvider = CreateMemberValueProvider(member);
// resolve converter for property
// the class type might have a converter but the property converter takes presidence
@@ -1,4 +1,29 @@
using System;
#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Runtime.Serialization;
using System.Reflection;
using System.Globalization;
@@ -59,9 +59,6 @@ namespace Newtonsoft.Json.Serialization
/// <value>The converter.</value>
public JsonConverter Converter { get; set; }
private static readonly StreamingContext SerializationStreamingContextParameter = new StreamingContext();
private static readonly object[] SerializationEventParameterValues = new object[] { SerializationStreamingContextParameter };
#if !PocketPC
/// <summary>
/// Gets or sets the method called immediately after deserialization of the object.
@@ -103,42 +100,42 @@ namespace Newtonsoft.Json.Serialization
/// <value>The method called when an error is thrown during the serialization of the object.</value>
public MethodInfo OnError { get; set; }
internal void InvokeOnSerializing(object o)
internal void InvokeOnSerializing(object o, StreamingContext context)
{
#if !PocketPC
if (OnSerializing != null)
OnSerializing.Invoke(o, SerializationEventParameterValues);
OnSerializing.Invoke(o, new object[] { context });
#endif
}
internal void InvokeOnSerialized(object o)
internal void InvokeOnSerialized(object o, StreamingContext context)
{
#if !PocketPC
if (OnSerialized != null)
OnSerialized.Invoke(o, SerializationEventParameterValues);
OnSerialized.Invoke(o, new object[] { context });
#endif
}
internal void InvokeOnDeserializing(object o)
internal void InvokeOnDeserializing(object o, StreamingContext context)
{
#if !PocketPC
if (OnDeserializing != null)
OnDeserializing.Invoke(o, SerializationEventParameterValues);
OnDeserializing.Invoke(o, new object[] { context });
#endif
}
internal void InvokeOnDeserialized(object o)
internal void InvokeOnDeserialized(object o, StreamingContext context)
{
#if !PocketPC
if (OnDeserialized != null)
OnDeserialized.Invoke(o, SerializationEventParameterValues);
OnDeserialized.Invoke(o, new object[] { context });
#endif
}
internal void InvokeOnError(object o, ErrorContext errorContext)
internal void InvokeOnError(object o, StreamingContext context, ErrorContext errorContext)
{
if (OnError != null)
OnError.Invoke(o, new object[] { SerializationStreamingContextParameter, errorContext });
OnError.Invoke(o, new object[] { context, errorContext });
}
internal JsonContract(Type underlyingType)
@@ -64,7 +64,7 @@ namespace Newtonsoft.Json.Serialization
protected bool IsErrorHandled(object currentObject, JsonContract contract, object keyValue, Exception ex)
{
ErrorContext errorContext = GetErrorContext(currentObject, keyValue, ex);
contract.InvokeOnError(currentObject, errorContext);
contract.InvokeOnError(currentObject, Serializer.Context, errorContext);
if (!errorContext.Handled)
Serializer.OnError(new ErrorEventArgs(currentObject, errorContext));
@@ -485,7 +485,7 @@ namespace Newtonsoft.Json.Serialization
if (id != null)
Serializer.ReferenceResolver.AddReference(id, dictionary.UnderlyingDictionary);
contract.InvokeOnDeserializing(dictionary.UnderlyingDictionary);
contract.InvokeOnDeserializing(dictionary.UnderlyingDictionary, Serializer.Context);
int initialDepth = reader.Depth;
@@ -510,7 +510,7 @@ namespace Newtonsoft.Json.Serialization
}
break;
case JsonToken.EndObject:
contract.InvokeOnDeserialized(dictionary.UnderlyingDictionary);
contract.InvokeOnDeserialized(dictionary.UnderlyingDictionary, Serializer.Context);
return dictionary.UnderlyingDictionary;
default:
@@ -546,7 +546,7 @@ namespace Newtonsoft.Json.Serialization
if (reference != null)
Serializer.ReferenceResolver.AddReference(reference, list);
contract.InvokeOnDeserializing(list);
contract.InvokeOnDeserializing(list, Serializer.Context);
int initialDepth = reader.Depth;
@@ -555,7 +555,7 @@ namespace Newtonsoft.Json.Serialization
switch (reader.TokenType)
{
case JsonToken.EndArray:
contract.InvokeOnDeserialized(list);
contract.InvokeOnDeserialized(list, Serializer.Context);
return wrappedList.UnderlyingCollection;
case JsonToken.Comment:
@@ -667,7 +667,7 @@ namespace Newtonsoft.Json.Serialization
if (id != null)
Serializer.ReferenceResolver.AddReference(id, createdObject);
contract.InvokeOnDeserializing(createdObject);
contract.InvokeOnDeserializing(createdObject, Serializer.Context);
// go through unused values and set the newly created object's properties
foreach (KeyValuePair<JsonProperty, object> remainingPropertyValue in remainingPropertyValues)
@@ -679,13 +679,13 @@ namespace Newtonsoft.Json.Serialization
property.ValueProvider.SetValue(createdObject, value);
}
contract.InvokeOnDeserialized(createdObject);
contract.InvokeOnDeserialized(createdObject, Serializer.Context);
return createdObject;
}
private object PopulateObject(object newObject, JsonReader reader, JsonObjectContract contract, string id)
{
contract.InvokeOnDeserializing(newObject);
contract.InvokeOnDeserializing(newObject, Serializer.Context);
Dictionary<JsonProperty, RequiredValue> requiredProperties =
contract.Properties.Where(m => m.Required != Required.Default).ToDictionary(m => m, m => RequiredValue.None);
@@ -749,7 +749,7 @@ namespace Newtonsoft.Json.Serialization
throw new JsonSerializationException("Required property '{0}' expects a value but got null.".FormatWith(CultureInfo.InvariantCulture, requiredProperty.Key.PropertyName));
}
contract.InvokeOnDeserialized(newObject);
contract.InvokeOnDeserialized(newObject, Serializer.Context);
return newObject;
case JsonToken.Comment:
// ignore
@@ -259,13 +259,13 @@ namespace Newtonsoft.Json.Serialization
private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract)
{
contract.InvokeOnSerializing(value);
contract.InvokeOnSerializing(value, Serializer.Context);
string s;
if (TryConvertToString(value, contract.UnderlyingType, out s))
{
writer.WriteValue(s);
contract.InvokeOnSerialized(value);
contract.InvokeOnSerialized(value, Serializer.Context);
return;
}
@@ -310,7 +310,7 @@ namespace Newtonsoft.Json.Serialization
writer.WriteEndObject();
SerializeStack.RemoveAt(SerializeStack.Count - 1);
contract.InvokeOnSerialized(value);
contract.InvokeOnSerialized(value, Serializer.Context);
}
private void WriteTypeProperty(JsonWriter writer, Type type)
@@ -355,7 +355,7 @@ namespace Newtonsoft.Json.Serialization
private void SerializeList(JsonWriter writer, IList values, JsonArrayContract contract)
{
contract.InvokeOnSerializing(values);
contract.InvokeOnSerializing(values, Serializer.Context);
SerializeStack.Add(values);
@@ -419,12 +419,12 @@ namespace Newtonsoft.Json.Serialization
SerializeStack.RemoveAt(SerializeStack.Count - 1);
contract.InvokeOnSerialized(values);
contract.InvokeOnSerialized(values, Serializer.Context);
}
private void SerializeDictionary(JsonWriter writer, IDictionary values, JsonDictionaryContract contract)
{
contract.InvokeOnSerializing(values);
contract.InvokeOnSerializing(values, Serializer.Context);
SerializeStack.Add(values);
writer.WriteStartObject();
@@ -478,7 +478,7 @@ namespace Newtonsoft.Json.Serialization
writer.WriteEndObject();
SerializeStack.RemoveAt(SerializeStack.Count - 1);
contract.InvokeOnSerialized(values);
contract.InvokeOnSerialized(values, Serializer.Context);
}
private string GetPropertyName(DictionaryEntry entry)
@@ -112,6 +112,12 @@ namespace Newtonsoft.Json.Serialization
set { _serializer.Binder = value; }
}
public override StreamingContext Context
{
get { return _serializer.Context; }
set { _serializer.Context = value; }
}
public JsonSerializerProxy(JsonSerializerInternalReader serializerReader)
{
ValidationUtils.ArgumentNotNull(serializerReader, "serializerReader");
@@ -197,7 +197,11 @@ namespace Newtonsoft.Json.Serialization
{
Type metadataType = GetAssociatedMetadataType(type);
if (metadataType != null)
return ReflectionUtils.GetAttribute<T>(metadataType, true);
{
T attribute = ReflectionUtils.GetAttribute<T>(metadataType, true);
if (attribute != null)
return attribute;
}
return ReflectionUtils.GetAttribute<T>(type, true);
}
@@ -212,7 +216,11 @@ namespace Newtonsoft.Json.Serialization
BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance).SingleOrDefault();
if (metadataTypeMemberInfo != null)
return ReflectionUtils.GetAttribute<T>(metadataTypeMemberInfo, true);
{
T attribute = ReflectionUtils.GetAttribute<T>(metadataTypeMemberInfo, true);
if (attribute != null)
return attribute;
}
}
return ReflectionUtils.GetAttribute<T>(memberInfo, true);