diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index c54b20fd..953cdc90 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -24,7 +24,9 @@ namespace Newtonsoft.Json.Tests [DataMember] public string FileName { get; set; } [DataMember] - public string Title { get; set; } + public string Author { get; set; } + [DataMember] + public string Caption { get; set; } [DataMember] public byte[] Data { get; set; } } @@ -94,44 +96,97 @@ namespace Newtonsoft.Json.Tests Image image = new Image(); image.Data = System.IO.File.ReadAllBytes(@"..\..\bunny_pancake.jpg"); image.FileName = "bunny_pancake.jpg"; - image.Title = "I have no idea what you are talking about so here's a bunny with a pancake on its head"; + image.Author = "Hironori Akutagawa"; + image.Caption = "I have no idea what you are talking about so here's a bunny with a pancake on its head"; SerializeSize(image); } + private T TimeOperation(Func operation, string name) + { + // warm up + operation(); + + Stopwatch timed = new Stopwatch(); + timed.Start(); + + T result = operation(); + + Console.WriteLine(name); + Console.WriteLine("{0} ms", timed.ElapsedMilliseconds); + + timed.Stop(); + + return result; + } + private void SerializeSize(object value) { - string json = JsonConvert.SerializeObject(value, Formatting.None); - byte[] jsonBytes = Encoding.UTF8.GetBytes(json); + byte[] jsonBytes = TimeOperation(() => + { + string json = null; + for (int i = 0; i < Iterations; i++) + { + json = JsonConvert.SerializeObject(value, Formatting.None); + } - MemoryStream ms = new MemoryStream(); - JsonSerializer serializer = new JsonSerializer(); - BsonWriter writer = new BsonWriter(ms); + return Encoding.UTF8.GetBytes(json); + }, "Json.NET"); - serializer.Serialize(writer, value); - writer.Flush(); + byte[] bsonBytes = TimeOperation(() => + { + MemoryStream ms = null; + for (int i = 0; i < Iterations; i++) + { + ms = new MemoryStream(); + JsonSerializer serializer = new JsonSerializer(); + BsonWriter writer = new BsonWriter(ms); - byte[] bsonBytes = ms.ToArray(); + serializer.Serialize(writer, value); + writer.Flush(); + } - DataContractSerializer dataContractSerializer = new DataContractSerializer(value.GetType()); + return ms.ToArray(); + }, "Json.NET BSON"); - ms = new MemoryStream(); - dataContractSerializer.WriteObject(ms, value); + byte[] xmlBytes = TimeOperation(() => + { + MemoryStream ms = null; + for (int i = 0; i < Iterations; i++) + { + ms = new MemoryStream(); + DataContractSerializer dataContractSerializer = new DataContractSerializer(value.GetType()); + dataContractSerializer.WriteObject(ms, value); + } - byte[] xmlBytes = ms.ToArray(); + return ms.ToArray(); + }, "DataContractSerializer"); - DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(value.GetType()); + byte[] wcfJsonBytes = TimeOperation(() => + { + MemoryStream ms = null; + for (int i = 0; i < Iterations; i++) + { + ms = new MemoryStream(); + DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(value.GetType()); + dataContractJsonSerializer.WriteObject(ms, value); + } - ms = new MemoryStream(); - dataContractJsonSerializer.WriteObject(ms, value); + return ms.ToArray(); + }, "DataContractJsonSerializer"); - byte[] wcfJsonBytes = ms.ToArray(); + byte[] binaryFormatterBytes = TimeOperation(() => + { + MemoryStream ms = null; + for (int i = 0; i < Iterations; i++) + { + ms = new MemoryStream(); + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(ms, value); + } - ms = new MemoryStream(); - BinaryFormatter formatter = new BinaryFormatter(); - formatter.Serialize(ms, value); - - byte[] binaryFormatterBytes = ms.ToArray(); + return ms.ToArray(); + }, "BinaryFormatter"); Console.WriteLine("Json.NET size: {0} bytes", jsonBytes.Length); Console.WriteLine("BSON size: {0} bytes", bsonBytes.Length); diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index c2da8bbd..771bdc56 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -47,6 +47,7 @@ using Newtonsoft.Json.Tests.TestObjects; using System.Runtime.Serialization; using System.Globalization; using Newtonsoft.Json.Utilities; +using System.Reflection; namespace Newtonsoft.Json.Tests.Serialization { @@ -417,7 +418,7 @@ keyword such as type of business."" public void BackslashEqivilence() { string json = @"[""vvv\/vvv\tvvv\""vvv\bvvv\nvvv\rvvv\\vvv\fvvv""]"; - + #if !SILVERLIGHT JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); List javaScriptSerializerResult = javaScriptSerializer.Deserialize>(json); @@ -431,7 +432,7 @@ keyword such as type of business."" Assert.AreEqual(1, jsonNetResult.Count); Assert.AreEqual(dataContractResult[0], jsonNetResult[0]); #if !SILVERLIGHT - Assert.AreEqual(javaScriptSerializerResult[0], jsonNetResult[0]); + Assert.AreEqual(javaScriptSerializerResult[0], jsonNetResult[0]); #endif } @@ -2004,7 +2005,7 @@ keyword such as type of business."" ""Newtonsoft.Json.Tests.TestObjects.Person"": 1, ""Newtonsoft.Json.Tests.TestObjects.Person"": 2 }"; - + JsonConvert.DeserializeObject>(json); } @@ -2093,7 +2094,7 @@ keyword such as type of business."" public void DeserializeBaseReferenceWithDerivedValue() { PersonPropertyClass personPropertyClass = new PersonPropertyClass(); - WagePerson wagePerson = (WagePerson) personPropertyClass.Person; + WagePerson wagePerson = (WagePerson)personPropertyClass.Person; wagePerson.BirthDate = new DateTime(2000, 11, 29, 23, 59, 59, DateTimeKind.Utc); wagePerson.Department = "McDees"; @@ -2114,7 +2115,7 @@ keyword such as type of business."" json); PersonPropertyClass newPersonPropertyClass = JsonConvert.DeserializeObject(json); - Assert.AreEqual(wagePerson.HourlyWage, ((WagePerson) newPersonPropertyClass.Person).HourlyWage); + Assert.AreEqual(wagePerson.HourlyWage, ((WagePerson)newPersonPropertyClass.Person).HourlyWage); } public class ExistingValueClass @@ -2328,13 +2329,13 @@ keyword such as type of business."" { var json = "{ Key: 'abc', Value: 123 }"; var item = JsonConvert.DeserializeObject(json); - + Assert.AreEqual(123, item.Value); } public abstract class Animal { - public abstract string Name { get; } + public abstract string Name { get; } } public class Human : Animal @@ -2364,7 +2365,7 @@ keyword such as type of business."" DataContractJsonSerializerTestClass c = new DataContractJsonSerializerTestClass(); c.TimeSpanProperty = new TimeSpan(200, 20, 59, 30, 900); c.GuidProperty = new Guid("66143115-BE2A-4a59-AF0A-348E1EA15B1E"); - c.AnimalProperty = new Human() {Ethnicity = "European"}; + c.AnimalProperty = new Human() { Ethnicity = "European" }; c.ExceptionProperty = ex; MemoryStream ms = new MemoryStream(); @@ -2381,7 +2382,7 @@ keyword such as type of business."" Console.WriteLine(JsonConvert.SerializeObject(c, Formatting.Indented, new JsonSerializerSettings { - // TypeNameHandling = TypeNameHandling.Objects + // TypeNameHandling = TypeNameHandling.Objects })); } #endif @@ -2670,5 +2671,181 @@ keyword such as type of business."" Assert.AreEqual(null, o2._nullableInt); } #endif + + public class KVPair + { + public TKey Key { get; set; } + public TValue Value { get; set; } + + public KVPair(TKey k, TValue v) + { + Key = k; + Value = v; + } + } + + [Test] + public void DeserializeUsingNonDefaultConstructorWithLeftOverValues() + { + List> kvPairs = + JsonConvert.DeserializeObject>>( + "[{\"Key\":\"Two\",\"Value\":\"2\"},{\"Key\":\"One\",\"Value\":\"1\"}]"); + + Assert.AreEqual(2, kvPairs.Count); + Assert.AreEqual("Two", kvPairs[0].Key); + Assert.AreEqual("2", kvPairs[0].Value); + Assert.AreEqual("One", kvPairs[1].Key); + Assert.AreEqual("1", kvPairs[1].Value); + } + + [Test] + public void SerializeClassWithInheritedProtectedMember() + { + AA myA = new AA(2); + string json = JsonConvert.SerializeObject(myA, Formatting.Indented); + Assert.AreEqual(@"{ + ""AA_field1"": 2, + ""AA_property1"": 2, + ""AA_property2"": 2, + ""AA_property3"": 2, + ""AA_property4"": 2 +}", json); + + BB myB = new BB(3, 4); + json = JsonConvert.SerializeObject(myB, Formatting.Indented); + Assert.AreEqual(@"{ + ""BB_field1"": 4, + ""AA_field1"": 3, + ""BB_property1"": 4, + ""BB_property2"": 4, + ""BB_property3"": 4, + ""BB_property4"": 4, + ""BB_property5"": 4, + ""AA_property1"": 3, + ""AA_property2"": 3, + ""AA_property3"": 3, + ""AA_property4"": 3 +}", json); + } + + [Test] + public void DeserializeClassWithInheritedProtectedMember() + { + AA myA = JsonConvert.DeserializeObject( + @"{ + ""AA_field1"": 2, + ""AA_property1"": 2, + ""AA_property2"": 2, + ""AA_property3"": 2, + ""AA_property4"": 2, + ""AA_property5"": 2, + ""AA_property6"": 2 +}"); + + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetField("AA_field1", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property1", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property2", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property3", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property4", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property5", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property6", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + + BB myB = JsonConvert.DeserializeObject( + @"{ + ""BB_field1"": 4, + ""AA_field1"": 3, + ""AA_property1"": 2, + ""AA_property2"": 2, + ""AA_property3"": 2, + ""AA_property4"": 2, + ""AA_property5"": 2, + ""AA_property6"": 2, + ""BB_property1"": 3, + ""BB_property2"": 3, + ""BB_property3"": 3, + ""BB_property4"": 3, + ""BB_property5"": 3, + ""BB_property6"": 3 +}"); + + Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(AA).GetField("AA_field1", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property1", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property2", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property3", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property4", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property5", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property6", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + + Assert.AreEqual(4, myB.BB_field1); + Assert.AreEqual(3, myB.BB_property1); + Assert.AreEqual(3, myB.BB_property2); + Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property3", BindingFlags.Instance | BindingFlags.Public), myB)); + Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property4", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(0, myB.BB_property5); + Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property6", BindingFlags.Instance | BindingFlags.Public), myB)); + } + + public class AA + { + [JsonProperty] + protected int AA_field1; + [JsonProperty] + protected int AA_property1 { get; set; } + [JsonProperty] + protected int AA_property2 { get; private set; } + [JsonProperty] + protected int AA_property3 { private get; set; } + [JsonProperty] + private int AA_property4 { get; set; } + protected int AA_property5 { get; private set; } + protected int AA_property6 { private get; set; } + + public AA() + { + } + + public AA(int f) + { + AA_field1 = f; + AA_property1 = f; + AA_property2 = f; + AA_property3 = f; + AA_property4 = f; + AA_property5 = f; + AA_property6 = f; + } + } + + public class BB : AA + { + [JsonProperty] + public int BB_field1; + [JsonProperty] + public int BB_property1 { get; set; } + [JsonProperty] + public int BB_property2 { get; private set; } + [JsonProperty] + public int BB_property3 { private get; set; } + [JsonProperty] + private int BB_property4 { get; set; } + public int BB_property5 { get; private set; } + public int BB_property6 { private get; set; } + + public BB() + { + } + + public BB(int f, int g) + : base(f) + { + BB_field1 = g; + BB_property1 = g; + BB_property2 = g; + BB_property3 = g; + BB_property4 = g; + BB_property5 = g; + BB_property6 = g; + } + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 62d10f41..860e9fad 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -553,8 +553,18 @@ namespace Newtonsoft.Json.Serialization #endif )); - property.Readable = ReflectionUtils.CanReadMemberValue(member); - property.Writable = ReflectionUtils.CanSetMemberValue(member); + bool allowNonPublicAccess = false; + if ((DefaultMembersSearchFlags & BindingFlags.NonPublic) == BindingFlags.NonPublic) + allowNonPublicAccess = true; + if (propertyAttribute != null) + allowNonPublicAccess = true; +#if !PocketPC && !NET20 + if (dataMemberAttribute != null) + allowNonPublicAccess = true; +#endif + + property.Readable = ReflectionUtils.CanReadMemberValue(member, allowNonPublicAccess); + property.Writable = ReflectionUtils.CanSetMemberValue(member, allowNonPublicAccess); property.MemberConverter = JsonTypeReflector.GetJsonConverter(member, ReflectionUtils.GetMemberUnderlyingType(member)); diff --git a/Src/Newtonsoft.Json/Serialization/JsonContract.cs b/Src/Newtonsoft.Json/Serialization/JsonContract.cs index 634d3ca2..857e5352 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonContract.cs @@ -87,9 +87,9 @@ namespace Newtonsoft.Json.Serialization #endif /// - /// Gets or sets the default creator. + /// Gets or sets the default creator method used to create the object. /// - /// The default creator. + /// The default creator method used to create the object. public Func DefaultCreator { get; set; } /// diff --git a/Src/Newtonsoft.Json/Serialization/JsonProperty.cs b/Src/Newtonsoft.Json/Serialization/JsonProperty.cs index 6604c644..c1518985 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonProperty.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonProperty.cs @@ -124,5 +124,16 @@ namespace Newtonsoft.Json.Serialization /// /// The object creation handling. public ObjectCreationHandling? ObjectCreationHandling { get; set; } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return PropertyName; + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index d5e832a3..71320153 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -655,7 +655,7 @@ namespace Newtonsoft.Json.Serialization if (contract.ISerializableCreator == null) throw new JsonSerializationException("ISerializable type '{0}' does not have a valid constructor.".FormatWith(CultureInfo.InvariantCulture, objectType)); - object createdObject = contract.ISerializableCreator(new object[] { serializationInfo, Serializer.Context }); + object createdObject = contract.ISerializableCreator(serializationInfo, Serializer.Context); if (id != null) Serializer.ReferenceResolver.AddReference(id, createdObject); diff --git a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs index a7beeee2..012fbede 100644 --- a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs @@ -610,5 +610,19 @@ namespace Newtonsoft.Json.Utilities return allAdded; } + + public static int IndexOf(this IEnumerable collection, Func predicate) + { + int index = 0; + foreach (T value in collection) + { + if (predicate(value)) + return index; + + index++; + } + + return -1; + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index 2903156d..d22752bc 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -489,17 +489,30 @@ namespace Newtonsoft.Json.Utilities /// Determines whether the specified MemberInfo can be read. /// /// The MemberInfo to determine whether can be read. + /// /// if set to true then allow the member to be gotten non-publicly. /// /// true if the specified MemberInfo can be read; otherwise, false. /// - public static bool CanReadMemberValue(MemberInfo member) + public static bool CanReadMemberValue(MemberInfo member, bool nonPublic) { switch (member.MemberType) { case MemberTypes.Field: - return true; + FieldInfo fieldInfo = (FieldInfo)member; + + if (nonPublic) + return true; + else if (fieldInfo.IsPublic) + return true; + return false; case MemberTypes.Property: - return ((PropertyInfo)member).CanRead; + PropertyInfo propertyInfo = (PropertyInfo) member; + + if (!propertyInfo.CanRead) + return false; + if (nonPublic) + return true; + return (propertyInfo.GetGetMethod(nonPublic) != null); default: return false; } @@ -509,17 +522,32 @@ namespace Newtonsoft.Json.Utilities /// Determines whether the specified MemberInfo can be set. /// /// The MemberInfo to determine whether can be set. + /// if set to true then allow the member to be set non-publicly. /// /// true if the specified MemberInfo can be set; otherwise, false. /// - public static bool CanSetMemberValue(MemberInfo member) + public static bool CanSetMemberValue(MemberInfo member, bool nonPublic) { switch (member.MemberType) { case MemberTypes.Field: - return !((FieldInfo)member).IsInitOnly; + FieldInfo fieldInfo = (FieldInfo)member; + + if (fieldInfo.IsInitOnly) + return false; + if (nonPublic) + return true; + else if (fieldInfo.IsPublic) + return true; + return false; case MemberTypes.Property: - return ((PropertyInfo)member).CanWrite; + PropertyInfo propertyInfo = (PropertyInfo)member; + + if (!propertyInfo.CanWrite) + return false; + if (nonPublic) + return true; + return (propertyInfo.GetSetMethod(nonPublic) != null); default: return false; } @@ -540,6 +568,7 @@ namespace Newtonsoft.Json.Utilities // for some reason .NET returns multiple members when overriding a generic member on a base class // http://forums.msdn.microsoft.com/en-US/netfxbcl/thread/b5abbfee-e292-4a64-8907-4e3f0fb90cd9/ // filter members to only return the override on the topmost class + // update: I think this is fixed in .NET 3.5 SP1 - leave this in for now... List distinctMembers = new List(targetMembers.Count); var groupedMembers = targetMembers.GroupBy(m => m.Name).Select(g => new { Count = g.Count(), Members = g.Cast() }); @@ -655,6 +684,9 @@ namespace Newtonsoft.Json.Utilities #if !PocketPC return Activator.CreateInstance(type, args); #else + // CF doesn't have a Activator.CreateInstance overload that takes args + // lame + if (type.IsValueType) return Activator.CreateInstance(type); @@ -732,6 +764,8 @@ namespace Newtonsoft.Json.Utilities ValidationUtils.ArgumentNotNull(targetType, "targetType"); List fieldInfos = new List(targetType.GetFields(bindingAttr)); + // Type.GetFields doesn't return inherited private fields + // manually find private fields from base class GetChildPrivateFields(fieldInfos, targetType, bindingAttr); return fieldInfos.Cast(); @@ -739,18 +773,20 @@ namespace Newtonsoft.Json.Utilities private static void GetChildPrivateFields(IList initialFields, Type targetType, BindingFlags bindingAttr) { - // fix weirdness with FieldInfos only being returned for the current Type + // fix weirdness with private FieldInfos only being returned for the current Type // find base type fields and add them to result if ((bindingAttr & BindingFlags.NonPublic) != 0) { // modify flags to not search for public fields - BindingFlags nonPublicBindingAttr = ((bindingAttr & BindingFlags.Public) == BindingFlags.Public) - ? bindingAttr ^ BindingFlags.Public - : bindingAttr; + BindingFlags nonPublicBindingAttr = bindingAttr.RemoveFlag(BindingFlags.Public); while ((targetType = targetType.BaseType) != null) { - initialFields.AddRange(targetType.GetFields(nonPublicBindingAttr)); + // filter out protected fields + IEnumerable childPrivateFields = + targetType.GetFields(nonPublicBindingAttr).Where(f => f.IsPrivate).Cast(); + + initialFields.AddRange(childPrivateFields); } } } @@ -765,20 +801,43 @@ namespace Newtonsoft.Json.Utilities return propertyInfos.Cast(); } - private static void GetChildPrivateProperties(IList initialFields, Type targetType, BindingFlags bindingAttr) + public static BindingFlags RemoveFlag(this BindingFlags bindingAttr, BindingFlags flag) { - // fix weirdness with FieldInfos only being returned for the current Type - // find base type fields and add them to result + return ((bindingAttr & flag) == flag) + ? bindingAttr ^ flag + : bindingAttr; + } + + private static void GetChildPrivateProperties(IList initialProperties, Type targetType, BindingFlags bindingAttr) + { + // fix weirdness with private PropertyInfos only being returned for the current Type + // find base type properties and add them to result if ((bindingAttr & BindingFlags.NonPublic) != 0) { // modify flags to not search for public fields - BindingFlags nonPublicBindingAttr = ((bindingAttr & BindingFlags.Public) == BindingFlags.Public) - ? bindingAttr ^ BindingFlags.Public - : bindingAttr; + BindingFlags nonPublicBindingAttr = bindingAttr.RemoveFlag(BindingFlags.Public); while ((targetType = targetType.BaseType) != null) { - initialFields.AddRange(targetType.GetProperties(nonPublicBindingAttr)); + foreach (PropertyInfo propertyInfo in targetType.GetProperties(nonPublicBindingAttr)) + { + PropertyInfo nonPublicProperty = propertyInfo; + + // have to test on name rather than reference because instances are different + // depending on the type that GetProperties was called on + int index = initialProperties.IndexOf(p => p.Name == nonPublicProperty.Name); + if (index == -1) + { + initialProperties.Add(nonPublicProperty); + } + else + { + // replace nonpublic properties for a child, but gotten from + // the parent with the one from the child + // the property gotten from the child will have access to private getter/setter + initialProperties[index] = nonPublicProperty; + } + } } } }