diff --git a/Src/Newtonsoft.Json.Tests/Issues/Issue2504.cs b/Src/Newtonsoft.Json.Tests/Issues/Issue2504.cs new file mode 100644 index 00000000..4363a911 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Issues/Issue2504.cs @@ -0,0 +1,121 @@ +#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 + +#if (NET45 || NET50) +#if DNXCORE50 +using Xunit; +using Test = Xunit.FactAttribute; +using Assert = Newtonsoft.Json.Tests.XUnitAssert; +#else +using NUnit.Framework; +#endif +using System.Collections.Generic; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Converters; +using System.Collections; +using System; +using System.IO; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Issues +{ + [TestFixture] + public class Issue2504 : TestFixtureBase + { + [Test] + public void Test() + { + string jsontext = GetNestedJson(150); + + var o = JsonConvert.DeserializeObject(jsontext, new JsonSerializerSettings + { + Converters = new List { new TestConverter() }, + MaxDepth = 150 + }); + + Assert.AreEqual(150, GetDepth(o.Children)); + } + + [Test] + public void Test_Failure() + { + string jsontext = GetNestedJson(150); + + string expectedMessage = @"The reader's MaxDepth of 100 has been exceeded. Path '0.1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.98.99', line 101, position 207."; + + ExceptionAssert.Throws(() => JsonConvert.DeserializeObject(jsontext, new JsonSerializerSettings + { + Converters = new List { new TestConverter() }, + MaxDepth = 100 + }), expectedMessage); + } + + private static int GetDepth(JToken o) + { + int depth = 1; + while (o.First != null) + { + o = o.First; + if (o.Type == JTokenType.Object) + { + depth++; + } + } + + return depth; + } + + private class TestObject + { + public JToken Children { get; set; } + } + + private class TestConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(TestObject); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + JToken token = JToken.Load(reader); + + var newToken = token.ToObject(serializer); + + return new TestObject + { + Children = newToken + }; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadAsyncTests.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadAsyncTests.cs index 7f23c53d..770b7736 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadAsyncTests.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadAsyncTests.cs @@ -1776,6 +1776,45 @@ third line", jsonTextReader.Value); JsonTextReader reader = new JsonTextReader(new StringReader(json)); await ExceptionAssert.ThrowsAsync(async () => await JToken.ReadFromAsync(reader, settings)); } + + [Test] + public async Task MaxDepth_GreaterThanDefaultAsync() + { + string json = GetNestedJson(150); + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + reader.MaxDepth = 150; + + while (await reader.ReadAsync()) + { + } + } + + [Test] + public async Task MaxDepth_NullAsync() + { + string json = GetNestedJson(150); + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + reader.MaxDepth = null; + + while (await reader.ReadAsync()) + { + } + } + + [Test] + public async Task MaxDepth_MaxValueAsync() + { + string json = GetNestedJson(150); + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + reader.MaxDepth = int.MaxValue; + + while (await reader.ReadAsync()) + { + } + } } } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadTests.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadTests.cs index 905dbbed..5a8bd1af 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadTests.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTests/ReadTests.cs @@ -1770,5 +1770,44 @@ third line", jsonTextReader.Value); JToken.ReadFrom(reader, settings); }); } + + [Test] + public void MaxDepth_GreaterThanDefault() + { + string json = GetNestedJson(150); + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + reader.MaxDepth = 150; + + while (reader.Read()) + { + } + } + + [Test] + public void MaxDepth_Null() + { + string json = GetNestedJson(150); + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + reader.MaxDepth = null; + + while (reader.Read()) + { + } + } + + [Test] + public void MaxDepth_MaxValue() + { + string json = GetNestedJson(150); + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + reader.MaxDepth = int.MaxValue; + + while (reader.Read()) + { + } + } } } diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 6ad965a3..6ca78fb3 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -8032,5 +8032,63 @@ This is just junk, though."; Assert.AreEqual(64, reader.MaxDepth); } + + [Test] + public void SetMaxDepth_DefaultDepthExceeded() + { + string json = GetNestedJson(150); + + ExceptionAssert.Throws( + () => JsonConvert.DeserializeObject(json), + "The reader's MaxDepth of 64 has been exceeded. Path '0.1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63', line 65, position 135."); + } + + [Test] + public void SetMaxDepth_IncreasedDepthNotExceeded() + { + string json = GetNestedJson(150); + + JObject o = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { MaxDepth = 150 }); + int depth = GetDepth(o); + + Assert.AreEqual(150, depth); + } + + [Test] + public void SetMaxDepth_NullDepthNotExceeded() + { + string json = GetNestedJson(150); + + JObject o = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { MaxDepth = null }); + int depth = GetDepth(o); + + Assert.AreEqual(150, depth); + } + + [Test] + public void SetMaxDepth_MaxValueDepthNotExceeded() + { + string json = GetNestedJson(150); + + JObject o = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { MaxDepth = int.MaxValue }); + int depth = GetDepth(o); + + Assert.AreEqual(150, depth); + } + + private static int GetDepth(JToken o) + { + int depth = 1; + while (o.First != null) + { + o = o.First; + if (o.Type == JTokenType.Object) + { + depth++; + } + } + + return depth; + } } } diff --git a/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs b/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs index 52c5ed35..7611f209 100644 --- a/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs +++ b/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs @@ -36,6 +36,7 @@ using System.Runtime.Serialization.Json; #endif using System.Text; using System.Threading; +using Newtonsoft.Json.Linq; #if DNXCORE50 using Xunit; using Assert = Newtonsoft.Json.Tests.XUnitAssert; @@ -306,6 +307,21 @@ namespace Newtonsoft.Json.Tests { return @"@""" + json.Replace(@"""", @"""""") + @""""; } + + protected string GetNestedJson(int depth) + { + JObject root = new JObject(); + JObject current = root; + for (int i = 0; i < depth - 1; i++) + { + JObject nested = new JObject(); + current[i.ToString()] = nested; + + current = nested; + } + + return root.ToString(); + } } public static class CustomAssert diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index b94c63ea..2351b187 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -228,7 +228,7 @@ namespace Newtonsoft.Json /// /// Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . /// A null value means there is no maximum. - /// The default value is 128. + /// The default value is 64. /// public int? MaxDepth { diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 23f5c6b6..13e3a9c6 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -514,7 +514,7 @@ namespace Newtonsoft.Json /// /// Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . /// A null value means there is no maximum. - /// The default value is 128. + /// The default value is 64. /// public virtual int? MaxDepth { @@ -913,7 +913,7 @@ namespace Newtonsoft.Json return value; } - private void SetupReader(JsonReader reader, out CultureInfo? previousCulture, out DateTimeZoneHandling? previousDateTimeZoneHandling, out DateParseHandling? previousDateParseHandling, out FloatParseHandling? previousFloatParseHandling, out int? previousMaxDepth, out string? previousDateFormatString) + internal void SetupReader(JsonReader reader, out CultureInfo? previousCulture, out DateTimeZoneHandling? previousDateTimeZoneHandling, out DateParseHandling? previousDateParseHandling, out FloatParseHandling? previousFloatParseHandling, out int? previousMaxDepth, out string? previousDateFormatString) { if (_culture != null && !_culture.Equals(reader.Culture)) { diff --git a/Src/Newtonsoft.Json/JsonSerializerSettings.cs b/Src/Newtonsoft.Json/JsonSerializerSettings.cs index bc0cd5a7..fc5205dd 100644 --- a/Src/Newtonsoft.Json/JsonSerializerSettings.cs +++ b/Src/Newtonsoft.Json/JsonSerializerSettings.cs @@ -326,7 +326,7 @@ namespace Newtonsoft.Json /// /// Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a . /// A null value means there is no maximum. - /// The default value is 128. + /// The default value is 64. /// public int? MaxDepth { diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index d1f6c451..ff4aaf93 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -45,6 +45,7 @@ using Newtonsoft.Json.Utilities.LinqBridge; #else using System.Linq; #endif +using Newtonsoft.Json.Serialization; namespace Newtonsoft.Json.Linq { @@ -2079,6 +2080,13 @@ namespace Newtonsoft.Json.Linq using (JTokenReader jsonReader = new JTokenReader(this)) { + // Hacky fix to ensure the serializer settings are set onto the new reader. + // This is required because the serializer won't update settings when used inside of a converter. + if (jsonSerializer is JsonSerializerProxy proxy) + { + proxy._serializer.SetupReader(jsonReader, out _, out _, out _, out _, out _, out _); + } + return jsonSerializer.Deserialize(jsonReader, objectType); } } diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs index fc8e26fd..a122fe00 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs @@ -36,7 +36,7 @@ namespace Newtonsoft.Json.Serialization { private readonly JsonSerializerInternalReader? _serializerReader; private readonly JsonSerializerInternalWriter? _serializerWriter; - private readonly JsonSerializer _serializer; + internal readonly JsonSerializer _serializer; public override event EventHandler? Error { diff --git a/Src/Newtonsoft.Json/Serialization/TraceJsonReader.cs b/Src/Newtonsoft.Json/Serialization/TraceJsonReader.cs index 04bc99ea..53ab4977 100644 --- a/Src/Newtonsoft.Json/Serialization/TraceJsonReader.cs +++ b/Src/Newtonsoft.Json/Serialization/TraceJsonReader.cs @@ -138,7 +138,7 @@ namespace Newtonsoft.Json.Serialization public override object? Value => _innerReader.Value; - public override Type ?ValueType => _innerReader.ValueType; + public override Type? ValueType => _innerReader.ValueType; public override void Close() {