-JProperty performance
-Improved property name escape skipping
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user