Extracted JsonNameTable for JsonTextReader (#1703)

This commit is contained in:
Ilya Grebnov
2018-06-21 10:22:33 +12:00
committed by James Newton-King
parent ebadeaf8e5
commit 2d5eba5599
8 changed files with 85 additions and 23 deletions
+28 -4
View File
@@ -174,11 +174,11 @@ namespace Newtonsoft.Json.Tests
reader.Read();
JsonTextReader jsonTextReader = (JsonTextReader)reader;
Assert.IsNotNull(jsonTextReader.NameTable);
Assert.IsNotNull(jsonTextReader.PropertyNameTable);
string s = serializer.Deserialize<string>(reader);
Assert.AreEqual("hi", s);
Assert.IsNotNull(jsonTextReader.NameTable);
Assert.IsNotNull(jsonTextReader.PropertyNameTable);
NameTableTestClass o = new NameTableTestClass
{
@@ -200,16 +200,40 @@ namespace Newtonsoft.Json.Tests
StringReader sr = new StringReader("{'property':'hi'}");
JsonTextReader jsonTextReader = new JsonTextReader(sr);
Assert.IsNull(jsonTextReader.NameTable);
Assert.IsNull(jsonTextReader.PropertyNameTable);
JsonSerializer serializer = new JsonSerializer();
serializer.Converters.Add(new NameTableTestClassConverter());
NameTableTestClass o = serializer.Deserialize<NameTableTestClass>(jsonTextReader);
Assert.IsNull(jsonTextReader.NameTable);
Assert.IsNull(jsonTextReader.PropertyNameTable);
Assert.AreEqual("hi", o.Value);
}
public class CustonNameTable : JsonNameTable
{
public override string Get(char[] key, int start, int length)
{
return "_" + new string(key, start, length);
}
}
[Test]
public void CustonNameTableTest()
{
StringReader sr = new StringReader("{'property':'hi'}");
JsonTextReader jsonTextReader = new JsonTextReader(sr);
Assert.IsNull(jsonTextReader.PropertyNameTable);
var nameTable = jsonTextReader.PropertyNameTable = new CustonNameTable();
JsonSerializer serializer = new JsonSerializer();
Dictionary<string, string> o = serializer.Deserialize<Dictionary<string, string>>(jsonTextReader);
Assert.AreEqual("hi", o["_property"]);
Assert.AreEqual(nameTable, jsonTextReader.PropertyNameTable);
}
[Test]
public void DefaultSettings_Example()
{
@@ -25,9 +25,12 @@
using System;
namespace Newtonsoft.Json.Utilities
namespace Newtonsoft.Json
{
internal class PropertyNameTable
/// <summary>
/// The default JSON name table implementation.
/// </summary>
public class DefaultJsonNameTable : JsonNameTable
{
// used to defeat hashtable DoS attack where someone passes in lots of strings that hash to the same hash code
private static readonly int HashCodeRandomizer;
@@ -36,17 +39,26 @@ namespace Newtonsoft.Json.Utilities
private Entry[] _entries;
private int _mask = 31;
static PropertyNameTable()
static DefaultJsonNameTable()
{
HashCodeRandomizer = Environment.TickCount;
}
public PropertyNameTable()
/// <summary>
/// Initializes a new instance of the <see cref="DefaultJsonNameTable"/> class.
/// </summary>
public DefaultJsonNameTable()
{
_entries = new Entry[_mask + 1];
}
public string Get(char[] key, int start, int length)
/// <summary>
/// Gets the string containing the same characters as the specified range of characters in the given array.
/// </summary>
/// <param name="key">The character array containing the name to find.</param>
/// <param name="start">The zero-based index into the array specifying the first character of the name.</param>
/// <param name="length">The number of characters in the name.</param>
public override string Get(char[] key, int start, int length)
{
if (length == 0)
{
@@ -74,6 +86,11 @@ namespace Newtonsoft.Json.Utilities
return null;
}
/// <summary>
/// Adds the specified string into name table.
/// </summary>
/// <param name="key">The string to add.</param>
/// <remarks>This method is not thread-safe.</remarks>
public string Add(string key)
{
if (key == null)
+16
View File
@@ -0,0 +1,16 @@
namespace Newtonsoft.Json
{
/// <summary>
/// Base class for a table of atomized string objects.
/// </summary>
public abstract class JsonNameTable
{
/// <summary>
/// Gets the string containing the same characters as the specified range of characters in the given array.
/// </summary>
/// <param name="key">The character array containing the name to find.</param>
/// <param name="start">The zero-based index into the array specifying the first character of the name.</param>
/// <param name="length">The number of characters in the name.</param>
public abstract string Get(char[] key, int start, int length);
}
}
+5 -4
View File
@@ -974,9 +974,9 @@ namespace Newtonsoft.Json
if (reader is JsonTextReader textReader)
{
if (_contractResolver is DefaultContractResolver resolver)
if (textReader.PropertyNameTable == null && _contractResolver is DefaultContractResolver resolver)
{
textReader.NameTable = resolver.GetNameTable();
textReader.PropertyNameTable = resolver.GetNameTable();
}
}
}
@@ -1009,9 +1009,10 @@ namespace Newtonsoft.Json
reader.DateFormatString = previousDateFormatString;
}
if (reader is JsonTextReader textReader)
if (reader is JsonTextReader textReader && textReader.PropertyNameTable != null &&
_contractResolver is DefaultContractResolver resolver && textReader.PropertyNameTable == resolver.GetNameTable())
{
textReader.NameTable = null;
textReader.PropertyNameTable = null;
}
}
+2 -2
View File
@@ -979,9 +979,9 @@ namespace Newtonsoft.Json
string propertyName;
if (NameTable != null)
if (PropertyNameTable != null)
{
propertyName = NameTable.Get(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length)
propertyName = PropertyNameTable.Get(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length)
// no match in name table
?? _stringReference.ToString();
}
+7 -3
View File
@@ -74,7 +74,6 @@ namespace Newtonsoft.Json
private StringBuffer _stringBuffer;
private StringReference _stringReference;
private IArrayPool<char> _arrayPool;
internal PropertyNameTable NameTable;
/// <summary>
/// Initializes a new instance of the <see cref="JsonTextReader"/> class with the specified <see cref="TextReader"/>.
@@ -105,6 +104,11 @@ namespace Newtonsoft.Json
internal int CharPos => _charPos;
#endif
/// <summary>
/// Gets or sets the reader's property name table.
/// </summary>
public JsonNameTable PropertyNameTable { get; set; }
/// <summary>
/// Gets or sets the reader's character buffer pool.
/// </summary>
@@ -1563,9 +1567,9 @@ namespace Newtonsoft.Json
string propertyName;
if (NameTable != null)
if (PropertyNameTable != null)
{
propertyName = NameTable.Get(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length);
propertyName = PropertyNameTable.Get(_stringReference.Chars, _stringReference.StartIndex, _stringReference.Length);
// no match in name table
if (propertyName == null)
@@ -68,7 +68,7 @@ namespace Newtonsoft.Json.Serialization
public class CamelCasePropertyNamesContractResolver : DefaultContractResolver
{
private static readonly object TypeContractCacheLock = new object();
private static readonly PropertyNameTable NameTable = new PropertyNameTable();
private static readonly DefaultJsonNameTable NameTable = new DefaultJsonNameTable();
private static Dictionary<ResolverContractKey, JsonContract> _contractCache;
/// <summary>
@@ -119,7 +119,7 @@ namespace Newtonsoft.Json.Serialization
return contract;
}
internal override PropertyNameTable GetNameTable()
internal override DefaultJsonNameTable GetNameTable()
{
return NameTable;
}
@@ -98,7 +98,7 @@ namespace Newtonsoft.Json.Serialization
new RegexConverter()
};
private readonly PropertyNameTable _nameTable = new PropertyNameTable();
private readonly DefaultJsonNameTable _nameTable = new DefaultJsonNameTable();
private readonly ThreadSafeStore<Type, JsonContract> _contractCache;
@@ -1334,7 +1334,7 @@ namespace Newtonsoft.Json.Serialization
throw new JsonSerializationException("Null collection of serializable members returned.");
}
PropertyNameTable nameTable = GetNameTable();
DefaultJsonNameTable nameTable = GetNameTable();
JsonPropertyCollection properties = new JsonPropertyCollection(type);
@@ -1358,7 +1358,7 @@ namespace Newtonsoft.Json.Serialization
return orderedProperties;
}
internal virtual PropertyNameTable GetNameTable()
internal virtual DefaultJsonNameTable GetNameTable()
{
return _nameTable;
}