-JProperty performance

-Improved property name escape skipping
This commit is contained in:
James Newton-King
2014-08-03 00:08:31 +12:00
parent 750771e890
commit 8e727a27b2
8 changed files with 281 additions and 29 deletions
@@ -27,6 +27,11 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
#if NET20
using Newtonsoft.Json.Utilities.LinqBridge;
#else
using System.Linq;
#endif
using Newtonsoft.Json.Linq;
#if !NETFX_CORE
using NUnit.Framework;
@@ -121,6 +126,59 @@ namespace Newtonsoft.Json.Tests.Linq
() => { l.Remove(p.Value); });
}
[Test]
public void IListRemoveAt()
{
JProperty p = new JProperty("TestProperty", null);
IList l = p;
ExceptionAssert.Throws<JsonException>(
"Cannot add or remove items from Newtonsoft.Json.Linq.JProperty.",
() => { l.RemoveAt(0); });
}
[Test]
public void JPropertyLinq()
{
JProperty p = new JProperty("TestProperty", null);
IList l = p;
List<JToken> result = l.Cast<JToken>().ToList();
Assert.AreEqual(1, result.Count);
}
[Test]
public void JPropertyDeepEquals()
{
JProperty p1 = new JProperty("TestProperty", null);
JProperty p2 = new JProperty("TestProperty", null);
Assert.AreEqual(true, JToken.DeepEquals(p1, p2));
}
[Test]
public void JPropertyIndexOf()
{
JValue v = new JValue(1);
JProperty p1 = new JProperty("TestProperty", v);
IList l1 = p1;
Assert.AreEqual(0, l1.IndexOf(v));
IList<JToken> l2 = p1;
Assert.AreEqual(0, l2.IndexOf(v));
}
[Test]
public void JPropertyContains()
{
JValue v = new JValue(1);
JProperty p = new JProperty("TestProperty", v);
Assert.AreEqual(true, p.Contains(v));
Assert.AreEqual(false, p.Contains(new JValue(1)));
}
[Test]
public void Load()
{
@@ -1039,6 +1039,25 @@ If attributes are not mentioned, default values are used in each case.
}, "JObject.ToString");
}
[Test]
public void JObjectCreationAndPropertyAccess()
{
TimeOperation<object>(() =>
{
for (int i = 0; i < Iterations * 100; i++)
{
JObject test = new JObject(
new JProperty("one", 1),
new JProperty("two", 2));
test["i"] = i;
int j = (int)test["i"];
test["j"] = j;
}
return null;
}, "JObjectCreationAndPropertyAccess");
}
[Test]
public void NestedJToken()
{
@@ -75,4 +75,4 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.4.17601")]
[assembly: AssemblyFileVersion("6.0.4.17602")]
@@ -71,9 +71,12 @@ namespace Newtonsoft.Json.Tests.Serialization
public class EscapedPropertiesContractResolver : DefaultContractResolver
{
public string PropertyPrefix { get; set; }
public string PropertySuffix { get; set; }
protected internal override string ResolvePropertyName(string propertyName)
{
return base.ResolvePropertyName(propertyName + @"-'-""-");
return base.ResolvePropertyName(PropertyPrefix + propertyName + PropertySuffix);
}
}
@@ -296,7 +299,10 @@ namespace Newtonsoft.Json.Tests.Serialization
},
new JsonSerializerSettings
{
ContractResolver = new EscapedPropertiesContractResolver()
ContractResolver = new EscapedPropertiesContractResolver
{
PropertySuffix = @"-'-""-"
}
});
Assert.AreEqual(@"{""AddressLine1-'-\""-"":""value!""}", json);
@@ -308,6 +314,94 @@ namespace Newtonsoft.Json.Tests.Serialization
Assert.AreEqual(@"AddressLine1-'-""-", reader.Value);
}
[Test]
public void SerializeWithHtmlEscapedPropertyName()
{
string json = JsonConvert.SerializeObject(
new AddressWithDataMember
{
AddressLine1 = "value!"
},
new JsonSerializerSettings
{
ContractResolver = new EscapedPropertiesContractResolver
{
PropertyPrefix = "<b>",
PropertySuffix = "</b>"
},
StringEscapeHandling = StringEscapeHandling.EscapeHtml
});
Assert.AreEqual(@"{""\u003cb\u003eAddressLine1\u003c/b\u003e"":""value!""}", json);
JsonTextReader reader = new JsonTextReader(new StringReader(json));
reader.Read();
reader.Read();
Assert.AreEqual(@"<b>AddressLine1</b>", reader.Value);
}
[Test]
public void CalculatingPropertyNameEscapedSkipping()
{
JsonProperty p = new JsonProperty { PropertyName = "abc" };
Assert.IsTrue(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = "123" };
Assert.IsTrue(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = "._-" };
Assert.IsTrue(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = "!@#" };
Assert.IsTrue(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = "$%^" };
Assert.IsTrue(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = "?*(" };
Assert.IsTrue(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = ")_+" };
Assert.IsTrue(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = "=:," };
Assert.IsTrue(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = null };
Assert.IsTrue(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = "&" };
Assert.IsFalse(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = "<" };
Assert.IsFalse(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = ">" };
Assert.IsFalse(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = "'" };
Assert.IsFalse(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = @"""" };
Assert.IsFalse(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = Environment.NewLine };
Assert.IsFalse(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = '\0'.ToString(CultureInfo.InvariantCulture) };
Assert.IsFalse(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = '\n'.ToString(CultureInfo.InvariantCulture) };
Assert.IsFalse(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = '\v'.ToString(CultureInfo.InvariantCulture) };
Assert.IsFalse(p._skipPropertyNameEscape);
p = new JsonProperty { PropertyName = '\u00B9'.ToString(CultureInfo.InvariantCulture) };
Assert.IsFalse(p._skipPropertyNameEscape);
}
#if !NET20
[Test]
public void DeserializeDataMemberClassWithNoDataContract()
+90 -3
View File
@@ -24,6 +24,7 @@
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using Newtonsoft.Json.Utilities;
using System.Diagnostics;
@@ -36,7 +37,93 @@ namespace Newtonsoft.Json.Linq
/// </summary>
public class JProperty : JContainer
{
private readonly List<JToken> _content = new List<JToken>();
#region JPropertyList
private class JPropertyList : IList<JToken>
{
internal JToken _token;
public IEnumerator<JToken> GetEnumerator()
{
if (_token != null)
yield return _token;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(JToken item)
{
_token = item;
}
public void Clear()
{
_token = null;
}
public bool Contains(JToken item)
{
return (_token == item);
}
public void CopyTo(JToken[] array, int arrayIndex)
{
if (_token != null)
array[arrayIndex] = _token;
}
public bool Remove(JToken item)
{
if (_token == item)
{
_token = null;
return true;
}
return false;
}
public int Count
{
get { return (_token != null) ? 1 : 0; }
}
public bool IsReadOnly
{
get { return false; }
}
public int IndexOf(JToken item)
{
return (_token == item) ? 0 : -1;
}
public void Insert(int index, JToken item)
{
if (index == 0)
_token = item;
}
public void RemoveAt(int index)
{
if (index == 0)
_token = null;
}
public JToken this[int index]
{
get { return (index == 0) ? _token : null; }
set
{
if (index == 0)
_token = value;
}
}
}
#endregion
private readonly JPropertyList _content = new JPropertyList();
private readonly string _name;
/// <summary>
@@ -65,14 +152,14 @@ namespace Newtonsoft.Json.Linq
public JToken Value
{
[DebuggerStepThrough]
get { return (_content.Count > 0) ? _content[0] : null; }
get { return _content._token; }
set
{
CheckReentrancy();
JToken newValue = value ?? JValue.CreateNull();
if (_content.Count == 0)
if (_content._token == null)
{
InsertItem(0, newValue, false);
}
@@ -91,5 +91,5 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.4.17601")]
[assembly: AssemblyFileVersion("6.0.4.17602")]
[assembly: CLSCompliant(true)]
@@ -43,7 +43,7 @@ namespace Newtonsoft.Json.Serialization
private object _defaultValue;
private bool _hasGeneratedDefaultValue;
private string _propertyName;
private bool _skipPropertyNameEscape;
internal bool _skipPropertyNameEscape;
private Type _propertyType;
// use to cache contract during deserialization
@@ -59,27 +59,7 @@ namespace Newtonsoft.Json.Serialization
set
{
_propertyName = value;
CalculateSkipPropertyNameEscape();
}
}
private void CalculateSkipPropertyNameEscape()
{
if (_propertyName == null)
{
_skipPropertyNameEscape = false;
}
else
{
_skipPropertyNameEscape = true;
foreach (char c in _propertyName)
{
if (!char.IsLetterOrDigit(c) && c != '_' && c != '@')
{
_skipPropertyNameEscape = false;
break;
}
}
_skipPropertyNameEscape = !JavaScriptUtils.ShouldEscapeJavaScriptString(_propertyName, JavaScriptUtils.HtmlCharEscapeFlags);
}
}
@@ -72,6 +72,20 @@ namespace Newtonsoft.Json.Utilities
private const string EscapedUnicodeText = "!";
public static bool ShouldEscapeJavaScriptString(string s, bool[] charEscapeFlags)
{
if (s == null)
return false;
foreach (char c in s)
{
if (c >= charEscapeFlags.Length || charEscapeFlags[c])
return true;
}
return false;
}
public static void WriteEscapedJavaScriptString(TextWriter writer, string s, char delimiter, bool appendDelimiters,
bool[] charEscapeFlags, StringEscapeHandling stringEscapeHandling, ref char[] writeBuffer)
{