-Fix possible errors caused by mutable GetHashCode implementations and reference tracking

This commit is contained in:
JamesNK
2010-03-23 01:20:27 +00:00
parent 7f7cccd598
commit 064e5cb8ed
3 changed files with 105 additions and 3 deletions
@@ -728,5 +728,79 @@ namespace Newtonsoft.Json.Tests.Serialization
// true
}
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class User
{
#region properties
[JsonProperty(Required = Required.Always, PropertyName = "SecretType")]
private string secretType;
[JsonProperty(Required = Required.Always)]
public string Login { get; set; }
public Type SecretType
{
get { return Type.GetType(secretType); }
set { secretType = value.AssemblyQualifiedName; }
}
[JsonProperty]
public User Friend { get; set; }
#endregion
#region constructors
public User()
{
}
public User(string login, Type secretType)
: this()
{
this.Login = login;
this.SecretType = secretType;
}
#endregion
#region methods
public override int GetHashCode()
{
return SecretType.GetHashCode();
}
public override string ToString()
{
return string.Format("SecretType: {0}, Login: {1}", secretType, Login);
}
#endregion
}
[Test]
public void DeserializeTypeWithDubiousGetHashcode()
{
User user1 = new User("Peter", typeof(Version));
User user2 = new User("Michael", typeof(Version));
user1.Friend = user2;
JsonSerializerSettings serializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
PreserveReferencesHandling = PreserveReferencesHandling.Objects
};
string json = JsonConvert.SerializeObject(user1, Formatting.Indented, serializerSettings);
User deserializedUser = JsonConvert.DeserializeObject<User>(json, serializerSettings);
Assert.IsNotNull(deserializedUser);
}
}
}
@@ -34,6 +34,19 @@ namespace Newtonsoft.Json.Serialization
{
internal class DefaultReferenceResolver : IReferenceResolver
{
private class ReferenceEqualsEqualityComparer : IEqualityComparer<object>
{
bool IEqualityComparer<object>.Equals(object x, object y)
{
return ReferenceEquals(x, y);
}
int IEqualityComparer<object>.GetHashCode(object obj)
{
return -1;
}
}
private int _referenceCount;
private BidirectionalDictionary<string, object> _mappings;
@@ -41,8 +54,12 @@ namespace Newtonsoft.Json.Serialization
{
get
{
// override equality comparer for object key dictionary
// object will be modified as it deserializes and might have mutable hashcode
if (_mappings == null)
_mappings = new BidirectionalDictionary<string, object>();
_mappings = new BidirectionalDictionary<string, object>(
EqualityComparer<string>.Default,
new ReferenceEqualsEqualityComparer());
return _mappings;
}
@@ -32,8 +32,19 @@ namespace Newtonsoft.Json.Utilities
{
internal class BidirectionalDictionary<TFirst, TSecond>
{
private readonly IDictionary<TFirst, TSecond> _firstToSecond = new Dictionary<TFirst, TSecond>();
private readonly IDictionary<TSecond, TFirst> _secondToFirst = new Dictionary<TSecond, TFirst>();
private readonly IDictionary<TFirst, TSecond> _firstToSecond;
private readonly IDictionary<TSecond, TFirst> _secondToFirst;
public BidirectionalDictionary()
: this(EqualityComparer<TFirst>.Default, EqualityComparer<TSecond>.Default)
{
}
public BidirectionalDictionary(IEqualityComparer<TFirst> firstEqualityComparer, IEqualityComparer<TSecond> secondEqualityComparer)
{
_firstToSecond = new Dictionary<TFirst, TSecond>(firstEqualityComparer);
_secondToFirst = new Dictionary<TSecond, TFirst>(secondEqualityComparer);
}
public void Add(TFirst first, TSecond second)
{