diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index c1723ecf..0eb49f61 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -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 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() { diff --git a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs index 5754334d..e17fc259 100644 --- a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs @@ -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) diff --git a/Src/Newtonsoft.Json/Utilities/MathUtils.cs b/Src/Newtonsoft.Json/Utilities/MathUtils.cs index 366f6283..c109de70 100644 --- a/Src/Newtonsoft.Json/Utilities/MathUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/MathUtils.cs @@ -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); } diff --git a/Src/Newtonsoft.Json/Utilities/StringUtils.cs b/Src/Newtonsoft.Json/Utilities/StringUtils.cs index 7a02ac25..9e824794 100644 --- a/Src/Newtonsoft.Json/Utilities/StringUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/StringUtils.cs @@ -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(this IEnumerable source, Func valueSelector, string testValue)