-Fixed JsonProperty.Readable and Writable not being correctly set for .NET properties with modified getter/setter visibility

-Fixed error caused by property properties on a base class when reflecting a type
-Fixed property CanRead and CanWrite coming back as false when a base class property has a private getter/setter visibility
This commit is contained in:
JamesNK
2010-02-14 02:58:28 +00:00
parent c7facae789
commit 98164c53a6
8 changed files with 381 additions and 55 deletions
+78 -23
View File
@@ -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<T>(Func<T> 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);
@@ -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<string> javaScriptSerializerResult = javaScriptSerializer.Deserialize<List<string>>(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<Dictionary<Person, int>>(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<PersonPropertyClass>(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<TestClass>(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<TKey, TValue>
{
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<KVPair<string, string>> kvPairs =
JsonConvert.DeserializeObject<List<KVPair<string, string>>>(
"[{\"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>(
@"{
""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>(
@"{
""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;
}
}
}
}
@@ -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));
@@ -87,9 +87,9 @@ namespace Newtonsoft.Json.Serialization
#endif
/// <summary>
/// Gets or sets the default creator.
/// Gets or sets the default creator method used to create the object.
/// </summary>
/// <value>The default creator.</value>
/// <value>The default creator method used to create the object.</value>
public Func<object> DefaultCreator { get; set; }
/// <summary>
@@ -124,5 +124,16 @@ namespace Newtonsoft.Json.Serialization
/// </summary>
/// <value>The object creation handling.</value>
public ObjectCreationHandling? ObjectCreationHandling { get; set; }
/// <summary>
/// Returns a <see cref="String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return PropertyName;
}
}
}
@@ -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);
@@ -610,5 +610,19 @@ namespace Newtonsoft.Json.Utilities
return allAdded;
}
public static int IndexOf<T>(this IEnumerable<T> collection, Func<T, bool> predicate)
{
int index = 0;
foreach (T value in collection)
{
if (predicate(value))
return index;
index++;
}
return -1;
}
}
}
@@ -489,17 +489,30 @@ namespace Newtonsoft.Json.Utilities
/// Determines whether the specified MemberInfo can be read.
/// </summary>
/// <param name="member">The MemberInfo to determine whether can be read.</param>
/// /// <param name="nonPublic">if set to <c>true</c> then allow the member to be gotten non-publicly.</param>
/// <returns>
/// <c>true</c> if the specified MemberInfo can be read; otherwise, <c>false</c>.
/// </returns>
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.
/// </summary>
/// <param name="member">The MemberInfo to determine whether can be set.</param>
/// <param name="nonPublic">if set to <c>true</c> then allow the member to be set non-publicly.</param>
/// <returns>
/// <c>true</c> if the specified MemberInfo can be set; otherwise, <c>false</c>.
/// </returns>
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<MemberInfo> distinctMembers = new List<MemberInfo>(targetMembers.Count);
var groupedMembers = targetMembers.GroupBy(m => m.Name).Select(g => new { Count = g.Count(), Members = g.Cast<MemberInfo>() });
@@ -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<MemberInfo> fieldInfos = new List<MemberInfo>(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<FieldInfo>();
@@ -739,18 +773,20 @@ namespace Newtonsoft.Json.Utilities
private static void GetChildPrivateFields(IList<MemberInfo> 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<MemberInfo> childPrivateFields =
targetType.GetFields(nonPublicBindingAttr).Where(f => f.IsPrivate).Cast<MemberInfo>();
initialFields.AddRange(childPrivateFields);
}
}
}
@@ -765,20 +801,43 @@ namespace Newtonsoft.Json.Utilities
return propertyInfos.Cast<PropertyInfo>();
}
private static void GetChildPrivateProperties(IList<MemberInfo> 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<MemberInfo> 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;
}
}
}
}
}