diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj
index 6a6bbf6f..84ac9c5f 100644
--- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj
+++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj
@@ -77,22 +77,30 @@
+
+
+
+
+
+
+
+
@@ -105,7 +113,9 @@
+
+
@@ -189,6 +199,7 @@
+
diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs
index 5c1cd929..91f5aa9d 100644
--- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs
+++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs
@@ -3,8 +3,6 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Diagnostics;
-
-using Newtonsoft.Json;
using System.IO;
using System.Web.Script.Serialization;
@@ -16,6 +14,13 @@ using Newtonsoft.Json.Bson;
namespace Newtonsoft.Json.Tests
{
+ public class Image
+ {
+ public string FileName { get; set; }
+ public string Title { get; set; }
+ public byte[] Data { get; set; }
+ }
+
public class PerformanceTests : TestFixtureBase
{
private const int Iterations = 100;
@@ -70,6 +75,50 @@ namespace Newtonsoft.Json.Tests
[Test]
public void Serialize()
+ {
+ TestClass test = CreateSerializationObject();
+
+ BenchmarkSerializeMethod(SerializeMethod.JsonNet, test);
+ BenchmarkSerializeMethod(SerializeMethod.JsonNetBinary, test);
+ BenchmarkSerializeMethod(SerializeMethod.JavaScriptSerializer, test);
+ BenchmarkSerializeMethod(SerializeMethod.DataContractJsonSerializer, test);
+ }
+
+ [Test]
+ public void Deserialize()
+ {
+ BenchmarkDeserializeMethod(SerializeMethod.JsonNet, JsonText);
+ BenchmarkDeserializeMethod(SerializeMethod.JsonNetBinary, MiscellaneousUtils.HexToBytes(BsonHex));
+ BenchmarkDeserializeMethod(SerializeMethod.JavaScriptSerializer, JsonText);
+ BenchmarkDeserializeMethod(SerializeMethod.DataContractJsonSerializer, JsonText);
+ }
+
+ [Test]
+ public void SerializeDataSize()
+ {
+ Image image = new Image();
+ image.Data = System.IO.File.ReadAllBytes(@"..\..\bunny_pancake.jpg");
+ image.FileName = "bunny_pancake.jpg";
+ image.Title = "I have no idea what you are talking about so here's a bunny with a pancake on its head";
+
+ string json = JsonConvert.SerializeObject(image, Formatting.None);
+ byte[] jsonBytes = Encoding.UTF8.GetBytes(json);
+
+ MemoryStream ms = new MemoryStream();
+ JsonSerializer serializer = new JsonSerializer();
+ BsonWriter writer = new BsonWriter(ms);
+
+ serializer.Serialize(writer, image);
+ writer.Flush();
+
+ byte[] bsonBytes = ms.ToArray();
+
+ Console.WriteLine("Image size: {0} bytes", image.Data.Length);
+ Console.WriteLine("JSON size: {0} bytes", jsonBytes.Length);
+ Console.WriteLine("BSON size: {0} bytes", bsonBytes.Length);
+ }
+
+ private TestClass CreateSerializationObject()
{
TestClass test = new TestClass();
@@ -90,20 +139,7 @@ namespace Newtonsoft.Json.Tests
address.Entered = DateTime.Now.AddDays(-2);
address.Address = "array 2 address";
test.Addresses.Add(address);
-
- BenchmarkSerializeMethod(SerializeMethod.JsonNet, test);
- BenchmarkSerializeMethod(SerializeMethod.JsonNetBinary, test);
- BenchmarkSerializeMethod(SerializeMethod.JavaScriptSerializer, test);
- BenchmarkSerializeMethod(SerializeMethod.DataContractJsonSerializer, test);
- }
-
- [Test]
- public void Deserialize()
- {
- BenchmarkDeserializeMethod(SerializeMethod.JsonNet, JsonText);
- BenchmarkDeserializeMethod(SerializeMethod.JsonNetBinary, MiscellaneousUtils.HexToBytes(BsonHex));
- BenchmarkDeserializeMethod(SerializeMethod.JavaScriptSerializer, JsonText);
- BenchmarkDeserializeMethod(SerializeMethod.DataContractJsonSerializer, JsonText);
+ return test;
}
public string SerializeJsonNet(object value)
@@ -203,33 +239,49 @@ namespace Newtonsoft.Json.Tests
return dataContractSerializer.ReadObject(ms);
}
+ private static readonly byte[] Buffer = new byte[4096];
+
+ private string Serialize(SerializeMethod method, object value)
+ {
+ string json;
+
+ switch (method)
+ {
+ case SerializeMethod.JsonNet:
+ json = JsonConvert.SerializeObject(value);
+ break;
+ case SerializeMethod.JsonNetBinary:
+ MemoryStream ms = new MemoryStream(Buffer);
+ JsonSerializer serializer = new JsonSerializer();
+ BsonWriter writer = new BsonWriter(ms);
+ serializer.Serialize(writer, value);
+
+ json = "Bytes = " + ms.Length;
+ break;
+ case SerializeMethod.JavaScriptSerializer:
+ json = SerializeWebExtensions(value);
+ break;
+ case SerializeMethod.DataContractJsonSerializer:
+ json = SerializeDataContract(value);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("method");
+ }
+
+ return json;
+ }
+
public void BenchmarkSerializeMethod(SerializeMethod method, object value)
{
+ Serialize(method, value);
+
Stopwatch timed = new Stopwatch();
timed.Start();
string json = null;
for (int x = 0; x < Iterations; x++)
{
- switch (method)
- {
- case SerializeMethod.JsonNet:
- json = JsonConvert.SerializeObject(value);
- break;
- case SerializeMethod.JsonNetBinary:
- JsonSerializer serializer = new JsonSerializer();
- MemoryStream ms = new MemoryStream();
- BsonWriter writer = new BsonWriter(ms);
- serializer.Serialize(writer, value);
- json = "{Bytes " + ms.Length + "}";
- break;
- case SerializeMethod.JavaScriptSerializer:
- json = SerializeWebExtensions(value);
- break;
- case SerializeMethod.DataContractJsonSerializer:
- json = SerializeDataContract(value);
- break;
- }
+ json = Serialize(method, value);
}
timed.Stop();
@@ -248,21 +300,7 @@ namespace Newtonsoft.Json.Tests
object value = null;
for (int x = 0; x < Iterations; x++)
{
- switch (method)
- {
- case SerializeMethod.JsonNet:
- value = DeserializeJsonNet((string)json);
- break;
- case SerializeMethod.JsonNetBinary:
- value = DeserializeJsonNetBinary((byte[])json);
- break;
- case SerializeMethod.JavaScriptSerializer:
- value = DeserializeWebExtensions((string)json);
- break;
- case SerializeMethod.DataContractJsonSerializer:
- value = DeserializeDataContract((string)json);
- break;
- }
+ value = Deserialize(method, json);
}
timed.Stop();
@@ -272,6 +310,30 @@ namespace Newtonsoft.Json.Tests
Console.WriteLine(value);
Console.WriteLine();
}
+
+ private object Deserialize(SerializeMethod method, object json)
+ {
+ object value;
+
+ switch (method)
+ {
+ case SerializeMethod.JsonNet:
+ value = DeserializeJsonNet((string) json);
+ break;
+ case SerializeMethod.JsonNetBinary:
+ value = DeserializeJsonNetBinary((byte[]) json);
+ break;
+ case SerializeMethod.JavaScriptSerializer:
+ value = DeserializeWebExtensions((string) json);
+ break;
+ case SerializeMethod.DataContractJsonSerializer:
+ value = DeserializeDataContract((string) json);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException("method");
+ }
+ return value;
+ }
}
diff --git a/Src/Newtonsoft.Json.Tests/bunny_pancake.jpg b/Src/Newtonsoft.Json.Tests/bunny_pancake.jpg
new file mode 100644
index 00000000..59e882ca
Binary files /dev/null and b/Src/Newtonsoft.Json.Tests/bunny_pancake.jpg differ
diff --git a/Src/Newtonsoft.Json/Bson/BsonWriter.cs b/Src/Newtonsoft.Json/Bson/BsonWriter.cs
index 575c5c16..1d7bc0c7 100644
--- a/Src/Newtonsoft.Json/Bson/BsonWriter.cs
+++ b/Src/Newtonsoft.Json/Bson/BsonWriter.cs
@@ -37,8 +37,13 @@ namespace Newtonsoft.Json.Bson
///
public class BsonWriter : JTokenWriter
{
+ private static readonly Encoding Encoding = Encoding.UTF8;
+
private readonly BinaryWriter _writer;
+ private byte[] _largeByteBuffer;
+ private int _maxChars;
+
///
/// Initializes a new instance of the class.
///
@@ -68,7 +73,7 @@ namespace Newtonsoft.Json.Bson
foreach (JProperty property in t)
{
_writer.Write((sbyte)GetTypeNumber(property.Value));
- WriteString(property.Name);
+ WriteString(property.Name, false);
WriteToken(property.Value);
}
_writer.Write((byte)0);
@@ -82,7 +87,7 @@ namespace Newtonsoft.Json.Bson
foreach (JToken c in t)
{
_writer.Write((sbyte)GetTypeNumber(c));
- WriteString(index.ToString(CultureInfo.InvariantCulture));
+ WriteString(index.ToString(CultureInfo.InvariantCulture), false);
WriteToken(c);
index++;
}
@@ -96,7 +101,7 @@ namespace Newtonsoft.Json.Bson
_writer.Write(Convert.ToDouble(((JValue)t).Value, CultureInfo.InvariantCulture));
break;
case JTokenType.String:
- WriteStringWithLength((string)t);
+ WriteString((string)t, true);
break;
case JTokenType.Boolean:
_writer.Write((bool)t);
@@ -121,17 +126,37 @@ namespace Newtonsoft.Json.Bson
}
}
- private void WriteString(string s)
+ private void WriteString(string s, bool includeLength)
{
- byte[] bytes = Encoding.UTF8.GetBytes(s);
- _writer.Write(bytes);
- _writer.Write((byte)0);
- }
+ int byteCount = Encoding.GetByteCount(s);
- private void WriteStringWithLength(string s)
- {
- _writer.Write(CalculateSizeWithLength(s, false));
- WriteString(s);
+ if (includeLength)
+ _writer.Write(CalculateSizeWithLength(byteCount, false));
+
+ if (_largeByteBuffer == null)
+ {
+ _largeByteBuffer = new byte[256];
+ _maxChars = 256 / Encoding.GetMaxByteCount(1);
+ }
+ if (byteCount <= 256)
+ {
+ Encoding.GetBytes(s, 0, s.Length, _largeByteBuffer, 0);
+ _writer.Write(_largeByteBuffer, 0, byteCount);
+ }
+ else
+ {
+ int charCount;
+ int totalCharsWritten = 0;
+ for (int i = s.Length; i > 0; i -= charCount)
+ {
+ charCount = (i > _maxChars) ? _maxChars : i;
+ int count = Encoding.GetBytes(s, totalCharsWritten, charCount, _largeByteBuffer, 0);
+ _writer.Write(_largeByteBuffer, 0, count);
+ totalCharsWritten += charCount;
+ }
+ }
+
+ _writer.Write((byte)0);
}
private BsonType GetTypeNumber(JToken t)
@@ -164,18 +189,21 @@ namespace Newtonsoft.Json.Bson
private int CalculateSize(string s)
{
- return Encoding.UTF8.GetByteCount(s) + 1;
+ return Encoding.GetByteCount(s) + 1;
}
private int CalculateSizeWithLength(string s, bool includeSize)
+ {
+ return CalculateSizeWithLength(Encoding.GetByteCount(s), includeSize);
+ }
+
+ private int CalculateSizeWithLength(int stringByteCount, bool includeSize)
{
int baseSize = (includeSize)
? 5 // size bytes + terminator
: 1; // terminator
- int byteCount = Encoding.UTF8.GetByteCount(s);
-
- return baseSize + byteCount;
+ return baseSize + stringByteCount;
}
private int CalculateSize(JToken t)
diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs
index 09d2ec5e..2a7ddc6f 100644
--- a/Src/Newtonsoft.Json/JsonWriter.cs
+++ b/Src/Newtonsoft.Json/JsonWriter.cs
@@ -601,9 +601,11 @@ namespace Newtonsoft.Json
WriteIndentSpace();
}
+ WriteState writeState = WriteState;
+
// don't indent a property when it is the first token to be written (i.e. at the start)
- if ((tokenBeingWritten == JsonToken.PropertyName && WriteState != WriteState.Start) ||
- WriteState == WriteState.Array || WriteState == WriteState.Constructor)
+ if ((tokenBeingWritten == JsonToken.PropertyName && writeState != WriteState.Start) ||
+ writeState == WriteState.Array || writeState == WriteState.Constructor)
{
WriteIndent();
}
diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs
index df4d9b57..ecd21917 100644
--- a/Src/Newtonsoft.Json/Linq/JContainer.cs
+++ b/Src/Newtonsoft.Json/Linq/JContainer.cs
@@ -219,8 +219,7 @@ namespace Newtonsoft.Json.Linq
{
yield return current;
}
- while (current != null
- && ((current = current.Next) != null));
+ while ((current = current.Next) != null);
}
///
diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs
index 5fb4260f..0087f473 100644
--- a/Src/Newtonsoft.Json/Linq/JObject.cs
+++ b/Src/Newtonsoft.Json/Linq/JObject.cs
@@ -105,10 +105,14 @@ namespace Newtonsoft.Json.Linq
if (o.Type != JTokenType.Property)
throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType()));
+ // looping over all properties every time isn't good
+ // need to think about performance here
JProperty property = (JProperty)o;
- bool matchingProperty = Properties().Any(p => string.Equals(p.Name, property.Name, StringComparison.Ordinal) && p != existing);
- if (matchingProperty)
- throw new ArgumentException("Can not add property {0} to {1}. Property with the same name already exists on object.".FormatWith(CultureInfo.InvariantCulture, property.Name, GetType()));
+ foreach (JProperty childProperty in Children())
+ {
+ if (childProperty != existing && string.Equals(childProperty.Name, property.Name, StringComparison.Ordinal))
+ throw new ArgumentException("Can not add property {0} to {1}. Property with the same name already exists on object.".FormatWith(CultureInfo.InvariantCulture, property.Name, GetType()));
+ }
}
internal void InternalPropertyChanged(JProperty childProperty)
diff --git a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs
index 8bfccdaf..1bb723aa 100644
--- a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs
+++ b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs
@@ -35,71 +35,68 @@ namespace Newtonsoft.Json.Utilities
{
internal static class JavaScriptUtils
{
- public static void WriteEscapedJavaScriptChar(TextWriter writer, char c, char delimiter)
- {
- switch (c)
- {
- case '\t':
- writer.Write(@"\t");
- break;
- case '\n':
- writer.Write(@"\n");
- break;
- case '\r':
- writer.Write(@"\r");
- break;
- case '\f':
- writer.Write(@"\f");
- break;
- case '\b':
- writer.Write(@"\b");
- break;
- case '\\':
- writer.Write(@"\\");
- break;
- case '\u0085': // Next Line
- writer.Write(@"\u0085");
- break;
- case '\u2028': // Line Separator
- writer.Write(@"\u2028");
- break;
- case '\u2029': // Paragraph Separator
- writer.Write(@"\u2029");
- break;
- case '\'':
- // only escape if this charater is being used as the delimiter
- writer.Write((delimiter == '\'') ? @"\'" : @"'");
- break;
- case '"':
- // only escape if this charater is being used as the delimiter
- writer.Write((delimiter == '"') ? "\\\"" : @"""");
- break;
- default:
- if (c > '\u001f')
- writer.Write(c);
- else
- StringUtils.WriteCharAsUnicode(writer, c);
- break;
- }
- }
-
- public static void WriteEscapedJavaScriptString(TextWriter writer, string value, char delimiter, bool appendDelimiters)
+ public static void WriteEscapedJavaScriptString(StringBuilder writer, string value, char delimiter, bool appendDelimiters)
{
// leading delimiter
if (appendDelimiters)
- writer.Write(delimiter);
+ writer.Append(delimiter);
if (value != null)
{
for (int i = 0; i < value.Length; i++)
{
- WriteEscapedJavaScriptChar(writer, value[i], delimiter);
+ char c = value[i];
+
+ switch (c)
+ {
+ case '\t':
+ writer.Append(@"\t");
+ break;
+ case '\n':
+ writer.Append(@"\n");
+ break;
+ case '\r':
+ writer.Append(@"\r");
+ break;
+ case '\f':
+ writer.Append(@"\f");
+ break;
+ case '\b':
+ writer.Append(@"\b");
+ break;
+ case '\\':
+ writer.Append(@"\\");
+ break;
+ case '\u0085': // Next Line
+ writer.Append(@"\u0085");
+ break;
+ case '\u2028': // Line Separator
+ writer.Append(@"\u2028");
+ break;
+ case '\u2029': // Paragraph Separator
+ writer.Append(@"\u2029");
+ break;
+ case '\'':
+ // only escape if this charater is being used as the delimiter
+ writer.Append((delimiter == '\'') ? @"\'" : @"'");
+ break;
+ case '"':
+ // only escape if this charater is being used as the delimiter
+ writer.Append((delimiter == '"') ? "\\\"" : @"""");
+ break;
+ default:
+ if (c > '\u001f')
+ writer.Append(c);
+ else
+ StringUtils.WriteCharAsUnicode(writer, c);
+ break;
+ }
}
}
// trailing delimiter
if (appendDelimiters)
- writer.Write(delimiter);
+ writer.Append(delimiter);
}
public static string ToEscapedJavaScriptString(string value)
@@ -109,11 +106,14 @@ namespace Newtonsoft.Json.Utilities
public static string ToEscapedJavaScriptString(string value, char delimiter, bool appendDelimiters)
{
- using (StringWriter w = StringUtils.CreateStringWriter(StringUtils.GetLength(value) ?? 16))
- {
- WriteEscapedJavaScriptString(w, value, delimiter, appendDelimiters);
- return w.ToString();
- }
+ int length = value.Length;
+ if (appendDelimiters)
+ length += 2;
+
+ StringBuilder builder = new StringBuilder(length);
+
+ WriteEscapedJavaScriptString(builder, value, delimiter, appendDelimiters);
+ return builder.ToString();
}
}
}
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Utilities/StringUtils.cs b/Src/Newtonsoft.Json/Utilities/StringUtils.cs
index 35d3cf18..79b19a89 100644
--- a/Src/Newtonsoft.Json/Utilities/StringUtils.cs
+++ b/Src/Newtonsoft.Json/Utilities/StringUtils.cs
@@ -325,14 +325,13 @@ namespace Newtonsoft.Json.Utilities
public static string ToCharAsUnicode(char c)
{
- using (StringWriter w = new StringWriter(CultureInfo.InvariantCulture))
- {
- WriteCharAsUnicode(w, c);
- return w.ToString();
- }
+ StringBuilder builder = new StringBuilder();
+ WriteCharAsUnicode(builder, c);
+
+ return builder.ToString();
}
- public static void WriteCharAsUnicode(TextWriter writer, char c)
+ public static void WriteCharAsUnicode(StringBuilder writer, char c)
{
ValidationUtils.ArgumentNotNull(writer, "writer");
@@ -341,12 +340,12 @@ namespace Newtonsoft.Json.Utilities
char h3 = MathUtils.IntToHex((c >> 4) & '\x000f');
char h4 = MathUtils.IntToHex(c & '\x000f');
- writer.Write('\\');
- writer.Write('u');
- writer.Write(h1);
- writer.Write(h2);
- writer.Write(h3);
- writer.Write(h4);
+ writer.Append('\\');
+ writer.Append('u');
+ writer.Append(h1);
+ writer.Append(h2);
+ writer.Append(h3);
+ writer.Append(h4);
}
public static TSource ForgivingCaseSensitiveFind(this IEnumerable source, Func valueSelector, string testValue)