diff --git a/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs index d0b10835..c216ed67 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs @@ -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(json, serializerSettings); + Assert.IsNotNull(deserializedUser); + } } } diff --git a/Src/Newtonsoft.Json/Serialization/DefaultReferenceResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultReferenceResolver.cs index f8db3fff..db8c47a9 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultReferenceResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultReferenceResolver.cs @@ -34,6 +34,19 @@ namespace Newtonsoft.Json.Serialization { internal class DefaultReferenceResolver : IReferenceResolver { + private class ReferenceEqualsEqualityComparer : IEqualityComparer + { + bool IEqualityComparer.Equals(object x, object y) + { + return ReferenceEquals(x, y); + } + + int IEqualityComparer.GetHashCode(object obj) + { + return -1; + } + } + private int _referenceCount; private BidirectionalDictionary _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(); + _mappings = new BidirectionalDictionary( + EqualityComparer.Default, + new ReferenceEqualsEqualityComparer()); return _mappings; } diff --git a/Src/Newtonsoft.Json/Utilities/BidirectionalDictionary.cs b/Src/Newtonsoft.Json/Utilities/BidirectionalDictionary.cs index f651f949..1a949fe0 100644 --- a/Src/Newtonsoft.Json/Utilities/BidirectionalDictionary.cs +++ b/Src/Newtonsoft.Json/Utilities/BidirectionalDictionary.cs @@ -32,8 +32,19 @@ namespace Newtonsoft.Json.Utilities { internal class BidirectionalDictionary { - private readonly IDictionary _firstToSecond = new Dictionary(); - private readonly IDictionary _secondToFirst = new Dictionary(); + private readonly IDictionary _firstToSecond; + private readonly IDictionary _secondToFirst; + + public BidirectionalDictionary() + : this(EqualityComparer.Default, EqualityComparer.Default) + { + } + + public BidirectionalDictionary(IEqualityComparer firstEqualityComparer, IEqualityComparer secondEqualityComparer) + { + _firstToSecond = new Dictionary(firstEqualityComparer); + _secondToFirst = new Dictionary(secondEqualityComparer); + } public void Add(TFirst first, TSecond second) {