-Fix possible errors caused by mutable GetHashCode implementations and reference tracking
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user