From aab01d93ca0cba4c3d4554a53ff45cd4b11a8970 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Mon, 11 Mar 2013 19:23:23 +1300 Subject: [PATCH] -Added IConvertible support to JValue -Changed IDictionary to be serialized in ISO date format -Fixed DataContractAttribute to not be inherited --- Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs | 140 ++++++++++++++ .../Newtonsoft.Json.Tests.Net20.csproj | 2 +- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 129 ++++++++++++- .../SerializationEventAttributeTests.cs | 174 +++++++++++++++++- Src/Newtonsoft.Json/JsonConvert.cs | 146 +++++++++++++-- Src/Newtonsoft.Json/JsonTextReader.cs | 126 ++----------- Src/Newtonsoft.Json/JsonTextWriter.cs | 30 ++- Src/Newtonsoft.Json/JsonWriter.cs | 5 + Src/Newtonsoft.Json/Linq/JValue.cs | 90 ++++++++- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/DefaultContractResolver.cs | 3 - .../JsonSerializerInternalReader.cs | 13 +- .../JsonSerializerInternalWriter.cs | 45 ++++- .../Serialization/JsonTypeReflector.cs | 13 +- 15 files changed, 760 insertions(+), 160 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs index eca54882..ba7b567a 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs @@ -397,5 +397,145 @@ namespace Newtonsoft.Json.Tests.Linq CustomAssert.IsInstanceOfType(typeof(DateTimeOffset), startDateTime); } #endif + + [Test] + public void ConvertsToBoolean() + { + Assert.AreEqual(true, Convert.ToBoolean(new JValue(true))); + } + + [Test] + public void ConvertsToBoolean_String() + { + Assert.AreEqual(true, Convert.ToBoolean(new JValue("true"))); + } + + [Test] + public void ConvertsToInt32() + { + Assert.AreEqual(Int32.MaxValue, Convert.ToInt32(new JValue(Int32.MaxValue))); + } + + [Test] + public void ConvertsToChar() + { + Assert.AreEqual('c', Convert.ToChar(new JValue('c'))); + } + + [Test] + public void ConvertsToSByte() + { + Assert.AreEqual(SByte.MaxValue, Convert.ToSByte(new JValue(SByte.MaxValue))); + } + + [Test] + public void ConvertsToByte() + { + Assert.AreEqual(Byte.MaxValue, Convert.ToByte(new JValue(Byte.MaxValue))); + } + + [Test] + public void ConvertsToInt16() + { + Assert.AreEqual(Int16.MaxValue, Convert.ToInt16(new JValue(Int16.MaxValue))); + } + + [Test] + public void ConvertsToUInt16() + { + Assert.AreEqual(UInt16.MaxValue, Convert.ToUInt16(new JValue(UInt16.MaxValue))); + } + + [Test] + public void ConvertsToUInt32() + { + Assert.AreEqual(UInt32.MaxValue, Convert.ToUInt32(new JValue(UInt32.MaxValue))); + } + + [Test] + public void ConvertsToInt64() + { + Assert.AreEqual(Int64.MaxValue, Convert.ToInt64(new JValue(Int64.MaxValue))); + } + + [Test] + public void ConvertsToUInt64() + { + Assert.AreEqual(UInt64.MaxValue, Convert.ToUInt64(new JValue(UInt64.MaxValue))); + } + + [Test] + public void ConvertsToSingle() + { + Assert.AreEqual(Single.MaxValue, Convert.ToSingle(new JValue(Single.MaxValue))); + } + + [Test] + public void ConvertsToDouble() + { + Assert.AreEqual(Double.MaxValue, Convert.ToDouble(new JValue(Double.MaxValue))); + } + + [Test] + public void ConvertsToDecimal() + { + Assert.AreEqual(Decimal.MaxValue, Convert.ToDecimal(new JValue(Decimal.MaxValue))); + } + + [Test] + public void ConvertsToString_Decimal() + { + Assert.AreEqual("79228162514264337593543950335", Convert.ToString(new JValue(Decimal.MaxValue))); + } + + [Test] + public void ConvertsToString_Uri() + { + Assert.AreEqual("http://www.google.com/", Convert.ToString(new JValue(new Uri("http://www.google.com")))); + } + + [Test] + public void ConvertsToString_Null() + { + Assert.AreEqual(string.Empty, Convert.ToString(new JValue((object)null))); + } + + [Test] + public void ConvertsToString_Guid() + { + Guid g = new Guid("0B5D4F85-E94C-4143-94C8-35F2AAEBB100"); + + Assert.AreEqual("0b5d4f85-e94c-4143-94c8-35f2aaebb100", Convert.ToString(new JValue(g))); + } + + [Test] + public void ConvertsToType() + { + Assert.AreEqual(Int32.MaxValue, Convert.ChangeType(new JValue(Int32.MaxValue), typeof(Int32), CultureInfo.InvariantCulture)); + } + + [Test] + public void ConvertsToDateTime() + { + Assert.AreEqual(new DateTime(2013, 02, 01, 01, 02, 03, 04), Convert.ToDateTime(new JValue(new DateTime(2013, 02, 01, 01, 02, 03, 04)))); + } + +#if !NET20 + [Test] + public void ConvertsToDateTime_DateTimeOffset() + { + var offset = new DateTimeOffset(2013, 02, 01, 01, 02, 03, 04, TimeSpan.Zero); + + Assert.AreEqual(new DateTime(2013, 02, 01, 01, 02, 03, 04), Convert.ToDateTime(new JValue(offset))); + } +#endif + + [Test] + public void GetTypeCode() + { + IConvertible v = new JValue(new Guid("0B5D4F85-E94C-4143-94C8-35F2AAEBB100")); + + Assert.AreEqual(TypeCode.Object, v.GetTypeCode()); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj index 9fc726da..90d16248 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj @@ -57,7 +57,7 @@ pdbonly true bin\Release\Net20\ - TRACE + TRACE;NET20 prompt 4 AllRules.ruleset diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 32b78551..fcb92db3 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -73,4 +73,4 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("4.5.11.15906")] +[assembly: AssemblyFileVersion("4.5.11.15911")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index ee89fbc6..d49942ec 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -4923,6 +4923,25 @@ To fix this error either change the environment to be fully trusted, change the [Test] public void ChildDataContractTest() + { + ChildDataContract cc = new ChildDataContract + { + VirtualMember = "VirtualMember!", + NonVirtualMember = "NonVirtualMember!" + }; + + string result = JsonConvert.SerializeObject(cc, Formatting.Indented); +// Assert.AreEqual(@"{ +// ""VirtualMember"": ""VirtualMember!"", +// ""NewMember"": null, +// ""nonVirtualMember"": ""NonVirtualMember!"" +//}", result); + + Console.WriteLine(result); + } + + [Test] + public void ChildDataContractTestWithDataContractSerializer() { ChildDataContract cc = new ChildDataContract { @@ -4930,8 +4949,14 @@ To fix this error either change the environment to be fully trusted, change the NonVirtualMember = "NonVirtualMember!" }; - string result = JsonConvert.SerializeObject(cc); - Assert.AreEqual(@"{""virtualMember"":""VirtualMember!"",""nonVirtualMember"":""NonVirtualMember!""}", result); + DataContractSerializer serializer = new DataContractSerializer(typeof(ChildDataContract)); + + MemoryStream ms = new MemoryStream(); + serializer.WriteObject(ms, cc); + + string xml = Encoding.UTF8.GetString(ms.ToArray(), 0, Convert.ToInt32(ms.Length)); + + Console.WriteLine(xml); } #endif @@ -6226,6 +6251,106 @@ Parameter name: value", Assert.AreEqual(@"{""Data"":["":::ONE:::"","":::TWO:::"","":::THREE:::""]}", json); } +#if !NET20 + [Test] + public void DateTimeDictionaryKey_DateTimeOffset_Iso() + { + IDictionary dic1 = new Dictionary + { + {new DateTimeOffset(2000, 12, 12, 12, 12, 12, TimeSpan.Zero), 1}, + {new DateTimeOffset(2013, 12, 12, 12, 12, 12, TimeSpan.Zero), 2} + }; + + string json = JsonConvert.SerializeObject(dic1, Formatting.Indented); + + Assert.AreEqual(@"{ + ""2000-12-12T12:12:12+00:00"": 1, + ""2013-12-12T12:12:12+00:00"": 2 +}", json); + + IDictionary dic2 = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(2, dic2.Count); + Assert.AreEqual(1, dic2[new DateTimeOffset(2000, 12, 12, 12, 12, 12, TimeSpan.Zero)]); + Assert.AreEqual(2, dic2[new DateTimeOffset(2013, 12, 12, 12, 12, 12, TimeSpan.Zero)]); + } + + [Test] + public void DateTimeDictionaryKey_DateTimeOffset_MS() + { + IDictionary dic1 = new Dictionary + { + {new DateTimeOffset(2000, 12, 12, 12, 12, 12, TimeSpan.Zero), 1}, + {new DateTimeOffset(2013, 12, 12, 12, 12, 12, TimeSpan.Zero), 2} + }; + + string json = JsonConvert.SerializeObject(dic1, Formatting.Indented, new JsonSerializerSettings + { + DateFormatHandling = DateFormatHandling.MicrosoftDateFormat + }); + + Assert.AreEqual(@"{ + ""\/Date(976623132000+0000)\/"": 1, + ""\/Date(1386850332000+0000)\/"": 2 +}", json); + + IDictionary dic2 = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(2, dic2.Count); + Assert.AreEqual(1, dic2[new DateTimeOffset(2000, 12, 12, 12, 12, 12, TimeSpan.Zero)]); + Assert.AreEqual(2, dic2[new DateTimeOffset(2013, 12, 12, 12, 12, 12, TimeSpan.Zero)]); + } +#endif + + [Test] + public void DateTimeDictionaryKey_DateTime_Iso() + { + IDictionary dic1 = new Dictionary + { + {new DateTime(2000, 12, 12, 12, 12, 12, DateTimeKind.Utc), 1}, + {new DateTime(2013, 12, 12, 12, 12, 12, DateTimeKind.Utc), 2} + }; + + string json = JsonConvert.SerializeObject(dic1, Formatting.Indented); + + Assert.AreEqual(@"{ + ""2000-12-12T12:12:12Z"": 1, + ""2013-12-12T12:12:12Z"": 2 +}", json); + + IDictionary dic2 = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(2, dic2.Count); + Assert.AreEqual(1, dic2[new DateTime(2000, 12, 12, 12, 12, 12, DateTimeKind.Utc)]); + Assert.AreEqual(2, dic2[new DateTime(2013, 12, 12, 12, 12, 12, DateTimeKind.Utc)]); + } + + [Test] + public void DateTimeDictionaryKey_DateTime_MS() + { + IDictionary dic1 = new Dictionary + { + {new DateTime(2000, 12, 12, 12, 12, 12, DateTimeKind.Utc), 1}, + {new DateTime(2013, 12, 12, 12, 12, 12, DateTimeKind.Utc), 2} + }; + + string json = JsonConvert.SerializeObject(dic1, Formatting.Indented, new JsonSerializerSettings + { + DateFormatHandling = DateFormatHandling.MicrosoftDateFormat + }); + + Assert.AreEqual(@"{ + ""\/Date(976623132000)\/"": 1, + ""\/Date(1386850332000)\/"": 2 +}", json); + + IDictionary dic2 = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(2, dic2.Count); + Assert.AreEqual(1, dic2[new DateTime(2000, 12, 12, 12, 12, 12, DateTimeKind.Utc)]); + Assert.AreEqual(2, dic2[new DateTime(2013, 12, 12, 12, 12, 12, DateTimeKind.Utc)]); + } + [Test] public void DeserializeEmptyJsonString() { diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SerializationEventAttributeTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SerializationEventAttributeTests.cs index 105f6072..491993d6 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SerializationEventAttributeTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SerializationEventAttributeTests.cs @@ -24,10 +24,17 @@ #endregion using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; +#if NET20 +using Newtonsoft.Json.Utilities.LinqBridge; +#else +using System.Linq; +#endif using System.Reflection; using System.Runtime.Serialization; +using System.Text; using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Tests.TestObjects; #if !NETFX_CORE @@ -346,5 +353,170 @@ namespace Newtonsoft.Json.Tests.Serialization Console.WriteLine("Error has been fixed"); } } + + [Test] + public void DerivedSerializationEvents() + { + var c = JsonConvert.DeserializeObject("{}"); + + JsonConvert.SerializeObject(c, Formatting.Indented); + + IList e = c.GetEvents(); + + Assert.AreEqual(@"OnDeserializing +OnDeserializing_Derived +OnDeserialized +OnDeserialized_Derived +OnSerializing +OnSerializing_Derived +OnSerialized +OnSerialized_Derived", string.Join(Environment.NewLine, e.ToArray())); + } + + [Test] + public void DerivedDerivedSerializationEvents() + { + var c = JsonConvert.DeserializeObject("{}"); + + JsonConvert.SerializeObject(c, Formatting.Indented); + + IList e = c.GetEvents(); + + Assert.AreEqual(@"OnDeserializing +OnDeserializing_Derived +OnDeserializing_Derived_Derived +OnDeserialized +OnDeserialized_Derived +OnDeserialized_Derived_Derived +OnSerializing +OnSerializing_Derived +OnSerializing_Derived_Derived +OnSerialized +OnSerialized_Derived +OnSerialized_Derived_Derived", string.Join(Environment.NewLine, e.ToArray())); + } + +#if !(NET20 || SILVERLIGHT) + [Test] + public void DerivedDerivedSerializationEvents_DataContractSerializer() + { + string xml = @""; + + DataContractSerializer ss = new DataContractSerializer(typeof(DerivedDerivedSerializationEventOrderTestObject)); + + DerivedDerivedSerializationEventOrderTestObject c = (DerivedDerivedSerializationEventOrderTestObject)ss.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(xml))); + + MemoryStream ms = new MemoryStream(); + ss.WriteObject(ms, c); + + IList e = c.GetEvents(); + + Assert.AreEqual(@"OnDeserializing +OnDeserializing_Derived +OnDeserializing_Derived_Derived +OnDeserialized +OnDeserialized_Derived +OnDeserialized_Derived_Derived +OnSerializing +OnSerializing_Derived +OnSerializing_Derived_Derived +OnSerialized +OnSerialized_Derived +OnSerialized_Derived_Derived", string.Join(Environment.NewLine, e.ToArray())); + } +#endif } -} + + public class SerializationEventOrderTestObject + { + protected IList Events { get; private set; } + + public SerializationEventOrderTestObject() + { + Events = new List(); + } + + public IList GetEvents() + { + return Events; + } + + [OnSerializing] + internal void OnSerializingMethod(StreamingContext context) + { + Events.Add("OnSerializing"); + } + + [OnSerialized] + internal void OnSerializedMethod(StreamingContext context) + { + Events.Add("OnSerialized"); + } + + [OnDeserializing] + internal void OnDeserializingMethod(StreamingContext context) + { + Events.Add("OnDeserializing"); + } + + [OnDeserialized] + internal void OnDeserializedMethod(StreamingContext context) + { + Events.Add("OnDeserialized"); + } + } + + public class DerivedSerializationEventOrderTestObject : SerializationEventOrderTestObject + { + [OnSerializing] + internal new void OnSerializingMethod(StreamingContext context) + { + Events.Add("OnSerializing_Derived"); + } + + [OnSerialized] + internal new void OnSerializedMethod(StreamingContext context) + { + Events.Add("OnSerialized_Derived"); + } + + [OnDeserializing] + internal new void OnDeserializingMethod(StreamingContext context) + { + Events.Add("OnDeserializing_Derived"); + } + + [OnDeserialized] + internal new void OnDeserializedMethod(StreamingContext context) + { + Events.Add("OnDeserialized_Derived"); + } + } + + public class DerivedDerivedSerializationEventOrderTestObject : DerivedSerializationEventOrderTestObject + { + [OnSerializing] + internal new void OnSerializingMethod(StreamingContext context) + { + Events.Add("OnSerializing_Derived_Derived"); + } + + [OnSerialized] + internal new void OnSerializedMethod(StreamingContext context) + { + Events.Add("OnSerialized_Derived_Derived"); + } + + [OnDeserializing] + internal new void OnDeserializingMethod(StreamingContext context) + { + Events.Add("OnDeserializing_Derived_Derived"); + } + + [OnDeserialized] + internal new void OnDeserializedMethod(StreamingContext context) + { + Events.Add("OnDeserialized_Derived_Derived"); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index d69e9c08..002b9553 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -110,7 +110,9 @@ namespace Newtonsoft.Json using (StringWriter writer = StringUtils.CreateStringWriter(64)) { - WriteDateTimeString(writer, updatedDateTime, updatedDateTime.GetUtcOffset(), updatedDateTime.Kind, format, '"'); + writer.Write('"'); + WriteDateTimeString(writer, updatedDateTime, updatedDateTime.GetUtcOffset(), updatedDateTime.Kind, format); + writer.Write('"'); return writer.ToString(); } } @@ -158,47 +160,44 @@ namespace Newtonsoft.Json { using (StringWriter writer = StringUtils.CreateStringWriter(64)) { - WriteDateTimeOffsetString(writer, value, format, null, CultureInfo.InvariantCulture, '"'); + writer.Write('"'); + WriteDateTimeOffsetString(writer, value, format, null, CultureInfo.InvariantCulture); + writer.Write('"'); return writer.ToString(); } } - internal static void WriteDateTimeOffsetString(TextWriter writer, DateTimeOffset value, DateFormatHandling format, string formatString, CultureInfo culture, char quoteChar) + internal static void WriteDateTimeOffsetString(TextWriter writer, DateTimeOffset value, DateFormatHandling format, string formatString, CultureInfo culture) { if (string.IsNullOrEmpty(formatString)) { - WriteDateTimeString(writer, (format == DateFormatHandling.IsoDateFormat) ? value.DateTime : value.UtcDateTime, value.Offset, DateTimeKind.Local, format, quoteChar); + WriteDateTimeString(writer, (format == DateFormatHandling.IsoDateFormat) ? value.DateTime : value.UtcDateTime, value.Offset, DateTimeKind.Local, format); } else { - writer.Write(quoteChar); writer.Write(value.ToString(formatString, culture)); - writer.Write(quoteChar); } } #endif - internal static void WriteDateTimeString(TextWriter writer, DateTime value, DateFormatHandling format, string formatString, CultureInfo culture, char quoteChar) + internal static void WriteDateTimeString(TextWriter writer, DateTime value, DateFormatHandling format, string formatString, CultureInfo culture) { if (string.IsNullOrEmpty(formatString)) { - WriteDateTimeString(writer, value, value.GetUtcOffset(), value.Kind, format, quoteChar); + WriteDateTimeString(writer, value, value.GetUtcOffset(), value.Kind, format); } else { - writer.Write(quoteChar); writer.Write(value.ToString(formatString, culture)); - writer.Write(quoteChar); } } - internal static void WriteDateTimeString(TextWriter writer, DateTime value, TimeSpan offset, DateTimeKind kind, DateFormatHandling format, char quoteChar) + internal static void WriteDateTimeString(TextWriter writer, DateTime value, TimeSpan offset, DateTimeKind kind, DateFormatHandling format) { if (format == DateFormatHandling.MicrosoftDateFormat) { long javaScriptTicks = ConvertDateTimeToJavaScriptTicks(value, offset); - writer.Write(quoteChar); writer.Write(@"\/Date("); writer.Write(javaScriptTicks); @@ -214,11 +213,9 @@ namespace Newtonsoft.Json } writer.Write(@")\/"); - writer.Write(quoteChar); } else { - writer.Write(quoteChar); writer.Write(value.ToString(@"yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFF", CultureInfo.InvariantCulture)); switch (kind) @@ -231,7 +228,6 @@ namespace Newtonsoft.Json break; } - writer.Write(quoteChar); } } @@ -727,6 +723,126 @@ namespace Newtonsoft.Json return IsJsonPrimitiveTypeCode(ConvertUtils.GetTypeCode(type)); } + internal static bool TryParseDateTime(string s, DateParseHandling dateParseHandling, DateTimeZoneHandling dateTimeZoneHandling, out object dt) + { + if (s.Length > 0) + { + if (s[0] == '/') + { + if (s.StartsWith("/Date(", StringComparison.Ordinal) && s.EndsWith(")/", StringComparison.Ordinal)) + { + return TryParseDateMicrosoft(s, dateParseHandling, dateTimeZoneHandling, out dt); + } + } + else if (char.IsDigit(s[0]) && s.Length >= 19 && s.Length <= 40) + { + return TryParseDateIso(s, dateParseHandling, dateTimeZoneHandling, out dt); + } + } + + dt = null; + return false; + } + + private static bool TryParseDateIso(string text, DateParseHandling dateParseHandling, DateTimeZoneHandling dateTimeZoneHandling, out object dt) + { + const string isoDateFormat = "yyyy-MM-ddTHH:mm:ss.FFFFFFFK"; + +#if !NET20 + if (dateParseHandling == DateParseHandling.DateTimeOffset) + { + DateTimeOffset dateTimeOffset; + if (DateTimeOffset.TryParseExact(text, isoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTimeOffset)) + { + dt = dateTimeOffset; + return true; + } + } + else +#endif + { + DateTime dateTime; + if (DateTime.TryParseExact(text, isoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTime)) + { + dateTime = EnsureDateTime(dateTime, dateTimeZoneHandling); + + dt = dateTime; + return true; + } + } + + dt = null; + return false; + } + + private static bool TryParseDateMicrosoft(string text, DateParseHandling dateParseHandling, DateTimeZoneHandling dateTimeZoneHandling, out object dt) + { + string value = text.Substring(6, text.Length - 8); + DateTimeKind kind = DateTimeKind.Utc; + + int index = value.IndexOf('+', 1); + + if (index == -1) + index = value.IndexOf('-', 1); + + TimeSpan offset = TimeSpan.Zero; + + if (index != -1) + { + kind = DateTimeKind.Local; + offset = ReadOffset(value.Substring(index)); + value = value.Substring(0, index); + } + + long javaScriptTicks = long.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + + DateTime utcDateTime = ConvertJavaScriptTicksToDateTime(javaScriptTicks); + +#if !NET20 + if (dateParseHandling == DateParseHandling.DateTimeOffset) + { + dt = new DateTimeOffset(utcDateTime.Add(offset).Ticks, offset); + return true; + } + else +#endif + { + DateTime dateTime; + + switch (kind) + { + case DateTimeKind.Unspecified: + dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified); + break; + case DateTimeKind.Local: + dateTime = utcDateTime.ToLocalTime(); + break; + default: + dateTime = utcDateTime; + break; + } + + dt = EnsureDateTime(dateTime, dateTimeZoneHandling); + return true; + } + } + + private static TimeSpan ReadOffset(string offsetText) + { + bool negative = (offsetText[0] == '-'); + + int hours = int.Parse(offsetText.Substring(1, 2), NumberStyles.Integer, CultureInfo.InvariantCulture); + int minutes = 0; + if (offsetText.Length >= 5) + minutes = int.Parse(offsetText.Substring(3, 2), NumberStyles.Integer, CultureInfo.InvariantCulture); + + TimeSpan offset = TimeSpan.FromHours(hours) + TimeSpan.FromMinutes(minutes); + if (negative) + offset = offset.Negate(); + + return offset; + } + #region Serialize /// /// Serializes the specified object to a JSON string. diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index ef8ffbc6..824eab2a 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -137,21 +137,21 @@ namespace Newtonsoft.Json if (_dateParseHandling != DateParseHandling.None) { - if (text.Length > 0) + DateParseHandling dateParseHandling; + if (_readType == ReadType.ReadAsDateTime) + dateParseHandling = DateParseHandling.DateTime; +#if !NET20 + else if (_readType == ReadType.ReadAsDateTimeOffset) + dateParseHandling = DateParseHandling.DateTimeOffset; +#endif + else + dateParseHandling = _dateParseHandling; + + object dt; + if (JsonConvert.TryParseDateTime(text, dateParseHandling, DateTimeZoneHandling, out dt)) { - if (text[0] == '/') - { - if (text.StartsWith("/Date(", StringComparison.Ordinal) && text.EndsWith(")/", StringComparison.Ordinal)) - { - ParseDateMicrosoft(text); - return; - } - } - else if (char.IsDigit(text[0]) && text.Length >= 19 && text.Length <= 40) - { - if (ParseDateIso(text)) - return; - } + SetToken(JsonToken.Date, dt); + return; } } @@ -160,88 +160,6 @@ namespace Newtonsoft.Json } } - private bool ParseDateIso(string text) - { - const string isoDateFormat = "yyyy-MM-ddTHH:mm:ss.FFFFFFFK"; - -#if !NET20 - if (_readType == ReadType.ReadAsDateTimeOffset || (_readType == ReadType.Read && _dateParseHandling == DateParseHandling.DateTimeOffset)) - { - DateTimeOffset dateTimeOffset; - if (DateTimeOffset.TryParseExact(text, isoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTimeOffset)) - { - SetToken(JsonToken.Date, dateTimeOffset); - return true; - } - } - else -#endif - { - DateTime dateTime; - if (DateTime.TryParseExact(text, isoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTime)) - { - dateTime = JsonConvert.EnsureDateTime(dateTime, DateTimeZoneHandling); - - SetToken(JsonToken.Date, dateTime); - return true; - } - } - - return false; - } - - private void ParseDateMicrosoft(string text) - { - string value = text.Substring(6, text.Length - 8); - DateTimeKind kind = DateTimeKind.Utc; - - int index = value.IndexOf('+', 1); - - if (index == -1) - index = value.IndexOf('-', 1); - - TimeSpan offset = TimeSpan.Zero; - - if (index != -1) - { - kind = DateTimeKind.Local; - offset = ReadOffset(value.Substring(index)); - value = value.Substring(0, index); - } - - long javaScriptTicks = long.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); - - DateTime utcDateTime = JsonConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks); - -#if !NET20 - if (_readType == ReadType.ReadAsDateTimeOffset || (_readType == ReadType.Read && _dateParseHandling == DateParseHandling.DateTimeOffset)) - { - SetToken(JsonToken.Date, new DateTimeOffset(utcDateTime.Add(offset).Ticks, offset)); - } - else -#endif - { - DateTime dateTime; - - switch (kind) - { - case DateTimeKind.Unspecified: - dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified); - break; - case DateTimeKind.Local: - dateTime = utcDateTime.ToLocalTime(); - break; - default: - dateTime = utcDateTime; - break; - } - - dateTime = JsonConvert.EnsureDateTime(dateTime, DateTimeZoneHandling); - - SetToken(JsonToken.Date, dateTime); - } - } - private static void BlockCopyChars(char[] src, int srcOffset, char[] dst, int dstOffset, int count) { const int charByteCount = 2; @@ -368,22 +286,6 @@ namespace Newtonsoft.Json return true; } - private static TimeSpan ReadOffset(string offsetText) - { - bool negative = (offsetText[0] == '-'); - - int hours = int.Parse(offsetText.Substring(1, 2), NumberStyles.Integer, CultureInfo.InvariantCulture); - int minutes = 0; - if (offsetText.Length >= 5) - minutes = int.Parse(offsetText.Substring(3, 2), NumberStyles.Integer, CultureInfo.InvariantCulture); - - TimeSpan offset = TimeSpan.FromHours(hours) + TimeSpan.FromMinutes(minutes); - if (negative) - offset = offset.Negate(); - - return offset; - } - /// /// Reads the next JSON token from the stream. /// diff --git a/Src/Newtonsoft.Json/JsonTextWriter.cs b/Src/Newtonsoft.Json/JsonTextWriter.cs index 4a5f4241..3cee32d9 100644 --- a/Src/Newtonsoft.Json/JsonTextWriter.cs +++ b/Src/Newtonsoft.Json/JsonTextWriter.cs @@ -211,6 +211,28 @@ namespace Newtonsoft.Json _writer.Write(':'); } + public override void WritePropertyName(string name, bool escape) + { + InternalWritePropertyName(name); + + if (escape) + { + JavaScriptUtils.WriteEscapedJavaScriptString(_writer, name, _quoteChar, _quoteName, GetCharEscapeFlags(), StringEscapeHandling); + } + else + { + if (_quoteName) + _writer.Write(_quoteChar); + + _writer.Write(name); + + if (_quoteName) + _writer.Write(_quoteChar); + } + + _writer.Write(':'); + } + private bool[] GetCharEscapeFlags() { if (StringEscapeHandling == StringEscapeHandling.EscapeHtml) @@ -484,7 +506,9 @@ namespace Newtonsoft.Json { InternalWriteValue(JsonToken.Date); value = JsonConvert.EnsureDateTime(value, DateTimeZoneHandling); - JsonConvert.WriteDateTimeString(_writer, value, DateFormatHandling, DateFormatString, Culture, _quoteChar); + _writer.Write(_quoteChar); + JsonConvert.WriteDateTimeString(_writer, value, DateFormatHandling, DateFormatString, Culture); + _writer.Write(_quoteChar); } /// @@ -515,7 +539,9 @@ namespace Newtonsoft.Json public override void WriteValue(DateTimeOffset value) { InternalWriteValue(JsonToken.Date); - JsonConvert.WriteDateTimeOffsetString(_writer, value, DateFormatHandling, DateFormatString, Culture, _quoteChar); + _writer.Write(_quoteChar); + JsonConvert.WriteDateTimeOffsetString(_writer, value, DateFormatHandling, DateFormatString, Culture); + _writer.Write(_quoteChar); } #endif diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index f074fe39..082f7065 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -411,6 +411,11 @@ namespace Newtonsoft.Json InternalWritePropertyName(name); } + public virtual void WritePropertyName(string name, bool escape) + { + WritePropertyName(name); + } + internal void InternalWritePropertyName(string name) { _currentPosition.PropertyName = name; diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index ce28e243..b36e88c0 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -37,7 +37,7 @@ namespace Newtonsoft.Json.Linq /// /// Represents a value in JSON (string, integer, date, etc). /// - public class JValue : JToken, IEquatable, IFormattable, IComparable, IComparable + public class JValue : JToken, IEquatable, IFormattable, IComparable, IComparable, IConvertible { private JTokenType _valueType; private object _value; @@ -830,5 +830,93 @@ namespace Newtonsoft.Json.Linq return Compare(_valueType, _value, obj._value); } + + TypeCode IConvertible.GetTypeCode() + { + if (_value == null) + return TypeCode.Empty; + + return System.Type.GetTypeCode(_value.GetType()); + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(_value, CultureInfo.InvariantCulture); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(_value, CultureInfo.InvariantCulture); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(_value, CultureInfo.InvariantCulture); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(_value, CultureInfo.InvariantCulture); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(_value, CultureInfo.InvariantCulture); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(_value, CultureInfo.InvariantCulture); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(_value, CultureInfo.InvariantCulture); + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(_value, CultureInfo.InvariantCulture); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(_value, CultureInfo.InvariantCulture); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(_value, CultureInfo.InvariantCulture); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(_value, CultureInfo.InvariantCulture); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(_value, CultureInfo.InvariantCulture); + } + + decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(_value, CultureInfo.InvariantCulture); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { +#if !NET20 + if (_value is DateTimeOffset) + return ((DateTimeOffset) _value).DateTime; +#endif + + return Convert.ToDateTime(_value, CultureInfo.InvariantCulture); + } + + object IConvertible.ToType(Type conversionType, IFormatProvider provider) + { + return Convert.ChangeType(_value, conversionType, provider); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 58c7c787..3ab2fed9 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -87,6 +87,6 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("4.5.11.15906")] +[assembly: AssemblyFileVersion("4.5.11.15911")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index f353e3c6..2a886d77 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -547,9 +547,6 @@ namespace Newtonsoft.Json.Serialization private void ResolveCallbackMethods(JsonContract contract, Type t) { - if (t.BaseType() != null) - ResolveCallbackMethods(contract, t.BaseType()); - List onSerializing; List onSerialized; List onDeserializing; diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 63c34283..1c466cdb 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -878,7 +878,18 @@ To fix this error either change the JSON to a {1} or change the deserialized typ { try { - keyValue = EnsureType(reader, keyValue, CultureInfo.InvariantCulture, contract.KeyContract, contract.DictionaryKeyType); + if (contract.DictionaryKeyType == typeof(DateTime) && JsonConvert.TryParseDateTime(keyValue.ToString(), DateParseHandling.DateTime, reader.DateTimeZoneHandling, out keyValue)) + { + } +#if !NET20 + else if (contract.DictionaryKeyType == typeof(DateTimeOffset) && JsonConvert.TryParseDateTime(keyValue.ToString(), DateParseHandling.DateTimeOffset, reader.DateTimeZoneHandling, out keyValue)) + { + } +#endif + else + { + keyValue = EnsureType(reader, keyValue, CultureInfo.InvariantCulture, contract.KeyContract, contract.DictionaryKeyType); + } } catch (Exception ex) { diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 4af07fd7..63f04e25 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -32,6 +32,7 @@ using System.Dynamic; #endif using System.Diagnostics; using System.Globalization; +using System.IO; using System.Security; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Utilities; @@ -833,7 +834,8 @@ To fix this error either change the environment to be fully trusted, change the foreach (DictionaryEntry entry in d) { - string propertyName = GetPropertyName(entry); + bool escape; + string propertyName = GetPropertyName(writer, entry, out escape); propertyName = (contract.PropertyNameResolver != null) ? contract.PropertyNameResolver(propertyName) @@ -846,7 +848,7 @@ To fix this error either change the environment to be fully trusted, change the if (ShouldWriteReference(value, null, valueContract, contract, member)) { - writer.WritePropertyName(propertyName); + writer.WritePropertyName(propertyName, escape); WriteReference(writer, value); } else @@ -854,7 +856,7 @@ To fix this error either change the environment to be fully trusted, change the if (!CheckForCircularReference(writer, value, null, valueContract, contract, member)) continue; - writer.WritePropertyName(propertyName); + writer.WritePropertyName(propertyName, escape); SerializeValue(writer, value, valueContract, null, contract, member); } @@ -875,16 +877,43 @@ To fix this error either change the environment to be fully trusted, change the OnSerialized(writer, contract, values.UnderlyingDictionary); } - private string GetPropertyName(DictionaryEntry entry) + private string GetPropertyName(JsonWriter writer, DictionaryEntry entry, out bool escape) { + object key = entry.Key; + string propertyName; - if (ConvertUtils.IsConvertible(entry.Key)) - return Convert.ToString(entry.Key, CultureInfo.InvariantCulture); - else if (TryConvertToString(entry.Key, entry.Key.GetType(), out propertyName)) + if (key is DateTime) + { + escape = false; + StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); + JsonConvert.WriteDateTimeString(sw, (DateTime)key, writer.DateFormatHandling, writer.DateFormatString, writer.Culture); + return sw.ToString(); + } +#if !NET20 + else if (key is DateTimeOffset) + { + escape = false; + StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); + JsonConvert.WriteDateTimeOffsetString(sw, (DateTimeOffset)key, writer.DateFormatHandling, writer.DateFormatString, writer.Culture); + return sw.ToString(); + } +#endif + else if (ConvertUtils.IsConvertible(key)) + { + escape = true; + return Convert.ToString(key, CultureInfo.InvariantCulture); + } + else if (TryConvertToString(key, key.GetType(), out propertyName)) + { + escape = true; return propertyName; + } else - return entry.Key.ToString(); + { + escape = true; + return key.ToString(); + } } private void HandleError(JsonWriter writer, int initialDepth) diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index 6b9ac68d..9cb87c79 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -119,18 +119,7 @@ namespace Newtonsoft.Json.Serialization public static DataContractAttribute GetDataContractAttribute(Type type) { // DataContractAttribute does not have inheritance - Type currentType = type; - - while (currentType != null) - { - DataContractAttribute result = CachedAttributeGetter.GetAttribute(currentType.GetCustomAttributeProvider()); - if (result != null) - return result; - - currentType = currentType.BaseType(); - } - - return null; + return CachedAttributeGetter.GetAttribute(type.GetCustomAttributeProvider()); } public static DataMemberAttribute GetDataMemberAttribute(MemberInfo memberInfo)