diff --git a/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs
index ecc808a0..581d5109 100644
--- a/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs
+++ b/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs
@@ -841,6 +841,69 @@ _____'propertyName': NaN
Assert.AreEqual(@"""2000-01-01T01:01:01Z""", sw.ToString());
}
+ [Test]
+ public void HtmlStringEscapeHandling()
+ {
+ StringWriter sw = new StringWriter();
+ JsonTextWriter writer = new JsonTextWriter(sw)
+ {
+ StringEscapeHandling = StringEscapeHandling.EscapeHtml
+ };
+
+ string script = @"";
+
+ writer.WriteValue(script);
+
+ string json = sw.ToString();
+
+ Assert.AreEqual(@"""\u003cscript type=\u0022text/javascript\u0022\u003ealert(\u0027hi\u0027);\u003c/script\u003e""", json);
+
+ JsonTextReader reader = new JsonTextReader(new StringReader(json));
+
+ Assert.AreEqual(script, reader.ReadAsString());
+
+ //Console.WriteLine(HttpUtility.HtmlEncode(script));
+
+ //System.Web.Script.Serialization.JavaScriptSerializer s = new System.Web.Script.Serialization.JavaScriptSerializer();
+ //Console.WriteLine(s.Serialize(new { html = script }));
+ }
+
+ [Test]
+ public void NonAsciiStringEscapeHandling()
+ {
+ StringWriter sw = new StringWriter();
+ JsonTextWriter writer = new JsonTextWriter(sw)
+ {
+ StringEscapeHandling = StringEscapeHandling.EscapeNonAscii
+ };
+
+ string unicode = "\u5f20";
+
+ writer.WriteValue(unicode);
+
+ string json = sw.ToString();
+
+ Assert.AreEqual(8, json.Length);
+ Assert.AreEqual(@"""\u5f20""", json);
+
+ JsonTextReader reader = new JsonTextReader(new StringReader(json));
+
+ Assert.AreEqual(unicode, reader.ReadAsString());
+
+ sw = new StringWriter();
+ writer = new JsonTextWriter(sw)
+ {
+ StringEscapeHandling = StringEscapeHandling.Default
+ };
+
+ writer.WriteValue(unicode);
+
+ json = sw.ToString();
+
+ Assert.AreEqual(3, json.Length);
+ Assert.AreEqual("\"\u5f20\"", json);
+ }
+
[Test]
public void WriteEndOnProperty()
{
diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs
index 7b5c016c..7505d312 100644
--- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs
+++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs
@@ -291,6 +291,48 @@ namespace Newtonsoft.Json.Tests
Console.WriteLine("XML: " + watch.Elapsed.TotalSeconds);
}
+ [Test]
+ public void SerializeString()
+ {
+ string text = @"The general form of an HTML element is therefore: content.
+Some HTML elements are defined as empty elements and take the form .
+Empty elements may enclose no content, for instance, the BR tag or the inline IMG tag.
+The name of an HTML element is the name used in the tags.
+Note that the end tag's name is preceded by a slash character, ""/"", and that in empty elements the end tag is neither required nor allowed.
+If attributes are not mentioned, default values are used in each case.
+
+The general form of an HTML element is therefore: content.
+Some HTML elements are defined as empty elements and take the form .
+Empty elements may enclose no content, for instance, the BR tag or the inline IMG tag.
+The name of an HTML element is the name used in the tags.
+Note that the end tag's name is preceded by a slash character, ""/"", and that in empty elements the end tag is neither required nor allowed.
+If attributes are not mentioned, default values are used in each case.
+
+The general form of an HTML element is therefore: content.
+Some HTML elements are defined as empty elements and take the form .
+Empty elements may enclose no content, for instance, the BR tag or the inline IMG tag.
+The name of an HTML element is the name used in the tags.
+Note that the end tag's name is preceded by a slash character, ""/"", and that in empty elements the end tag is neither required nor allowed.
+If attributes are not mentioned, default values are used in each case.
+";
+
+ int interations = 1000;
+
+ TimeOperation(() =>
+ {
+ string json = null;
+ for (int i = 0; i < interations; i++)
+ {
+ using (StringWriter w = StringUtils.CreateStringWriter(StringUtils.GetLength(text) ?? 16))
+ {
+ JavaScriptUtils.WriteEscapedJavaScriptString(w, text, '"', true, JavaScriptUtils.DoubleQuoteCharEscapeFlags, StringEscapeHandling.Default);
+ }
+ }
+
+ return "";
+ }, "New");
+ }
+
private void SerializeSize(object value)
{
// this is extremely slow with 5000 interations
diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs
index de5e070e..676a0eb9 100644
--- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs
+++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs
@@ -76,5 +76,5 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("4.5.0.0")]
#if !PocketPC
-[assembly: AssemblyFileVersion("4.5.10.15512")]
+[assembly: AssemblyFileVersion("4.5.10.15518")]
#endif
diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs
index 8c9fab92..7333aa27 100644
--- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs
+++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs
@@ -7154,6 +7154,68 @@ Parameter name: value",
Assert.IsNull(c2.MyNullableDecimal);
}
#endif
+
+#if !(PORTABLE || NET20 || NET35 || WINDOWS_PHONE)
+ [Test]
+ public void HashSetInterface()
+ {
+ ISet s1 = new HashSet(new[] {"1", "two", "III"});
+
+ string json = JsonConvert.SerializeObject(s1);
+
+ ISet s2 = JsonConvert.DeserializeObject>(json);
+
+ Assert.AreEqual(s1.Count, s2.Count);
+ foreach (string s in s1)
+ {
+ Assert.IsTrue(s2.Contains(s));
+ }
+ }
+#endif
+
+ public class NewEmployee : Employee
+ {
+ public int Age { get; set; }
+
+ public bool ShouldSerializeName()
+ {
+ return false;
+ }
+ }
+
+ [Test]
+ public void ShouldSerializeInheritedClassTest()
+ {
+ NewEmployee joe = new NewEmployee();
+ joe.Name = "Joe Employee";
+ joe.Age = 100;
+
+ Employee mike = new Employee();
+ mike.Name = "Mike Manager";
+ mike.Manager = mike;
+
+ joe.Manager = mike;
+
+ //StringWriter sw = new StringWriter();
+
+ //XmlSerializer x = new XmlSerializer(typeof(NewEmployee));
+ //x.Serialize(sw, joe);
+
+ //Console.WriteLine(sw);
+
+ //JavaScriptSerializer s = new JavaScriptSerializer();
+ //Console.WriteLine(s.Serialize(new {html = @"; & ! ^ * ( ) ! @ # $ % ^ ' "" - , . / ; : [ { } ] ; ' - _ = + ? ` ~ \ |"}));
+
+ string json = JsonConvert.SerializeObject(joe, Formatting.Indented);
+
+ Assert.AreEqual(@"{
+ ""Age"": 100,
+ ""Name"": ""Joe Employee"",
+ ""Manager"": {
+ ""Name"": ""Mike Manager""
+ }
+}", json);
+ }
}
#if !(SILVERLIGHT || NETFX_CORE || PORTABLE)
diff --git a/Src/Newtonsoft.Json.Tests/Serialization/ReferenceLoopHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/ReferenceLoopHandlingTests.cs
index 17800bbf..940abc1f 100644
--- a/Src/Newtonsoft.Json.Tests/Serialization/ReferenceLoopHandlingTests.cs
+++ b/Src/Newtonsoft.Json.Tests/Serialization/ReferenceLoopHandlingTests.cs
@@ -23,6 +23,7 @@
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
+using System;
using System.Collections.Generic;
#if !NETFX_CORE
using NUnit.Framework;
@@ -31,6 +32,11 @@ using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute;
using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute;
#endif
+#if !(NET20 || NET35 || WINDOWS_PHONE)
+using System.Dynamic;
+#endif
+using System.Runtime.Serialization;
+using Newtonsoft.Json.Tests.Linq;
namespace Newtonsoft.Json.Tests.Serialization
{
@@ -142,6 +148,126 @@ namespace Newtonsoft.Json.Tests.Serialization
]
}", json);
}
+
+#if !(PORTABLE || SILVERLIGHT)
+ public class MainClass : ISerializable
+ {
+ public ChildClass Child { get; set; }
+
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue("Child", Child);
+ }
+ }
+
+ public class ChildClass : ISerializable
+ {
+ public string Name { get; set; }
+ public MainClass Parent { get; set; }
+
+ public void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue("Parent", Parent);
+ info.AddValue("Name", Name);
+ }
+ }
+
+ [Test]
+ public void ErrorISerializableCyclicReferenceLoop()
+ {
+ var main = new MainClass();
+ var child = new ChildClass();
+
+ child.Name = "Child1";
+ child.Parent = main; // Obvious Circular Reference
+
+ main.Child = child;
+
+ var settings =
+ new JsonSerializerSettings();
+
+ ExceptionAssert.Throws(
+ "Self referencing loop detected with type 'Newtonsoft.Json.Tests.Serialization.ReferenceLoopHandlingTests+MainClass'. Path 'Child'.",
+ () => JsonConvert.SerializeObject(main, settings));
+ }
+
+ [Test]
+ public void IgnoreISerializableCyclicReferenceLoop()
+ {
+ var main = new MainClass();
+ var child = new ChildClass();
+
+ child.Name = "Child1";
+ child.Parent = main; // Obvious Circular Reference
+
+ main.Child = child;
+
+ var settings =
+ new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
+
+ var c = JsonConvert.SerializeObject(main, settings);
+ Assert.AreEqual(@"{""Child"":{""Name"":""Child1""}}", c);
+ }
+#endif
+
+#if !(PORTABLE || NET20 || NET35 || WINDOWS_PHONE)
+ public class DictionaryDynamicObject : DynamicObject
+ {
+ public IDictionary Values { get; private set; }
+
+ public DictionaryDynamicObject()
+ {
+ Values = new Dictionary();
+ }
+
+ public override bool TrySetMember(SetMemberBinder binder, object value)
+ {
+ Values[binder.Name] = value;
+ return true;
+ }
+
+ public override bool TryGetMember(GetMemberBinder binder, out object result)
+ {
+ return Values.TryGetValue(binder.Name, out result);
+ }
+
+ public override IEnumerable GetDynamicMemberNames()
+ {
+ return Values.Keys;
+ }
+ }
+
+ [Test]
+ public void ErrorDynamicCyclicReferenceLoop()
+ {
+ dynamic parent = new DictionaryDynamicObject();
+ dynamic child = new DictionaryDynamicObject();
+ parent.child = child;
+ child.parent = parent;
+
+ var settings = new JsonSerializerSettings();
+
+ ExceptionAssert.Throws(
+ "Self referencing loop detected with type 'Newtonsoft.Json.Tests.Serialization.ReferenceLoopHandlingTests+DictionaryDynamicObject'. Path 'child'.",
+ () => JsonConvert.SerializeObject(parent, settings));
+ }
+
+ [Test]
+ public void IgnoreDynamicCyclicReferenceLoop()
+ {
+ dynamic parent = new DictionaryDynamicObject();
+ dynamic child = new DictionaryDynamicObject();
+ parent.child = child;
+ parent.name = "parent";
+ child.parent = parent;
+ child.name = "child";
+
+ var settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
+
+ var c = JsonConvert.SerializeObject(parent, settings);
+ Assert.AreEqual(@"{""child"":{""name"":""child""},""name"":""parent""}", c);
+ }
+#endif
}
public class PropertyItemReferenceLoopHandling
diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs
index 2a651de8..7cf4ab85 100644
--- a/Src/Newtonsoft.Json/JsonConvert.cs
+++ b/Src/Newtonsoft.Json/JsonConvert.cs
@@ -556,11 +556,14 @@ namespace Newtonsoft.Json
/// Converts the to its JSON string representation.
///
/// The value to convert.
- /// The string delimiter character.
+ /// The string delimiter character.
/// A JSON string representation of the .
- public static string ToString(string value, char delimter)
+ public static string ToString(string value, char delimiter)
{
- return JavaScriptUtils.ToEscapedJavaScriptString(value, delimter, true);
+ if (delimiter != '"' && delimiter != '\'')
+ throw new ArgumentException("Delimiter must be a single or double quote.", "delimiter");
+
+ return JavaScriptUtils.ToEscapedJavaScriptString(value, delimiter, true);
}
///
diff --git a/Src/Newtonsoft.Json/JsonTextWriter.cs b/Src/Newtonsoft.Json/JsonTextWriter.cs
index 68ab5ebb..77533155 100644
--- a/Src/Newtonsoft.Json/JsonTextWriter.cs
+++ b/Src/Newtonsoft.Json/JsonTextWriter.cs
@@ -206,11 +206,22 @@ namespace Newtonsoft.Json
{
InternalWritePropertyName(name);
- JavaScriptUtils.WriteEscapedJavaScriptString(_writer, name, _quoteChar, _quoteName);
+ JavaScriptUtils.WriteEscapedJavaScriptString(_writer, name, _quoteChar, _quoteName, GetCharEscapeFlags(), StringEscapeHandling);
_writer.Write(':');
}
+ private bool[] GetCharEscapeFlags()
+ {
+ if (StringEscapeHandling == StringEscapeHandling.EscapeHtml)
+ return JavaScriptUtils.HtmlCharEscapeFlags;
+
+ if (_quoteChar == '"')
+ return JavaScriptUtils.DoubleQuoteCharEscapeFlags;
+
+ return JavaScriptUtils.SingleQuoteCharEscapeFlags;
+ }
+
///
/// Writes indent characters.
///
@@ -294,7 +305,7 @@ namespace Newtonsoft.Json
if (value == null)
WriteValueInternal(JsonConvert.Null, JsonToken.Null);
else
- JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true);
+ JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true, GetCharEscapeFlags(), StringEscapeHandling);
}
///
diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs
index 0a1fa296..1c555afb 100644
--- a/Src/Newtonsoft.Json/JsonWriter.cs
+++ b/Src/Newtonsoft.Json/JsonWriter.cs
@@ -209,6 +209,7 @@ namespace Newtonsoft.Json
private DateFormatHandling _dateFormatHandling;
private DateTimeZoneHandling _dateTimeZoneHandling;
+ private StringEscapeHandling _stringEscapeHandling;
///
/// Indicates how JSON text output is formatted.
@@ -229,7 +230,7 @@ namespace Newtonsoft.Json
}
///
- /// Get or set how time zones are handling when writing JSON.
+ /// Get or set how time zones are handling when writing JSON text.
///
public DateTimeZoneHandling DateTimeZoneHandling
{
@@ -237,6 +238,15 @@ namespace Newtonsoft.Json
set { _dateTimeZoneHandling = value; }
}
+ ///
+ /// Get or set how strings are escaped when writing JSON text.
+ ///
+ public StringEscapeHandling StringEscapeHandling
+ {
+ get { return _stringEscapeHandling; }
+ set { _stringEscapeHandling = value; }
+ }
+
///
/// Creates an instance of the JsonWriter class.
///
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj
index d60c32ee..ba2a7d1e 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj
@@ -109,6 +109,7 @@
+
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj
index 500953ae..85281869 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj
@@ -121,6 +121,7 @@
+
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj
index 8cccf090..fa32a3a1 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj
@@ -175,6 +175,7 @@
+
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj
index 483ae3fd..e74417e7 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj
@@ -197,6 +197,7 @@
+
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj
index ef4f4a77..220638f0 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj
@@ -170,6 +170,7 @@
+
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
index b1295ef0..756a6507 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
@@ -117,6 +117,7 @@
+
diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs
index 837296a6..b8cabcce 100644
--- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs
+++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs
@@ -90,7 +90,7 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("4.5.0.0")]
#if !PocketPC
-[assembly: AssemblyFileVersion("4.5.10.15512")]
+[assembly: AssemblyFileVersion("4.5.10.15518")]
#endif
[assembly: CLSCompliant(true)]
diff --git a/Src/Newtonsoft.Json/Serialization/DiagnosticsTraceWriter.cs b/Src/Newtonsoft.Json/Serialization/DiagnosticsTraceWriter.cs
index ea6fce9f..f9555b98 100644
--- a/Src/Newtonsoft.Json/Serialization/DiagnosticsTraceWriter.cs
+++ b/Src/Newtonsoft.Json/Serialization/DiagnosticsTraceWriter.cs
@@ -5,8 +5,19 @@ using DiagnosticsTrace = System.Diagnostics.Trace;
namespace Newtonsoft.Json.Serialization
{
+ ///
+ /// Represents a trace writer that writes to the application's instances.
+ ///
public class DiagnosticsTraceWriter : ITraceWriter
{
+ ///
+ /// Gets the that will be used to filter the trace messages passed to the writer.
+ /// For example a filter level of Info will exclude Verbose messages and include Info,
+ /// Warning and Error messages.
+ ///
+ ///
+ /// The that will be used to filter the trace messages passed to the writer.
+ ///
public TraceLevel LevelFilter { get; set; }
private TraceEventType GetTraceEventType(TraceLevel level)
@@ -26,6 +37,12 @@ namespace Newtonsoft.Json.Serialization
}
}
+ ///
+ /// Writes the specified trace level, message and optional exception.
+ ///
+ /// The at which to write this trace.
+ /// The trace message.
+ /// The trace exception. This parameter is optional.
public void Trace(TraceLevel level, string message, Exception ex)
{
if (level == TraceLevel.Off)
diff --git a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs
index b5952e9b..14698ef8 100644
--- a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs
+++ b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs
@@ -85,9 +85,11 @@ namespace Newtonsoft.Json.Serialization
_isCollectionItemTypeNullableType = ReflectionUtils.IsNullableType(CollectionItemType);
if (IsTypeGenericCollectionInterface(UnderlyingType))
- {
CreatedType = ReflectionUtils.MakeGenericType(typeof(List<>), CollectionItemType);
- }
+#if !(PORTABLE || NET20 || NET35 || WINDOWS_PHONE)
+ else if (IsTypeGenericSetnterface(UnderlyingType))
+ CreatedType = ReflectionUtils.MakeGenericType(typeof(HashSet<>), CollectionItemType);
+#endif
IsMultidimensionalArray = (UnderlyingType.IsArray && UnderlyingType.GetArrayRank() > 1);
}
@@ -152,5 +154,17 @@ namespace Newtonsoft.Json.Serialization
|| genericDefinition == typeof(ICollection<>)
|| genericDefinition == typeof(IEnumerable<>));
}
+
+#if !(PORTABLE || NET20 || NET35 || WINDOWS_PHONE)
+ private bool IsTypeGenericSetnterface(Type type)
+ {
+ if (!type.IsGenericType())
+ return false;
+
+ Type genericDefinition = type.GetGenericTypeDefinition();
+
+ return (genericDefinition == typeof(ISet<>));
+ }
+#endif
}
}
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs b/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs
index 560ef74d..d99a5f8b 100644
--- a/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs
+++ b/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs
@@ -31,7 +31,7 @@ namespace Newtonsoft.Json.Serialization
///
/// Contract details for a used by the .
///
- public class JsonISerializableContract : JsonContract
+ public class JsonISerializableContract : JsonContainerContract
{
///
/// Gets or sets the ISerializable object constructor.
diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs
index f51d5bf0..400cd3ab 100644
--- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs
+++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs
@@ -682,8 +682,13 @@ To fix this error either change the environment to be fully trusted, change the
foreach (SerializationEntry serializationEntry in serializationInfo)
{
- writer.WritePropertyName(serializationEntry.Name);
- SerializeValue(writer, serializationEntry.Value, GetContractSafe(serializationEntry.Value), null, null, member);
+ JsonContract valueContract = GetContractSafe(serializationEntry.Value);
+
+ if (CheckForCircularReference(writer, serializationEntry.Value, null, valueContract, contract, member))
+ {
+ writer.WritePropertyName(serializationEntry.Name);
+ SerializeValue(writer, serializationEntry.Value, valueContract, null, contract, member);
+ }
}
writer.WriteEndObject();
@@ -736,12 +741,17 @@ To fix this error either change the environment to be fully trusted, change the
{
try
{
- string resolvedPropertyName = (contract.PropertyNameResolver != null)
- ? contract.PropertyNameResolver(memberName)
- : memberName;
+ JsonContract valueContract = GetContractSafe(memberValue);
- writer.WritePropertyName(resolvedPropertyName);
- SerializeValue(writer, memberValue, GetContractSafe(memberValue), null, null, member);
+ if (CheckForCircularReference(writer, memberValue, null, valueContract, contract, member))
+ {
+ string resolvedPropertyName = (contract.PropertyNameResolver != null)
+ ? contract.PropertyNameResolver(memberName)
+ : memberName;
+
+ writer.WritePropertyName(resolvedPropertyName);
+ SerializeValue(writer, memberValue, valueContract, null, contract, member);
+ }
}
catch (Exception ex)
{
diff --git a/Src/Newtonsoft.Json/Serialization/MemoryTraceWriter.cs b/Src/Newtonsoft.Json/Serialization/MemoryTraceWriter.cs
index 768684ff..7b639db5 100644
--- a/Src/Newtonsoft.Json/Serialization/MemoryTraceWriter.cs
+++ b/Src/Newtonsoft.Json/Serialization/MemoryTraceWriter.cs
@@ -5,18 +5,39 @@ using System.Text;
namespace Newtonsoft.Json.Serialization
{
+ ///
+ /// Represents a trace writer that writes to memory. When the trace message limit is
+ /// reached then old trace messages will be removed as new messages are added.
+ ///
public class MemoryTraceWriter : ITraceWriter
{
private readonly Queue _traceMessages;
-
+
+ ///
+ /// Gets the that will be used to filter the trace messages passed to the writer.
+ /// For example a filter level of Info will exclude Verbose messages and include Info,
+ /// Warning and Error messages.
+ ///
+ ///
+ /// The that will be used to filter the trace messages passed to the writer.
+ ///
public TraceLevel LevelFilter { get; set; }
-
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
public MemoryTraceWriter()
{
LevelFilter = TraceLevel.Verbose;
_traceMessages = new Queue();
}
+ ///
+ /// Writes the specified trace level, message and optional exception.
+ ///
+ /// The at which to write this trace.
+ /// The trace message.
+ /// The trace exception. This parameter is optional.
public void Trace(TraceLevel level, string message, Exception ex)
{
string traceMessage = DateTime.Now.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff") + " " + level.ToString("g") + " " + message;
@@ -27,11 +48,21 @@ namespace Newtonsoft.Json.Serialization
_traceMessages.Enqueue(traceMessage);
}
+ ///
+ /// Returns an enumeration of the most recent trace messages.
+ ///
+ /// An enumeration of the most recent trace messages.
public IEnumerable GetTraceMessages()
{
return _traceMessages;
}
+ ///
+ /// Returns a of the most recent trace messages.
+ ///
+ ///
+ /// A of the most recent trace messages.
+ ///
public override string ToString()
{
StringBuilder sb = new StringBuilder();
diff --git a/Src/Newtonsoft.Json/StringEscapeHandling.cs b/Src/Newtonsoft.Json/StringEscapeHandling.cs
new file mode 100644
index 00000000..cf530a17
--- /dev/null
+++ b/Src/Newtonsoft.Json/StringEscapeHandling.cs
@@ -0,0 +1,46 @@
+#region License
+// Copyright (c) 2007 James Newton-King
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+#endregion
+
+namespace Newtonsoft.Json
+{
+ ///
+ /// Specifies how strings are escaped when writing JSON text.
+ ///
+ public enum StringEscapeHandling
+ {
+ ///
+ /// Only control characters (e.g. newline) are escaped.
+ ///
+ Default,
+ ///
+ /// All non-ASCII and control characters (e.g. newline) are escaped.
+ ///
+ EscapeNonAscii,
+ ///
+ /// HTML (<, >, &, ', ") and control characters (e.g. newline) are escaped.
+ ///
+ EscapeHtml
+ }
+}
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs
index e17fc259..4247882f 100644
--- a/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs
+++ b/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs
@@ -30,14 +30,48 @@ using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
+#if NET20
+using Newtonsoft.Json.Utilities.LinqBridge;
+#else
+using System.Linq;
+#endif
namespace Newtonsoft.Json.Utilities
{
internal static class JavaScriptUtils
{
+ internal static readonly bool[] SingleQuoteCharEscapeFlags = new bool[128];
+ internal static readonly bool[] DoubleQuoteCharEscapeFlags = new bool[128];
+ internal static readonly bool[] HtmlCharEscapeFlags = new bool[128];
+
+ static JavaScriptUtils()
+ {
+ IList escapeChars = new List
+ {
+ '\n', '\r', '\t', '\\', '\f', '\b',
+ };
+ for (int i = 0; i < ' '; i++)
+ {
+ escapeChars.Add((char)i);
+ }
+
+ foreach (var escapeChar in escapeChars.Union(new[] { '\'' }))
+ {
+ SingleQuoteCharEscapeFlags[escapeChar] = true;
+ }
+ foreach (var escapeChar in escapeChars.Union(new[] { '"' }))
+ {
+ DoubleQuoteCharEscapeFlags[escapeChar] = true;
+ }
+ foreach (var escapeChar in escapeChars.Union(new[] { '"', '\'', '<', '>', '&' }))
+ {
+ HtmlCharEscapeFlags[escapeChar] = true;
+ }
+ }
+
private const string EscapedUnicodeText = "!";
- public static void WriteEscapedJavaScriptString(TextWriter writer, string s, char delimiter, bool appendDelimiters)
+ public static void WriteEscapedJavaScriptString(TextWriter writer, string s, char delimiter, bool appendDelimiters, bool[] charEscapeFlags, StringEscapeHandling stringEscapeHandling)
{
// leading delimiter
if (appendDelimiters)
@@ -53,8 +87,7 @@ namespace Newtonsoft.Json.Utilities
{
var c = s[i];
- // don't escape standard text/numbers except '\' and the text delimiter
- if (c >= ' ' && c < 128 && c != '\\' && c != delimiter)
+ if (c < charEscapeFlags.Length && !charEscapeFlags[c])
continue;
string escapedValue;
@@ -88,24 +121,27 @@ namespace Newtonsoft.Json.Utilities
case '\u2029': // Paragraph Separator
escapedValue = @"\u2029";
break;
- case '\'':
- // this charater is being used as the delimiter
- escapedValue = @"\'";
- break;
- case '"':
- // this charater is being used as the delimiter
- escapedValue = "\\\"";
- break;
default:
- if (c <= '\u001f')
+ if (c < charEscapeFlags.Length || stringEscapeHandling == StringEscapeHandling.EscapeNonAscii)
{
- if (unicodeBuffer == null)
- unicodeBuffer = new char[6];
+ if (c == '\'' && stringEscapeHandling != StringEscapeHandling.EscapeHtml)
+ {
+ escapedValue = @"\'";
+ }
+ else if (c == '"' && stringEscapeHandling != StringEscapeHandling.EscapeHtml)
+ {
+ escapedValue = @"\""";
+ }
+ else
+ {
+ if (unicodeBuffer == null)
+ unicodeBuffer = new char[6];
- StringUtils.ToCharAsUnicode(c, unicodeBuffer);
+ StringUtils.ToCharAsUnicode(c, unicodeBuffer);
- // slightly hacky but it saves multiple conditions in if test
- escapedValue = EscapedUnicodeText;
+ // slightly hacky but it saves multiple conditions in if test
+ escapedValue = EscapedUnicodeText;
+ }
}
else
{
@@ -162,7 +198,7 @@ namespace Newtonsoft.Json.Utilities
{
using (StringWriter w = StringUtils.CreateStringWriter(StringUtils.GetLength(value) ?? 16))
{
- WriteEscapedJavaScriptString(w, value, delimiter, appendDelimiters);
+ WriteEscapedJavaScriptString(w, value, delimiter, appendDelimiters, (delimiter == '"') ? DoubleQuoteCharEscapeFlags : SingleQuoteCharEscapeFlags, StringEscapeHandling.Default);
return w.ToString();
}
}