From 8f01fa44f512b2611c4913680265721f9d809e18 Mon Sep 17 00:00:00 2001 From: JamesNK Date: Thu, 28 Jul 2011 00:53:50 +0000 Subject: [PATCH] -Added support for BindToName on SerializationBinder --- .../Newtonsoft.Json.Tests.Silverlight.csproj | 3 + .../Newtonsoft.Json.Tests.WindowsPhone.csproj | 4 + .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/DynamicTests.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 93 +++++++++++++++++++ .../Serialization/TypeNameHandlingTests.cs | 84 ++++++++++++++++- .../Properties/AssemblyInfo.cs | 2 +- .../DefaultSerializationBinder.cs | 13 +++ .../JsonSerializerInternalWriter.cs | 2 +- Src/Newtonsoft.Json/SerializationBinder.cs | 6 ++ .../Utilities/ReflectionUtils.cs | 37 +++++--- 11 files changed, 230 insertions(+), 18 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index d47d3f66..3726e8db 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -125,10 +125,13 @@ + + + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj index f2e59550..30152672 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj @@ -88,9 +88,13 @@ + + + + diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 800165e5..3e6d3276 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -41,5 +41,5 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.2.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.2.13904")] +[assembly: AssemblyFileVersion("4.0.2.13928")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs index f57f1536..501a3a87 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs @@ -1,4 +1,4 @@ -#if !(NET35 || NET20) +#if !(NET35 || NET20 || WINDOWS_PHONE) using System; using System.Collections; using System.Collections.Generic; diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 83b6e44e..f24f3ff2 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -4511,5 +4511,98 @@ keyword such as type of business."" Assert.AreEqual(1, c2.Categories["one"]); Assert.AreEqual(2, c2.Categories["two"]); } + + [JsonObject(MemberSerialization.OptIn)] + public class ClassAttributeBase + { + [JsonProperty] + public string BaseClassValue { get; set; } + } + + public class ClassAttributeDerived : ClassAttributeBase + { + [JsonProperty] + public string DerivedClassValue { get; set; } + + public string NonSerialized { get; set; } + } + + public class CollectionClassAttributeDerived : ClassAttributeBase, ICollection + { + [JsonProperty] + public string CollectionDerivedClassValue { get; set; } + + public void Add(object item) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(object item) + { + throw new NotImplementedException(); + } + + public void CopyTo(object[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public int Count + { + get { throw new NotImplementedException(); } + } + + public bool IsReadOnly + { + get { throw new NotImplementedException(); } + } + + public bool Remove(object item) + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + [Test] + public void ClassAttributesInheritance() + { + string json = JsonConvert.SerializeObject(new ClassAttributeDerived + { + BaseClassValue = "BaseClassValue!", + DerivedClassValue = "DerivedClassValue!", + NonSerialized = "NonSerialized!" + }, Formatting.Indented); + + Assert.AreEqual(@"{ + ""DerivedClassValue"": ""DerivedClassValue!"", + ""BaseClassValue"": ""BaseClassValue!"" +}", json); + + json = JsonConvert.SerializeObject(new CollectionClassAttributeDerived + { + BaseClassValue = "BaseClassValue!", + CollectionDerivedClassValue = "CollectionDerivedClassValue!" + }, Formatting.Indented); + + Assert.AreEqual(@"{ + ""CollectionDerivedClassValue"": ""CollectionDerivedClassValue!"", + ""BaseClassValue"": ""BaseClassValue!"" +}", json); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs index 198645c7..dc71f3fc 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -430,7 +430,6 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(5, nested[4]); } -#if !SILVERLIGHT && !PocketPC [Test] public void DeserializeUsingCustomBinder() { @@ -460,6 +459,89 @@ namespace Newtonsoft.Json.Tests.Serialization return typeof (Person); } } + +#if !(NET20 || NET35) + [Test] + public void SerializeUsingCustomBinder() + { + IList values = new List + { + new Person + { + BirthDate = new DateTime(2000, 10, 23, 1, 1, 1, DateTimeKind.Utc), + LastModified = new DateTime(2000, 10, 23, 1, 1, 1, DateTimeKind.Utc), + Name = "Name!", + Department = "Department!" + }, + new Employee + { + BirthDate = new DateTime(2000, 10, 23, 1, 1, 1, DateTimeKind.Utc), + FirstName = "FirstName!", + LastName = "LastName!", + Department = "Department!", + JobTitle = "JobTitle!" + } + }; + + string json = JsonConvert.SerializeObject(values, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto, + Binder = new CompleteSerializationBinder() + }); + + Assert.AreEqual(@"[ + { + ""$type"": ""Person"", + ""Name"": ""Name!"", + ""BirthDate"": ""\/Date(972262861000)\/"", + ""LastModified"": ""\/Date(972262861000)\/"" + }, + { + ""$type"": ""Employee"", + ""FirstName"": ""FirstName!"", + ""LastName"": ""LastName!"", + ""BirthDate"": ""\/Date(972262861000)\/"", + ""Department"": ""Department!"", + ""JobTitle"": ""JobTitle!"" + } +]", json); + + IList newValues = JsonConvert.DeserializeObject>(json, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto, + Binder = new CompleteSerializationBinder() + }); + + Assert.IsInstanceOfType(typeof(Person), newValues[0]); + Person person = (Person)newValues[0]; + Assert.AreEqual("Name!", person.Name); + + Assert.IsInstanceOfType(typeof(Employee), newValues[1]); + Employee employee = (Employee)newValues[1]; + Assert.AreEqual("FirstName!", employee.FirstName); + } + + public class CompleteSerializationBinder : SerializationBinder + { + public override void BindToName(Type serializedType, out string assemblyName, out string typeName) + { + assemblyName = null; + typeName = serializedType.Name; + } + + public override Type BindToType(string assemblyName, string typeName) + { + switch (typeName) + { + case "Employee": + return typeof (Employee); + case "Person": + return typeof (Person); + default: + throw new ArgumentException(); + } + } + } #endif [Test] diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index ced2ade6..3ea5defe 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -110,7 +110,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.2.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.2.13904")] +[assembly: AssemblyFileVersion("4.0.2.13928")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs b/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs index a1b7ddf2..798b9db8 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs @@ -116,5 +116,18 @@ namespace Newtonsoft.Json.Serialization { return _typeCache.Get(new TypeNameKey(assemblyName, typeName)); } + +#if !(NET35 || NET20) + public override void BindToName(Type serializedType, out string assemblyName, out string typeName) + { +#if !SILVERLIGHT + assemblyName = serializedType.Assembly.FullName; + typeName = serializedType.FullName; +#else + assemblyName = null; + typeName = serializedType.AssemblyQualifiedName; +#endif + } +#endif } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index fa3d0a33..d70a4434 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -357,7 +357,7 @@ namespace Newtonsoft.Json.Serialization private void WriteTypeProperty(JsonWriter writer, Type type) { writer.WritePropertyName(JsonTypeReflector.TypePropertyName); - writer.WriteValue(ReflectionUtils.GetTypeName(type, Serializer.TypeNameAssemblyFormat)); + writer.WriteValue(ReflectionUtils.GetTypeName(type, Serializer.TypeNameAssemblyFormat, Serializer.Binder)); } private bool HasFlag(PreserveReferencesHandling value, PreserveReferencesHandling flag) diff --git a/Src/Newtonsoft.Json/SerializationBinder.cs b/Src/Newtonsoft.Json/SerializationBinder.cs index 9f55569a..0d5fffa2 100644 --- a/Src/Newtonsoft.Json/SerializationBinder.cs +++ b/Src/Newtonsoft.Json/SerializationBinder.cs @@ -16,6 +16,12 @@ namespace Newtonsoft.Json /// Specifies the name of the serialized object /// public abstract Type BindToType(string assemblyName, string typeName); + + public virtual void BindToName(Type serializedType, out string assemblyName, out string typeName) + { + assemblyName = null; + typeName = null; + } } } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index a125e043..bf7a0b6a 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -29,6 +29,7 @@ using System.Reflection; using System.Collections; using System.Linq; using System.Globalization; +using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters; using System.Text; using System.Text.RegularExpressions; @@ -59,10 +60,31 @@ namespace Newtonsoft.Json.Utilities public static string GetTypeName(Type t, FormatterAssemblyStyle assemblyFormat) { + return GetTypeName(t, assemblyFormat, null); + } + + public static string GetTypeName(Type t, FormatterAssemblyStyle assemblyFormat, SerializationBinder binder) + { + string fullyQualifiedTypeName; +#if !(NET20 || NET35) + if (binder != null) + { + string assemblyName, typeName; + binder.BindToName(t, out assemblyName, out typeName); + fullyQualifiedTypeName = typeName + (assemblyName == null ? "" : ", " + assemblyName); + } + else + { + fullyQualifiedTypeName = t.AssemblyQualifiedName; + } +#else + fullyQualifiedTypeName = t.AssemblyQualifiedName; +#endif + switch (assemblyFormat) { case FormatterAssemblyStyle.Simple: - return GetSimpleTypeName(t); + return RemoveAssemblyDetails(fullyQualifiedTypeName); case FormatterAssemblyStyle.Full: return t.AssemblyQualifiedName; default: @@ -70,19 +92,8 @@ namespace Newtonsoft.Json.Utilities } } - private static string GetSimpleTypeName(Type type) + private static string RemoveAssemblyDetails(string fullyQualifiedTypeName) { -#if !SILVERLIGHT - string fullyQualifiedTypeName = type.FullName + ", " + type.Assembly.GetName().Name; - - // for type names with no nested type names then return - if (!type.IsGenericType || type.IsGenericTypeDefinition) - return fullyQualifiedTypeName; -#else - // Assembly.GetName() is marked SecurityCritical - string fullyQualifiedTypeName = type.AssemblyQualifiedName; -#endif - StringBuilder builder = new StringBuilder(); // loop through the type name and filter out qualified assembly details from nested type names