Files

411 lines
13 KiB
C#

#region License
// Copyright (c) 2007 James Newton-King
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
#if DNXCORE50
using Xunit;
using Test = Xunit.FactAttribute;
using Assert = Newtonsoft.Json.Tests.XUnitAssert;
#else
using NUnit.Framework;
#endif
#if !(NET20 || NET35)
using System.Dynamic;
#endif
using System.Runtime.Serialization;
using Newtonsoft.Json.Tests.Linq;
namespace Newtonsoft.Json.Tests.Serialization
{
[TestFixture]
public class ReferenceLoopHandlingTests : TestFixtureBase
{
[Test]
public void ReferenceLoopHandlingTest()
{
JsonPropertyAttribute attribute = new JsonPropertyAttribute();
Assert.AreEqual(null, attribute._defaultValueHandling);
Assert.AreEqual(ReferenceLoopHandling.Error, attribute.ReferenceLoopHandling);
attribute.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
Assert.AreEqual(ReferenceLoopHandling.Ignore, attribute._referenceLoopHandling);
Assert.AreEqual(ReferenceLoopHandling.Ignore, attribute.ReferenceLoopHandling);
}
[Test]
public void IgnoreObjectReferenceLoop()
{
ReferenceLoopHandlingObjectContainerAttribute o = new ReferenceLoopHandlingObjectContainerAttribute();
o.Value = o;
string json = JsonConvert.SerializeObject(o, Formatting.Indented, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
});
Assert.AreEqual("{}", json);
}
[Test]
public void IgnoreObjectReferenceLoopWithPropertyOverride()
{
ReferenceLoopHandlingObjectContainerAttributeWithPropertyOverride o = new ReferenceLoopHandlingObjectContainerAttributeWithPropertyOverride();
o.Value = o;
string json = JsonConvert.SerializeObject(o, Formatting.Indented, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
});
StringAssert.AreEqual(@"{
""Value"": {
""Value"": {
""Value"": {
""Value"": {
""Value"": {
""Value"": null
}
}
}
}
}
}", json);
}
[Test]
public void IgnoreArrayReferenceLoop()
{
ReferenceLoopHandlingList a = new ReferenceLoopHandlingList();
a.Add(a);
string json = JsonConvert.SerializeObject(a, Formatting.Indented, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
});
Assert.AreEqual("[]", json);
}
[Test]
public void IgnoreDictionaryReferenceLoop()
{
ReferenceLoopHandlingDictionary d = new ReferenceLoopHandlingDictionary();
d.Add("First", d);
string json = JsonConvert.SerializeObject(d, Formatting.Indented, new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
});
Assert.AreEqual("{}", json);
}
[Test]
public void SerializePropertyItemReferenceLoopHandling()
{
PropertyItemReferenceLoopHandling c = new PropertyItemReferenceLoopHandling();
c.Text = "Text!";
c.SetData(new List<PropertyItemReferenceLoopHandling> { c });
string json = JsonConvert.SerializeObject(c, Formatting.Indented);
StringAssert.AreEqual(@"{
""Text"": ""Text!"",
""Data"": [
{
""Text"": ""Text!"",
""Data"": [
{
""Text"": ""Text!"",
""Data"": [
{
""Text"": ""Text!"",
""Data"": null
}
]
}
]
}
]
}", json);
}
#if !(PORTABLE || DNXCORE50 || PORTABLE40) || NETSTANDARD1_3 || NETSTANDARD2_0 || NET6_0_OR_GREATER
[Serializable]
public class MainClass : ISerializable
{
public ChildClass Child { get; set; }
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Child", Child);
}
}
[Serializable]
public class ChildClass : ISerializable
{
public string Name { get; set; }
public MainClass Parent { get; set; }
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Parent", Parent);
info.AddValue("Name", Name);
}
}
[Test]
public void ErrorISerializableCyclicReferenceLoop()
{
var main = new MainClass();
var child = new ChildClass();
child.Name = "Child1";
child.Parent = main; // Obvious Circular Reference
main.Child = child;
var settings =
new JsonSerializerSettings();
ExceptionAssert.Throws<JsonSerializationException>(() => JsonConvert.SerializeObject(main, settings), "Self referencing loop detected with type 'Newtonsoft.Json.Tests.Serialization.ReferenceLoopHandlingTests+MainClass'. Path 'Child'.");
}
[Test]
public void IgnoreISerializableCyclicReferenceLoop()
{
var main = new MainClass();
var child = new ChildClass();
child.Name = "Child1";
child.Parent = main; // Obvious Circular Reference
main.Child = child;
var settings =
new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var c = JsonConvert.SerializeObject(main, settings);
Assert.AreEqual(@"{""Child"":{""Name"":""Child1""}}", c);
}
#endif
#if !(NET20 || NET35 || PORTABLE40)
public class DictionaryDynamicObject : DynamicObject
{
public IDictionary<string, object> Values { get; private set; }
public DictionaryDynamicObject()
{
Values = new Dictionary<string, object>();
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
Values[binder.Name] = value;
return true;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return Values.TryGetValue(binder.Name, out result);
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return Values.Keys;
}
}
[Test]
public void ErrorDynamicCyclicReferenceLoop()
{
dynamic parent = new DictionaryDynamicObject();
dynamic child = new DictionaryDynamicObject();
parent.child = child;
child.parent = parent;
var settings = new JsonSerializerSettings();
ExceptionAssert.Throws<JsonSerializationException>(() => JsonConvert.SerializeObject(parent, settings), "Self referencing loop detected with type 'Newtonsoft.Json.Tests.Serialization.ReferenceLoopHandlingTests+DictionaryDynamicObject'. Path 'child'.");
}
[Test]
public void IgnoreDynamicCyclicReferenceLoop()
{
dynamic parent = new DictionaryDynamicObject();
dynamic child = new DictionaryDynamicObject();
parent.child = child;
parent.name = "parent";
child.parent = parent;
child.name = "child";
var settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
var c = JsonConvert.SerializeObject(parent, settings);
Assert.AreEqual(@"{""child"":{""name"":""child""},""name"":""parent""}", c);
}
#endif
[Test]
public void EqualityComparer()
{
AccountWithEquals account = new AccountWithEquals
{
Name = "main"
};
AccountWithEquals manager = new AccountWithEquals
{
Name = "main"
};
account.Manager = manager;
ExceptionAssert.Throws<JsonSerializationException>(
() => JsonConvert.SerializeObject(account),
"Self referencing loop detected for property 'Manager' with type 'Newtonsoft.Json.Tests.Serialization.AccountWithEquals'. Path ''.");
string json = JsonConvert.SerializeObject(account, new JsonSerializerSettings
{
EqualityComparer = new ReferenceEqualsEqualityComparer(),
Formatting = Formatting.Indented
});
StringAssert.AreEqual(@"{
""Name"": ""main"",
""Manager"": {
""Name"": ""main"",
""Manager"": null
}
}", json);
}
}
public class ReferenceEqualsEqualityComparer : IEqualityComparer
{
bool IEqualityComparer.Equals(object x, object y)
{
return ReferenceEquals(x, y);
}
int IEqualityComparer.GetHashCode(object obj)
{
// put objects in a bucket based on their reference
return RuntimeHelpers.GetHashCode(obj);
}
}
public class AccountWithEquals
{
public string Name { get; set; }
public AccountWithEquals Manager { get; set; }
public override bool Equals(object obj)
{
AccountWithEquals a = obj as AccountWithEquals;
if (a == null)
{
return false;
}
return Name == a.Name;
}
public override int GetHashCode()
{
if (Name == null)
{
return 0;
}
return Name.GetHashCode();
}
}
public class PropertyItemReferenceLoopHandling
{
private IList<PropertyItemReferenceLoopHandling> _data;
private int _accessCount;
public string Text { get; set; }
[JsonProperty(ItemReferenceLoopHandling = ReferenceLoopHandling.Serialize)]
public IList<PropertyItemReferenceLoopHandling> Data
{
get
{
if (_accessCount >= 3)
{
return null;
}
_accessCount++;
return new List<PropertyItemReferenceLoopHandling>(_data);
}
}
public void SetData(IList<PropertyItemReferenceLoopHandling> data)
{
_data = data;
}
}
[JsonArray(ItemReferenceLoopHandling = ReferenceLoopHandling.Ignore)]
public class ReferenceLoopHandlingList : List<ReferenceLoopHandlingList>
{
}
[JsonDictionary(ItemReferenceLoopHandling = ReferenceLoopHandling.Ignore)]
public class ReferenceLoopHandlingDictionary : Dictionary<string, ReferenceLoopHandlingDictionary>
{
}
[JsonObject(ItemReferenceLoopHandling = ReferenceLoopHandling.Ignore)]
public class ReferenceLoopHandlingObjectContainerAttribute
{
public ReferenceLoopHandlingObjectContainerAttribute Value { get; set; }
}
[JsonObject(ItemReferenceLoopHandling = ReferenceLoopHandling.Ignore)]
public class ReferenceLoopHandlingObjectContainerAttributeWithPropertyOverride
{
private ReferenceLoopHandlingObjectContainerAttributeWithPropertyOverride _value;
private int _getCount;
[JsonProperty(ReferenceLoopHandling = ReferenceLoopHandling.Serialize)]
public ReferenceLoopHandlingObjectContainerAttributeWithPropertyOverride Value
{
get
{
if (_getCount < 5)
{
_getCount++;
return _value;
}
return null;
}
set { _value = value; }
}
}
}