-Improve performance and memory usage when serializing Unicode characters

This commit is contained in:
James Newton-King
2012-07-09 18:35:41 +12:00
parent 737f490cac
commit 9d7046bca2
4 changed files with 102 additions and 11 deletions
@@ -94,6 +94,7 @@ namespace Newtonsoft.Json.Tests
JsonNetWithIsoConverter,
JsonNetBinary,
JsonNetLinq,
JsonNetManual,
BinaryFormatter,
JavaScriptSerializer,
DataContractSerializer,
@@ -132,6 +133,7 @@ namespace Newtonsoft.Json.Tests
BenchmarkSerializeMethod(SerializeMethod.DataContractJsonSerializer, value);
BenchmarkSerializeMethod(SerializeMethod.JsonNet, value);
BenchmarkSerializeMethod(SerializeMethod.JsonNetLinq, value);
BenchmarkSerializeMethod(SerializeMethod.JsonNetManual, value);
BenchmarkSerializeMethod(SerializeMethod.JsonNetWithIsoConverter, value);
BenchmarkSerializeMethod(SerializeMethod.JsonNetBinary, value);
}
@@ -513,6 +515,69 @@ namespace Newtonsoft.Json.Tests
}
break;
}
case SerializeMethod.JsonNetManual:
{
TestClass c = value as TestClass;
if (c != null)
{
StringWriter sw = new StringWriter();
JsonTextWriter writer = new JsonTextWriter(sw);
writer.WriteStartObject();
writer.WritePropertyName("strings");
writer.WriteStartArray();
foreach (string s in c.strings)
{
writer.WriteValue(s);
}
writer.WriteEndArray();
writer.WritePropertyName("dictionary");
writer.WriteStartObject();
foreach (KeyValuePair<string, int> keyValuePair in c.dictionary)
{
writer.WritePropertyName(keyValuePair.Key);
writer.WriteValue(keyValuePair.Value);
}
writer.WriteEndObject();
writer.WritePropertyName("Name");
writer.WriteValue(c.Name);
writer.WritePropertyName("Now");
writer.WriteValue(c.Now);
writer.WritePropertyName("BigNumber");
writer.WriteValue(c.BigNumber);
writer.WritePropertyName("Address1");
writer.WriteStartObject();
writer.WritePropertyName("Street");
writer.WriteValue(c.BigNumber);
writer.WritePropertyName("Street");
writer.WriteValue(c.BigNumber);
writer.WritePropertyName("Street");
writer.WriteValue(c.BigNumber);
writer.WriteEndObject();
writer.WritePropertyName("Addresses");
writer.WriteStartArray();
foreach (Address address in c.Addresses)
{
writer.WriteStartObject();
writer.WritePropertyName("Street");
writer.WriteValue(address.Street);
writer.WritePropertyName("Phone");
writer.WriteValue(address.Phone);
writer.WritePropertyName("Entered");
writer.WriteValue(address.Entered);
writer.WriteEndObject();
}
writer.WriteEndArray();
writer.WriteEndObject();
writer.Flush();
json = sw.ToString();
}
else
{
json = string.Empty;
}
break;
}
case SerializeMethod.JsonNetBinary:
{
MemoryStream ms = new MemoryStream(Buffer);
@@ -695,6 +760,14 @@ namespace Newtonsoft.Json.Tests
BenchmarkSerializeMethod(SerializeMethod.JsonNetBinary, rootValue);
}
[Test]
public void SerializeUnicodeChars()
{
string s = (new string('\0', 30));
BenchmarkSerializeMethod(SerializeMethod.JsonNet, s);
}
[Test]
public void JObjectToString()
{
@@ -35,6 +35,8 @@ namespace Newtonsoft.Json.Utilities
{
internal static class JavaScriptUtils
{
private const string EscapedUnicodeText = "!";
public static void WriteEscapedJavaScriptString(TextWriter writer, string s, char delimiter, bool appendDelimiters)
{
// leading delimiter
@@ -44,6 +46,7 @@ namespace Newtonsoft.Json.Utilities
if (s != null)
{
char[] chars = null;
char[] unicodeBuffer = null;
int lastWritePosition = 0;
for (int i = 0; i < s.Length; i++)
@@ -94,7 +97,20 @@ namespace Newtonsoft.Json.Utilities
escapedValue = "\\\"";
break;
default:
escapedValue = (c <= '\u001f') ? StringUtils.ToCharAsUnicode(c) : null;
if (c <= '\u001f')
{
if (unicodeBuffer == null)
unicodeBuffer = new char[6];
StringUtils.ToCharAsUnicode(c, unicodeBuffer);
// slightly hacky but it saves multiple conditions in if test
escapedValue = EscapedUnicodeText;
}
else
{
escapedValue = null;
}
break;
}
@@ -111,7 +127,10 @@ namespace Newtonsoft.Json.Utilities
}
lastWritePosition = i + 1;
writer.Write(escapedValue);
if (!string.Equals(escapedValue, EscapedUnicodeText))
writer.Write(escapedValue);
else
writer.Write(unicodeBuffer);
}
if (lastWritePosition == 0)
+1 -2
View File
@@ -45,9 +45,8 @@ namespace Newtonsoft.Json.Utilities
public static char IntToHex(int n)
{
if (n <= 9)
{
return (char)(n + 48);
}
return (char)((n - 10) + 97);
}
+7 -7
View File
@@ -117,14 +117,14 @@ namespace Newtonsoft.Json.Utilities
return value.Length;
}
public static string ToCharAsUnicode(char c)
public static void ToCharAsUnicode(char c, char[] buffer)
{
char h1 = MathUtils.IntToHex((c >> 12) & '\x000f');
char h2 = MathUtils.IntToHex((c >> 8) & '\x000f');
char h3 = MathUtils.IntToHex((c >> 4) & '\x000f');
char h4 = MathUtils.IntToHex(c & '\x000f');
return new string(new[] { '\\', 'u', h1, h2, h3, h4 });
buffer[0] = '\\';
buffer[1] = 'u';
buffer[2] = MathUtils.IntToHex((c >> 12) & '\x000f');
buffer[3] = MathUtils.IntToHex((c >> 8) & '\x000f');
buffer[4] = MathUtils.IntToHex((c >> 4) & '\x000f');
buffer[5] = MathUtils.IntToHex(c & '\x000f');
}
public static TSource ForgivingCaseSensitiveFind<TSource>(this IEnumerable<TSource> source, Func<TSource, string> valueSelector, string testValue)