Fix MaxDepth when used with ToObject inside of a JsonConverter (#2505)

This commit is contained in:
James Newton-King
2021-03-31 15:57:25 +13:00
committed by GitHub
parent ae9fe44e13
commit f7e7bd05d9
11 changed files with 287 additions and 6 deletions
@@ -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<TestObject>(jsontext, new JsonSerializerSettings
{
Converters = new List<JsonConverter> { 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<JsonReaderException>(() => JsonConvert.DeserializeObject<TestObject>(jsontext, new JsonSerializerSettings
{
Converters = new List<JsonConverter> { 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<JObject>(serializer);
return new TestObject
{
Children = newToken
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
}
#endif
@@ -1776,6 +1776,45 @@ third line", jsonTextReader.Value);
JsonTextReader reader = new JsonTextReader(new StringReader(json));
await ExceptionAssert.ThrowsAsync<JsonReaderException>(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
@@ -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())
{
}
}
}
}
@@ -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<JsonReaderException>(
() => JsonConvert.DeserializeObject<JObject>(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<JObject>(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<JObject>(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<JObject>(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;
}
}
}
@@ -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
+1 -1
View File
@@ -228,7 +228,7 @@ namespace Newtonsoft.Json
/// <summary>
/// Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a <see cref="JsonReaderException"/>.
/// A null value means there is no maximum.
/// The default value is <c>128</c>.
/// The default value is <c>64</c>.
/// </summary>
public int? MaxDepth
{
+2 -2
View File
@@ -514,7 +514,7 @@ namespace Newtonsoft.Json
/// <summary>
/// Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a <see cref="JsonReaderException"/>.
/// A null value means there is no maximum.
/// The default value is <c>128</c>.
/// The default value is <c>64</c>.
/// </summary>
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))
{
@@ -326,7 +326,7 @@ namespace Newtonsoft.Json
/// <summary>
/// Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a <see cref="JsonReaderException"/>.
/// A null value means there is no maximum.
/// The default value is <c>128</c>.
/// The default value is <c>64</c>.
/// </summary>
public int? MaxDepth
{
+8
View File
@@ -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);
}
}
@@ -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<ErrorEventArgs>? Error
{
@@ -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()
{