diff --git a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs index f1348c22..01bfb474 100644 --- a/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs @@ -24,11 +24,18 @@ #endregion using System; +using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; using System.Text; +#if !(NET20 || NET35 || SILVERLIGHT) +using System.Threading.Tasks; +#endif using System.Xml; using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Tests.Serialization; +using Newtonsoft.Json.Tests.TestObjects; using Newtonsoft.Json.Utilities; using NUnit.Framework; @@ -640,5 +647,78 @@ now brown cow?", '"', true); return (T)converter.ReadJson(reader, typeof(T), null, null); } + +#if !(NET20 || NET35 || SILVERLIGHT) + [Test] + public void Async() + { + Task task = null; + + task = JsonConvert.SerializeObjectAsync(42); + task.Wait(); + + Assert.AreEqual("42", task.Result); + + task = JsonConvert.SerializeObjectAsync(new[] {1, 2, 3, 4, 5}, Formatting.Indented); + task.Wait(); + + Assert.AreEqual(@"[ + 1, + 2, + 3, + 4, + 5 +]", task.Result); + + task = JsonConvert.SerializeObjectAsync(DateTime.MaxValue, Formatting.None, new JsonSerializerSettings + { + DateFormatHandling = DateFormatHandling.MicrosoftDateFormat + }); + task.Wait(); + + Assert.AreEqual(@"""\/Date(253402300799999)\/""", task.Result); + + var taskObject = JsonConvert.DeserializeObjectAsync("[]"); + taskObject.Wait(); + + Assert.AreEqual(new JArray(), taskObject.Result); + + Task taskVersionArray = JsonConvert.DeserializeObjectAsync("['2.0']", typeof(Version[]), new JsonSerializerSettings + { + Converters = {new VersionConverter()} + }); + taskVersionArray.Wait(); + + Version[] versionArray = (Version[])taskVersionArray.Result; + + Assert.AreEqual(1, versionArray.Length); + Assert.AreEqual(2, versionArray[0].Major); + + Task taskInt = JsonConvert.DeserializeObjectAsync("5"); + taskInt.Wait(); + + Assert.AreEqual(5, taskInt.Result); + + var taskVersion = JsonConvert.DeserializeObjectAsync("'2.0'", new JsonSerializerSettings + { + Converters = {new VersionConverter()} + }); + taskVersion.Wait(); + + Assert.AreEqual(2, taskVersion.Result.Major); + + Movie p = new Movie(); + p.Name = "Existing,"; + + Task taskVoid = JsonConvert.PopulateObjectAsync("{'Name':'Appended'}", p, new JsonSerializerSettings + { + Converters = new List { new JsonSerializerTest.StringAppenderConverter() } + }); + + taskVoid.Wait(); + + Assert.AreEqual("Existing,Appended", p.Name); + } +#endif } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index ca928597..57c9e8eb 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -704,7 +704,6 @@ namespace Newtonsoft.Json.Tests } [Test] - [Ignore] public void NestedJToken() { Stopwatch sw; @@ -717,7 +716,7 @@ namespace Newtonsoft.Json.Tests for (int j = 0; j < i; j++) { JArray temp = new JArray(); - ija.Add(temp); + ija.AddAndSkipParentCheck(temp); ija = temp; } ija.Add(1); @@ -727,26 +726,21 @@ namespace Newtonsoft.Json.Tests } [Test] - [Ignore] - public void NestedXElement() + public void DeserializeNestedJToken() { + string json = (new string('[', 100000)) + "1" + ((new string(']', 100000))); + Stopwatch sw; - for (int i = 10000; i <= 100000; i += 10000) - { - sw = new Stopwatch(); - sw.Start(); - XElement ija = new XElement("root"); - XElement ijt = ija; - for (int j = 0; j < i; j++) - { - XElement temp = new XElement("child"); - ija.Add(temp); - ija = temp; - } - ija.Add(1); - sw.Stop(); - Console.WriteLine("Created a XElement of depth {0} (using OM) in {1} millis", i, sw.ElapsedMilliseconds); - } + sw = new Stopwatch(); + sw.Start(); + + var a = (JArray)JsonConvert.DeserializeObject(json); + + sw.Stop(); + + Assert.AreEqual(1, a.Count); + + Console.WriteLine("Deserialize big ass nested array in {0} millis", sw.ElapsedMilliseconds); } } diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index e5c19f66..7920d374 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -72,5 +72,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.8.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.8.14704")] +[assembly: AssemblyFileVersion("4.0.8.14707")] #endif diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 5682f8e7..33689def 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -26,6 +26,9 @@ using System; using System.IO; using System.Globalization; +#if !(NET20 || NET35 || SILVERLIGHT) +using System.Threading.Tasks; +#endif using Newtonsoft.Json.Utilities; using System.Xml; using Newtonsoft.Json.Converters; @@ -643,6 +646,7 @@ namespace Newtonsoft.Json return IsJsonPrimitiveTypeCode(Type.GetTypeCode(type)); } + #region Serialize /// /// Serializes the specified object to a JSON string. /// @@ -733,6 +737,50 @@ namespace Newtonsoft.Json return sw.ToString(); } +#if !(NET20 || NET35 || SILVERLIGHT) + /// + /// Asynchronously serializes the specified object to a JSON string using a collection of . + /// + /// The object to serialize. + /// + /// A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. + /// + public static Task SerializeObjectAsync(object value) + { + return SerializeObjectAsync(value, Formatting.None, null); + } + + /// + /// Asynchronously serializes the specified object to a JSON string using a collection of . + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// + /// A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. + /// + public static Task SerializeObjectAsync(object value, Formatting formatting) + { + return SerializeObjectAsync(value, formatting, null); + } + + /// + /// Asynchronously serializes the specified object to a JSON string using a collection of . + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// The used to serialize the object. + /// If this is null, default serialization settings will be is used. + /// + /// A task that represents the asynchronous serialize operation. The value of the TResult parameter contains a JSON string representation of the object. + /// + public static Task SerializeObjectAsync(object value, Formatting formatting, JsonSerializerSettings settings) + { + return Task.Factory.StartNew(() => SerializeObject(value, formatting, settings)); + } +#endif + #endregion + + #region Deserialize /// /// Deserializes the JSON to a .NET object. /// @@ -868,6 +916,68 @@ namespace Newtonsoft.Json return deserializedValue; } +#if !(NET20 || NET35 || SILVERLIGHT) + /// + /// Asynchronously deserializes the JSON to the specified .NET type. + /// + /// The type of the object to deserialize to. + /// The JSON to deserialize. + /// + /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. + /// + public static Task DeserializeObjectAsync(string value) + { + return DeserializeObjectAsync(value, null); + } + + /// + /// Asynchronously deserializes the JSON to the specified .NET type. + /// + /// The type of the object to deserialize to. + /// The JSON to deserialize. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be is used. + /// + /// + /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. + /// + public static Task DeserializeObjectAsync(string value, JsonSerializerSettings settings) + { + return Task.Factory.StartNew(() => DeserializeObject(value, settings)); + } + + /// + /// Asynchronously deserializes the JSON to the specified .NET type. + /// + /// The JSON to deserialize. + /// + /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. + /// + public static Task DeserializeObjectAsync(string value) + { + return DeserializeObjectAsync(value, null, null); + } + + /// + /// Asynchronously deserializes the JSON to the specified .NET type. + /// + /// The JSON to deserialize. + /// The type of the object to deserialize to. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be is used. + /// + /// + /// A task that represents the asynchronous deserialize operation. The value of the TResult parameter contains the deserialized object from the JSON string. + /// + public static Task DeserializeObjectAsync(string value, Type type, JsonSerializerSettings settings) + { + return Task.Factory.StartNew(() => DeserializeObject(value, type, settings)); + } +#endif + #endregion + /// /// Populates the object with values from the JSON string. /// @@ -878,7 +988,6 @@ namespace Newtonsoft.Json PopulateObject(value, target, null); } - /// /// Populates the object with values from the JSON string. /// @@ -902,6 +1011,25 @@ namespace Newtonsoft.Json } } +#if !(NET20 || NET35 || SILVERLIGHT) + /// + /// Asynchronously populates the object with values from the JSON string. + /// + /// The JSON to populate values from. + /// The target object to populate values onto. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be is used. + /// + /// + /// A task that represents the asynchronous populate operation. + /// + public static Task PopulateObjectAsync(string value, object target, JsonSerializerSettings settings) + { + return Task.Factory.StartNew(() => PopulateObject(value, target, settings)); + } +#endif + #if !SILVERLIGHT /// /// Serializes the XML node to a JSON string. diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index f4091756..c7ffa80c 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -159,11 +159,13 @@ namespace Newtonsoft.Json private bool ParseDateIso(string text) { + const string isoDateFormat = "yyyy-MM-ddTHH:mm:ss.FFFFFFFK"; + #if !NET20 if (_readType == ReadType.ReadAsDateTimeOffset) { DateTimeOffset dateTimeOffset; - if (DateTimeOffset.TryParseExact(text, "yyyy-MM-ddTHH:mm:ss.FFFFFFFK", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTimeOffset)) + if (DateTimeOffset.TryParseExact(text, isoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTimeOffset)) { SetToken(JsonToken.Date, dateTimeOffset); return true; @@ -173,7 +175,7 @@ namespace Newtonsoft.Json #endif { DateTime dateTime; - if (DateTime.TryParseExact(text, "yyyy-MM-ddTHH:mm:ss.FFFFFFFK", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTime)) + if (DateTime.TryParseExact(text, isoDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out dateTime)) { dateTime = JsonConvert.EnsureDateTime(dateTime, DateTimeZoneHandling); diff --git a/Src/Newtonsoft.Json/Linq/JArray.cs b/Src/Newtonsoft.Json/Linq/JArray.cs index 3705a8d8..da7d2a0d 100644 --- a/Src/Newtonsoft.Json/Linq/JArray.cs +++ b/Src/Newtonsoft.Json/Linq/JArray.cs @@ -247,7 +247,7 @@ namespace Newtonsoft.Json.Linq /// The is read-only. public void Insert(int index, JToken item) { - InsertItem(index, item); + InsertItem(index, item, false); } /// diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index 01bda6e7..d1f83e0d 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -261,7 +261,7 @@ namespace Newtonsoft.Json.Linq return (content is IEnumerable && !(content is string) && !(content is JToken) && !(content is byte[])); } - internal JToken EnsureParentToken(JToken item) + internal JToken EnsureParentToken(JToken item, bool skipParentCheck) { if (item == null) return new JValue((object) null); @@ -273,14 +273,18 @@ namespace Newtonsoft.Json.Linq else { // check whether attempting to add a token to itself - JContainer parent = this; - while (parent.Parent != null) + // not possible with a value, only a container + if (item is JContainer && !skipParentCheck) { - parent = parent.Parent; - } - if (item == parent) - { - item = item.CloneToken(); + JContainer parent = this; + while (parent.Parent != null) + { + parent = parent.Parent; + } + if (item == parent) + { + item = item.CloneToken(); + } } } return item; @@ -309,14 +313,14 @@ namespace Newtonsoft.Json.Linq return ChildrenTokens.IndexOf(item, JTokenReferenceEqualityComparer.Instance); } - internal virtual void InsertItem(int index, JToken item) + internal virtual void InsertItem(int index, JToken item, bool skipParentCheck) { if (index > ChildrenTokens.Count) throw new ArgumentOutOfRangeException("index", "Index must be within the bounds of the List."); CheckReentrancy(); - item = EnsureParentToken(item); + item = EnsureParentToken(item, skipParentCheck); JToken previous = (index == 0) ? null : ChildrenTokens[index - 1]; // haven't inserted new token yet so next token is still at the inserting index @@ -409,7 +413,7 @@ namespace Newtonsoft.Json.Linq CheckReentrancy(); - item = EnsureParentToken(item); + item = EnsureParentToken(item, false); ValidateToken(item, existing); @@ -523,7 +527,12 @@ namespace Newtonsoft.Json.Linq /// The content to be added. public virtual void Add(object content) { - AddInternal(ChildrenTokens.Count, content); + AddInternal(ChildrenTokens.Count, content, false); + } + + internal void AddAndSkipParentCheck(JToken token) + { + AddInternal(ChildrenTokens.Count, token, true); } /// @@ -532,10 +541,10 @@ namespace Newtonsoft.Json.Linq /// The content to be added. public void AddFirst(object content) { - AddInternal(0, content); + AddInternal(0, content, false); } - internal void AddInternal(int index, object content) + internal void AddInternal(int index, object content, bool skipParentCheck) { if (IsMultiContent(content)) { @@ -544,7 +553,7 @@ namespace Newtonsoft.Json.Linq int multiIndex = index; foreach (object c in enumerable) { - AddInternal(multiIndex, c); + AddInternal(multiIndex, c, skipParentCheck); multiIndex++; } } @@ -552,7 +561,7 @@ namespace Newtonsoft.Json.Linq { JToken item = CreateFromContent(content); - InsertItem(index, item); + InsertItem(index, item, skipParentCheck); } } @@ -745,7 +754,7 @@ namespace Newtonsoft.Json.Linq void IList.Insert(int index, JToken item) { - InsertItem(index, item); + InsertItem(index, item, false); } void IList.RemoveAt(int index) @@ -831,7 +840,7 @@ namespace Newtonsoft.Json.Linq void IList.Insert(int index, object value) { - InsertItem(index, EnsureValue(value)); + InsertItem(index, EnsureValue(value), false); } bool IList.IsFixedSize diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 0478d2c3..13ca2fdc 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -146,13 +146,13 @@ namespace Newtonsoft.Json.Linq return (t != null && ContentsEqual(t)); } - internal override void InsertItem(int index, JToken item) + internal override void InsertItem(int index, JToken item, bool skipParentCheck) { // don't add comments to JObject, no name to reference comment by if (item != null && item.Type == JTokenType.Comment) return; - base.InsertItem(index, item); + base.InsertItem(index, item, skipParentCheck); } internal override void ValidateToken(JToken o, JToken existing) diff --git a/Src/Newtonsoft.Json/Linq/JProperty.cs b/Src/Newtonsoft.Json/Linq/JProperty.cs index 3487ab10..44568184 100644 --- a/Src/Newtonsoft.Json/Linq/JProperty.cs +++ b/Src/Newtonsoft.Json/Linq/JProperty.cs @@ -78,7 +78,7 @@ namespace Newtonsoft.Json.Linq if (ChildrenTokens.Count == 0) { - InsertItem(0, newValue); + InsertItem(0, newValue, false); } else { @@ -132,12 +132,12 @@ namespace Newtonsoft.Json.Linq throw new Exception("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); } - internal override void InsertItem(int index, JToken item) + internal override void InsertItem(int index, JToken item, bool skipParentCheck) { if (Value != null) throw new Exception("{0} cannot have multiple values.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); - base.InsertItem(0, item); + base.InsertItem(0, item, false); } internal override bool ContainsItem(JToken item) diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 019fbc1e..15cb4a97 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -167,7 +167,7 @@ namespace Newtonsoft.Json.Linq throw new InvalidOperationException("The parent is missing."); int index = _parent.IndexOfItem(this); - _parent.AddInternal(index + 1, content); + _parent.AddInternal(index + 1, content, false); } /// @@ -180,7 +180,7 @@ namespace Newtonsoft.Json.Linq throw new InvalidOperationException("The parent is missing."); int index = _parent.IndexOfItem(this); - _parent.AddInternal(index, content); + _parent.AddInternal(index, content, false); } /// diff --git a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs index d5875393..ccc02b86 100644 --- a/Src/Newtonsoft.Json/Linq/JTokenWriter.cs +++ b/Src/Newtonsoft.Json/Linq/JTokenWriter.cs @@ -81,7 +81,7 @@ namespace Newtonsoft.Json.Linq if (_parent == null) _token = container; else - _parent.Add(container); + _parent.AddAndSkipParentCheck(container); _parent = container; } diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 32ff8a28..73df4a05 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -85,7 +85,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.8.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.8.14704")] +[assembly: AssemblyFileVersion("4.0.8.14707")] #endif [assembly: CLSCompliant(true)]