diff --git a/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs b/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs index 1aa6a1ab..e6cd9619 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs @@ -512,6 +512,17 @@ namespace Newtonsoft.Json.Tests.Converters Assert.AreEqual(@"113150assets/images/resized/0001/1070/11070v1-max-150x150.jpg189250assets/images/resized/0001/1070/11070v1-max-250x250.jpg341450assets/images/resized/0001/1070/11070v1-max-450x450.jpg", newDoc.InnerXml); } + + [Test] + public void Encoding() + { + XmlDocument doc = new XmlDocument(); + + doc.LoadXml(@"O""Connor"); // i use "" so it will be easier to see the problem + + string json = JsonConvert.SerializeXmlNode(doc); + Assert.AreEqual(@"{""name"":""O\""Connor""}", json); + } } } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/BindingTests.cs b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/BindingTests.cs new file mode 100644 index 00000000..6ee97a10 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/BindingTests.cs @@ -0,0 +1,79 @@ +#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 + +#if !PocketPC && !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using System.Web.UI; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Linq.ComponentModel +{ + public class BindingTests : TestFixtureBase + { + [Test] + public void DataBinderEval() + { + JObject o = new JObject( + new JProperty("First", "String!"), + new JProperty("Second", 12345.6789m), + new JProperty("Third", new JArray( + 1, + 2, + 3, + 4, + 5, + new JObject( + new JProperty("Fourth", "String!"), + new JProperty("Fifth", new JObject( + new JProperty("Sixth", "String!"))))))); + + object value; + + value = DataBinder.Eval(o, "First"); + Assert.AreEqual(value, (string)o["First"]); + + value = DataBinder.Eval(o, "Second"); + Assert.AreEqual(value, (decimal)o["Second"]); + + value = DataBinder.Eval(o, "Third"); + Assert.AreEqual(value, o["Third"]); + + value = DataBinder.Eval(o, "Third[0]"); + // urg! + Assert.AreEqual((int)(JToken)value, (int)o["Third"][0]); + + value = DataBinder.Eval(o, "Third[5].Fourth"); + Assert.AreEqual(value, (string)o["Third"][5]["Fourth"]); + + value = DataBinder.Eval(o, "Third[5].Fifth.Sixth"); + Assert.AreEqual(value, (string)o["Third"][5]["Fifth"]["Sixth"]); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs new file mode 100644 index 00000000..4e82bb9b --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs @@ -0,0 +1,91 @@ +#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 + +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Linq.ComponentModel; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Linq.ComponentModel +{ + public class JPropertyDescriptorTests : TestFixtureBase + { + [Test] + public void GetValue() + { + JObject o = JObject.Parse("{prop1:'12345!',prop2:[1,'two','III']}"); + + JPropertyDescriptor prop1 = new JPropertyDescriptor("prop1", typeof(string)); + JPropertyDescriptor prop2 = new JPropertyDescriptor("prop2", typeof(JArray)); + + Assert.AreEqual("12345!", prop1.GetValue(o)); + Assert.AreEqual(o["prop2"], prop2.GetValue(o)); + } + + [Test] + public void SetValue() + { + JObject o = JObject.Parse("{prop1:'12345!'}"); + + JPropertyDescriptor propertyDescriptor1 = new JPropertyDescriptor("prop1", typeof(string)); + + propertyDescriptor1.SetValue(o, "54321!"); + + Assert.AreEqual("54321!", (string)o["prop1"]); + } + + [Test] + public void ResetValue() + { + JObject o = JObject.Parse("{prop1:'12345!'}"); + + JPropertyDescriptor propertyDescriptor1 = new JPropertyDescriptor("prop1", typeof(string)); + propertyDescriptor1.ResetValue(o); + + Assert.AreEqual("12345!", (string)o["prop1"]); + } + + [Test] + public void IsReadOnly() + { + JPropertyDescriptor propertyDescriptor1 = new JPropertyDescriptor("prop1", typeof(string)); + + Assert.AreEqual(false, propertyDescriptor1.IsReadOnly); + } + + [Test] + public void PropertyType() + { + JPropertyDescriptor propertyDescriptor1 = new JPropertyDescriptor("prop1", typeof(string)); + + Assert.AreEqual(typeof(string), propertyDescriptor1.PropertyType); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JTypeDescriptionProviderTests.cs b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JTypeDescriptionProviderTests.cs new file mode 100644 index 00000000..1dafce03 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JTypeDescriptionProviderTests.cs @@ -0,0 +1,49 @@ +#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 + +#if !PocketPC && !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Linq; +using System.ComponentModel; +using Newtonsoft.Json.Linq.ComponentModel; + +namespace Newtonsoft.Json.Tests.Linq.ComponentModel +{ + public class JTypeDescriptionProviderTests : TestFixtureBase + { + [Test] + public void GetTypeDescriptor() + { + JTypeDescriptionProvider provider = new JTypeDescriptionProvider(); + JTypeDescriptor descriptor = (JTypeDescriptor)provider.GetTypeDescriptor(typeof (JObject), new JObject()); + Assert.IsNotNull(descriptor); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JTypeDescriptorTests.cs b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JTypeDescriptorTests.cs new file mode 100644 index 00000000..a7bfd71b --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JTypeDescriptorTests.cs @@ -0,0 +1,80 @@ +#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 + +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Linq.ComponentModel; +using NUnit.Framework; +using Newtonsoft.Json.Linq; +using System.ComponentModel; + +namespace Newtonsoft.Json.Tests.Linq.ComponentModel +{ + public class JTypeDescriptorTests : TestFixtureBase + { + [Test] + public void GetProperties() + { + JObject o = JObject.Parse("{'prop1':12,'prop2':'hi!','prop3':null,'prop4':[1,2,3]}"); + + JTypeDescriptor descriptor = new JTypeDescriptor(o); + + PropertyDescriptorCollection properties = descriptor.GetProperties(); + Assert.AreEqual(4, properties.Count); + + PropertyDescriptor prop1 = properties[0]; + Assert.AreEqual("prop1", prop1.Name); + Assert.AreEqual(typeof(long), prop1.PropertyType); + Assert.AreEqual(typeof(JObject), prop1.ComponentType); + Assert.AreEqual(false, prop1.CanResetValue(o)); + Assert.AreEqual(false, prop1.ShouldSerializeValue(o)); + + PropertyDescriptor prop2 = properties[1]; + Assert.AreEqual("prop2", prop2.Name); + Assert.AreEqual(typeof(string), prop2.PropertyType); + Assert.AreEqual(typeof(JObject), prop2.ComponentType); + Assert.AreEqual(false, prop2.CanResetValue(o)); + Assert.AreEqual(false, prop2.ShouldSerializeValue(o)); + + PropertyDescriptor prop3 = properties[2]; + Assert.AreEqual("prop3", prop3.Name); + Assert.AreEqual(typeof(object), prop3.PropertyType); + Assert.AreEqual(typeof(JObject), prop3.ComponentType); + Assert.AreEqual(false, prop3.CanResetValue(o)); + Assert.AreEqual(false, prop3.ShouldSerializeValue(o)); + + PropertyDescriptor prop4 = properties[3]; + Assert.AreEqual("prop4", prop4.Name); + Assert.AreEqual(typeof(JArray), prop4.PropertyType); + Assert.AreEqual(typeof(JObject), prop4.ComponentType); + Assert.AreEqual(false, prop4.CanResetValue(o)); + Assert.AreEqual(false, prop4.ShouldSerializeValue(o)); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs index 24b5d56b..cea8516a 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -7,6 +7,9 @@ using NUnit.Framework; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Converters; using System.IO; +#if !PocketPC && !SILVERLIGHT +using System.Web.UI; +#endif namespace Newtonsoft.Json.Tests.Linq { @@ -556,5 +559,107 @@ Parameter name: arrayIndex")] // "JobTitle": "Manager" //} } + +#if !PocketPC && !SILVERLIGHT + [Test] + public void PropertyChanging() + { + object changing = null; + object changed = null; + int changingCount = 0; + int changedCount = 0; + + JObject o = new JObject(); + o.PropertyChanging += (sender, args) => + { + JObject s = (JObject) sender; + changing = (s[args.PropertyName] != null) ? ((JValue)s[args.PropertyName]).Value : null; + changingCount++; + }; + o.PropertyChanged += (sender, args) => + { + JObject s = (JObject)sender; + changed = (s[args.PropertyName] != null) ? ((JValue)s[args.PropertyName]).Value : null; + changedCount++; + }; + + o["StringValue"] = "value1"; + Assert.AreEqual(null, changing); + Assert.AreEqual("value1", changed); + Assert.AreEqual("value1", (string)o["StringValue"]); + Assert.AreEqual(1, changingCount); + Assert.AreEqual(1, changedCount); + + o["StringValue"] = "value1"; + Assert.AreEqual(1, changingCount); + Assert.AreEqual(1, changedCount); + + o["StringValue"] = "value2"; + Assert.AreEqual("value1", changing); + Assert.AreEqual("value2", changed); + Assert.AreEqual("value2", (string)o["StringValue"]); + Assert.AreEqual(2, changingCount); + Assert.AreEqual(2, changedCount); + + o["StringValue"] = null; + Assert.AreEqual("value2", changing); + Assert.AreEqual(null, changed); + Assert.AreEqual(null, (string)o["StringValue"]); + Assert.AreEqual(3, changingCount); + Assert.AreEqual(3, changedCount); + + o["NullValue"] = null; + Assert.AreEqual(null, changing); + Assert.AreEqual(null, changed); + Assert.AreEqual(new JValue((object)null), o["NullValue"]); + Assert.AreEqual(4, changingCount); + Assert.AreEqual(4, changedCount); + + o["NullValue"] = null; + Assert.AreEqual(4, changingCount); + Assert.AreEqual(4, changedCount); + } +#endif + + [Test] + public void PropertyChanged() + { + object changed = null; + int changedCount = 0; + + JObject o = new JObject(); + o.PropertyChanged += (sender, args) => + { + JObject s = (JObject)sender; + changed = (s[args.PropertyName] != null) ? ((JValue)s[args.PropertyName]).Value : null; + changedCount++; + }; + + o["StringValue"] = "value1"; + Assert.AreEqual("value1", changed); + Assert.AreEqual("value1", (string)o["StringValue"]); + Assert.AreEqual(1, changedCount); + + o["StringValue"] = "value1"; + Assert.AreEqual(1, changedCount); + + o["StringValue"] = "value2"; + Assert.AreEqual("value2", changed); + Assert.AreEqual("value2", (string)o["StringValue"]); + Assert.AreEqual(2, changedCount); + + o["StringValue"] = null; + Assert.AreEqual(null, changed); + Assert.AreEqual(null, (string)o["StringValue"]); + Assert.AreEqual(3, changedCount); + + o["NullValue"] = null; + Assert.AreEqual(null, changed); + Assert.AreEqual(new JValue((object)null), o["NullValue"]); + Assert.AreEqual(4, changedCount); + + o["NullValue"] = null; + Assert.AreEqual(4, changedCount); + } } } diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj index 8ee9bfa3..e70648aa 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj @@ -61,6 +61,10 @@ + + + + @@ -69,6 +73,7 @@ + @@ -132,7 +137,6 @@ - diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj index bfa1091a..1d9ca0e7 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj @@ -68,7 +68,12 @@ + + + + + @@ -136,7 +141,6 @@ - diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index 5d92e233..9f3e67a6 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -67,6 +67,10 @@ + + + + @@ -75,6 +79,7 @@ + @@ -138,7 +143,6 @@ - diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index 41b72e8b..82cb273e 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -105,6 +105,10 @@ + + + + @@ -173,7 +177,7 @@ - + diff --git a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs similarity index 90% rename from Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs rename to Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 25af25e3..5f30620f 100644 --- a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -46,7 +46,7 @@ using Newtonsoft.Json.Tests.TestObjects; using System.Runtime.Serialization; using System.Globalization; -namespace Newtonsoft.Json.Tests +namespace Newtonsoft.Json.Tests.Serialization { public class JsonSerializerTest : TestFixtureBase { @@ -1291,7 +1291,7 @@ keyword such as type of business."" } [Test] - [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Could not create an instance of type Newtonsoft.Json.Tests.JsonSerializerTest+ICo. Type is an interface or abstract class and cannot be instantated.")] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Could not create an instance of type Newtonsoft.Json.Tests.Serialization.JsonSerializerTest+ICo. Type is an interface or abstract class and cannot be instantated.")] public void DeserializeInterfaceProperty() { Test testClass = new Test(); @@ -2049,5 +2049,158 @@ keyword such as type of business."" NullableDateTimeTestClass c = JsonConvert.DeserializeObject(json); Assert.AreEqual(null, c.DateTimeField); } + + /// + /// What types of events are there? Just sticking to a basic set of four for now. + /// + /// + public enum EventType + { + Debug = 0, + Info = 1, + Warning = 2, + Error = 3 + } + + public sealed class Event + { + + /// + /// If no current user is specified, returns Nothing (0 from VB) + /// + /// + /// + private static int GetCurrentUserId() + { + return 0; + } + + /// + /// Gets either the application path or the current stack trace. + /// NOTE: You MUST call this from the top level entry point. Otherwise, + /// the stack trace will be buried in Logger itself. + /// + /// + /// + private static string GetCurrentSubLocation() + { + return ""; + } + + private string _sublocation; + private int _userId; + private EventType _type; + private string _summary; + private string _details; + private string _stackTrace; + private string _tag; + private DateTime _time; + + public Event(string summary) + { + _summary = summary; + _time = DateTime.Now; + + if (_userId == 0) _userId = GetCurrentUserId(); + //This call only works at top level for now. + //If _stackTrace = Nothing Then _stackTrace = Environment.StackTrace + if (_sublocation == null) _sublocation = GetCurrentSubLocation(); + } + + public Event(string sublocation, int userId, EventType type, string summary, string details, string stackTrace, string tag) + { + _sublocation = sublocation; + _userId = userId; + _type = type; + _summary = summary; + _details = details; + _stackTrace = stackTrace; + _tag = tag; + _time = DateTime.Now; + + if (_userId == 0) _userId = GetCurrentUserId(); + //If _stackTrace = Nothing Then _stackTrace = Environment.StackTrace + if (_sublocation == null) _sublocation = GetCurrentSubLocation(); + } + + public override string ToString() + { + return string.Format("{{ sublocation = {0}, userId = {1}, type = {2}, summary = {3}, details = {4}, stackTrace = {5}, tag = {6} }}", _sublocation, _userId, _type, _summary, _details, _stackTrace, _tag); + } + + public string sublocation + { + get { return _sublocation; } + set { _sublocation = value; } + } + public int userId + { + get { return _userId; } + set { _userId = value; } + } + public EventType type + { + get { return _type; } + set { _type = value; } + } + public string summary + { + get { return _summary; } + set { _summary = value; } + } + public string details + { + get { return _details; } + set { _details = value; } + } + public string stackTrace + { + get { return _stackTrace; } + set { _stackTrace = value; } + } + public string tag + { + get { return _tag; } + set { _tag = value; } + } + public DateTime time + { + get { return _time; } + } + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Unable to determine which constructor to use for type Newtonsoft.Json.Tests.Serialization.JsonSerializerTest+Event. A class with no default constructor should have only one constructor with arguments.")] + public void FailWhenClassWithNoDefaultConstructorHasMultipleConstructorsWithArguments() + { + string json = @"{""sublocation"":""AlertEmailSender.Program.Main"",""userId"":0,""type"":0,""summary"":""Loading settings variables"",""details"":null,""stackTrace"":"" at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)\r\n at System.Environment.get_StackTrace()\r\n at mr.Logging.Event..ctor(String summary) in C:\\Projects\\MRUtils\\Logging\\Event.vb:line 71\r\n at AlertEmailSender.Program.Main(String[] args) in C:\\Projects\\AlertEmailSender\\AlertEmailSender\\Program.cs:line 25"",""tag"":null,""time"":""\/Date(1249591032026-0400)\/""}"; + + Event e = JsonConvert.DeserializeObject(json); + } + + public class SetOnlyPropertyClass2 + { + private object _value; + public object SetOnlyProperty + { + set { _value = value; } + } + public object GetValue() + { + return _value; + } + } + + [Test] + public void DeserializeObjectSetOnlyProperty() + { + string json = @"{'SetOnlyProperty':[1,2,3,4,5]}"; + + SetOnlyPropertyClass2 setOnly = JsonConvert.DeserializeObject(json); + JArray a = (JArray)setOnly.GetValue(); + Assert.AreEqual(5, a.Count); + Assert.AreEqual(1, (int)a[0]); + Assert.AreEqual(5, (int)a[a.Count - 1]); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/PopulateTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/PopulateTests.cs index ad616391..f3e65e9f 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/PopulateTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/PopulateTests.cs @@ -1,4 +1,29 @@ -using System; +#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 + +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -15,8 +40,7 @@ namespace Newtonsoft.Json.Tests.Serialization { Person p = new Person(); - JsonSerializer serializer = new JsonSerializer(); - serializer.Populate(new StringReader(@"{""Name"":""James""}"), p); + JsonConvert.PopulateObject(@"{""Name"":""James""}", p); Assert.AreEqual("James", p.Name); } @@ -71,9 +95,10 @@ namespace Newtonsoft.Json.Tests.Serialization ] }"; - JsonSerializer serializer = new JsonSerializer(); - serializer.ObjectCreationHandling = ObjectCreationHandling.Replace; - serializer.Populate(new StringReader(json), s); + JsonConvert.PopulateObject(json, s, new JsonSerializerSettings + { + ObjectCreationHandling = ObjectCreationHandling.Replace + }); Assert.AreEqual(1, s.Mottos.Count); Assert.AreEqual("Fail whale", s.Mottos[0]); diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SerializationEventAttributeTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SerializationEventAttributeTests.cs index e0b018e0..61dec3e4 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SerializationEventAttributeTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SerializationEventAttributeTests.cs @@ -26,13 +26,18 @@ #if !PocketPC && !SILVERLIGHT && !NET20 using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; using System.Linq; +using System.Reflection; using System.Runtime.Serialization; using System.Text; +using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Tests; using Newtonsoft.Json.Tests.TestObjects; using NUnit.Framework; +using Newtonsoft.Json.Linq; +using System.Collections; namespace Newtonsoft.Json.Tests.Serialization { @@ -47,6 +52,7 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual("Hello World!", obj.Member2); Assert.AreEqual("This is a nonserialized value", obj.Member3); Assert.AreEqual(null, obj.Member4); + Assert.AreEqual(null, obj.Member5); string json = JsonConvert.SerializeObject(obj, Formatting.Indented); Assert.AreEqual(@"{ @@ -59,13 +65,22 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual("This value was reset after serialization.", obj.Member2); Assert.AreEqual("This is a nonserialized value", obj.Member3); Assert.AreEqual(null, obj.Member4); + Assert.AreEqual("Error message for member Member6 = Exception has been thrown by the target of an invocation.", obj.Member5); - obj = JsonConvert.DeserializeObject(json); + JObject o = JObject.Parse(@"{ + ""Member1"": 11, + ""Member2"": ""This value went into the data file during serialization."", + ""Member4"": null +}"); + o["Member6"] = "Dummy text for error"; + + obj = JsonConvert.DeserializeObject(o.ToString()); Assert.AreEqual(11, obj.Member1); Assert.AreEqual("This value went into the data file during serialization.", obj.Member2); Assert.AreEqual("This value was set during deserialization", obj.Member3); Assert.AreEqual("This value was set after deserialization.", obj.Member4); + Assert.AreEqual("Error message for member Member6 = Exception has been thrown by the target of an invocation.", obj.Member5); } [Test] @@ -98,6 +113,136 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual("This value was set after deserialization.", obj.Member4); } + public class ListErrorObject + { + public string Member + { + get { throw new Exception("ListErrorObject.Member get error!"); } + set { throw new Exception("ListErrorObject.Member set error!"); } + } + } + + public class ListErrorObjectCollection : Collection + { + [OnError] + internal void OnErrorMethod(StreamingContext context, ErrorContext errorContext) + { + errorContext.Handled = true; + } + } + + public class VersionKeyedCollection : KeyedCollection, IList + { + public List Messages { get; set; } + + public VersionKeyedCollection() + { + Messages = new List(); + } + + protected override string GetKeyForItem(Person item) + { + return item.Name; + } + + [OnError] + internal void OnErrorMethod(StreamingContext context, ErrorContext errorContext) + { + Messages.Add("Error message for member " + errorContext.Member + " = " + errorContext.Error.Message); + errorContext.Handled = true; + } + + object IList.this[int index] + { + get + { + if (index % 2 == 0) + throw new Exception("Index even: " + index); + + return this[index]; + } + set { this[index] = (Person)value; } + } + } + + [Test] + public void ErrorDeserializingListHandled() + { + string json = @"[ + { + ""Name"": ""Jim"", + ""BirthDate"": ""\/Date(978048000000)\/"", + ""LastModified"": ""\/Date(978048000000)\/"" + }, + { + ""Name"": ""Jim"", + ""BirthDate"": ""\/Date(978048000000)\/"", + ""LastModified"": ""\/Date(978048000000)\/"" + } +]"; + + VersionKeyedCollection c = JsonConvert.DeserializeObject(json); + Assert.AreEqual(1, c.Count); + Assert.AreEqual(1, c.Messages.Count); + Assert.AreEqual("Error message for member 1 = An item with the same key has already been added.", c.Messages[0]); + } + + [Test] + public void ErrorSerializingListHandled() + { + VersionKeyedCollection c = new VersionKeyedCollection(); + c.Add(new Person + { + Name = "Jim", + BirthDate = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc), + LastModified = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc) + }); + c.Add(new Person + { + Name = "Jimbo", + BirthDate = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc), + LastModified = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc) + }); + c.Add(new Person + { + Name = "Jimmy", + BirthDate = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc), + LastModified = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc) + }); + c.Add(new Person + { + Name = "Jim Bean", + BirthDate = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc), + LastModified = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc) + }); + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"[ + { + ""Name"": ""Jimbo"", + ""BirthDate"": ""\/Date(978048000000)\/"", + ""LastModified"": ""\/Date(978048000000)\/"" + }, + { + ""Name"": ""Jim Bean"", + ""BirthDate"": ""\/Date(978048000000)\/"", + ""LastModified"": ""\/Date(978048000000)\/"" + } +]", json); + + Assert.AreEqual(2, c.Messages.Count); + Assert.AreEqual("Error message for member 0 = Index even: 0", c.Messages[0]); + Assert.AreEqual("Error message for member 2 = Index even: 2", c.Messages[1]); + } + + [Test] + [ExpectedException(typeof(TargetInvocationException))] + public void ErrorInChild() + { + JsonConvert.DeserializeObject(@"[{""Member"":""Value""}]"); + } + [Test] public void ListEvents() { @@ -187,6 +332,8 @@ namespace Newtonsoft.Json.Tests.Serialization // This is a nonserialized value Console.WriteLine(obj.Member4); // null + Console.WriteLine(obj.Member5); + // null string json = JsonConvert.SerializeObject(obj, Formatting.Indented); // { @@ -203,6 +350,8 @@ namespace Newtonsoft.Json.Tests.Serialization // This is a nonserialized value Console.WriteLine(obj.Member4); // null + Console.WriteLine(obj.Member5); + // Error message for member Member6 = Exception has been thrown by the target of an invocation. obj = JsonConvert.DeserializeObject(json); diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestList.cs b/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestList.cs index 0b712023..0583b735 100644 --- a/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestList.cs +++ b/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestList.cs @@ -26,6 +26,7 @@ #if !PocketPC && !SILVERLIGHT using System.Collections.ObjectModel; using System.Runtime.Serialization; +using Newtonsoft.Json.Tests.Serialization; namespace Newtonsoft.Json.Tests.TestObjects { diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObject.cs b/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObject.cs index 190d476d..8c9d4374 100644 --- a/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObject.cs +++ b/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObject.cs @@ -23,11 +23,12 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion -#if !PocketPC && !SILVERLIGHT +#if !PocketPC && !SILVERLIGHT && !NET20 using System; using System.Linq; using System.Runtime.Serialization; using System.Text; +using Newtonsoft.Json.Serialization; namespace Newtonsoft.Json.Tests.TestObjects { @@ -48,6 +49,17 @@ namespace Newtonsoft.Json.Tests.TestObjects // This field is set to null, but populated after deserialization. public string Member4 { get; set; } + // This field is set to null, but populated after error. + [JsonIgnore] + public string Member5 { get; set; } + + // Getting or setting this field will throw an error. + public string Member6 + { + get { throw new Exception("Member5 get error!"); } + set { throw new Exception("Member5 set error!"); } + } + public SerializationEventTestObject() { Member1 = 11; @@ -79,6 +91,13 @@ namespace Newtonsoft.Json.Tests.TestObjects { Member4 = "This value was set after deserialization."; } + + [OnError] + internal void OnErrorMethod(StreamingContext context, ErrorContext errorContext) + { + Member5 = "Error message for member " + errorContext.Member + " = " + errorContext.Error.Message; + errorContext.Handled = true; + } } } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObjectWithConstructor.cs b/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObjectWithConstructor.cs index 943da942..6b315272 100644 --- a/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObjectWithConstructor.cs +++ b/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObjectWithConstructor.cs @@ -24,7 +24,9 @@ #endregion #if !PocketPC && !SILVERLIGHT +using System; using System.Runtime.Serialization; +using Newtonsoft.Json.Serialization; namespace Newtonsoft.Json.Tests.TestObjects { diff --git a/Src/Newtonsoft.Json/Converters/EntityKeyMemberConverter.cs b/Src/Newtonsoft.Json/Converters/EntityKeyMemberConverter.cs index 031f3101..44436639 100644 --- a/Src/Newtonsoft.Json/Converters/EntityKeyMemberConverter.cs +++ b/Src/Newtonsoft.Json/Converters/EntityKeyMemberConverter.cs @@ -25,9 +25,6 @@ #if !PocketPC && !SILVERLIGHT && !NET20 using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Data; using Newtonsoft.Json.Serialization; using System.Globalization; @@ -53,7 +50,7 @@ namespace Newtonsoft.Json.Converters if (keyType != null) { string valueJson; - if (JsonSerializerWriter.TryConvertToString(entityKeyMember.Value, keyType, out valueJson)) + if (JsonSerializerInternalWriter.TryConvertToString(entityKeyMember.Value, keyType, out valueJson)) writer.WriteValue(valueJson); else writer.WriteValue(entityKeyMember.Value); diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index d62395e5..6b6eb17e 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -628,6 +628,26 @@ namespace Newtonsoft.Json return deserializedValue; } + public static void PopulateObject(string value, object target) + { + PopulateObject(value, target, null); + } + + + public static void PopulateObject(string value, object target, JsonSerializerSettings settings) + { + StringReader sr = new StringReader(value); + JsonSerializer jsonSerializer = JsonSerializer.Create(settings); + + using (JsonReader jsonReader = new JsonTextReader(sr)) + { + jsonSerializer.Populate(jsonReader, target); + + if (jsonReader.Read() && jsonReader.TokenType != JsonToken.Comment) + throw new JsonSerializationException("Additional text found in JSON string after finishing deserializing object."); + } + } + #if !SILVERLIGHT /// /// Serializes the XML node to a JSON string. diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 76309c9c..9084247d 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -309,7 +309,7 @@ namespace Newtonsoft.Json ValidationUtils.ArgumentNotNull(reader, "reader"); ValidationUtils.ArgumentNotNull(target, "target"); - JsonSerializerReader serializerReader = new JsonSerializerReader(this); + JsonSerializerInternalReader serializerReader = new JsonSerializerInternalReader(this); serializerReader.Populate(reader, target); } @@ -351,7 +351,7 @@ namespace Newtonsoft.Json { ValidationUtils.ArgumentNotNull(reader, "reader"); - JsonSerializerReader serializerReader = new JsonSerializerReader(this); + JsonSerializerInternalReader serializerReader = new JsonSerializerInternalReader(this); return serializerReader.Deserialize(reader, objectType); } @@ -381,7 +381,7 @@ namespace Newtonsoft.Json { ValidationUtils.ArgumentNotNull(jsonWriter, "jsonWriter"); - JsonSerializerWriter serializerWriter = new JsonSerializerWriter(this); + JsonSerializerInternalWriter serializerWriter = new JsonSerializerInternalWriter(this); serializerWriter.Serialize(jsonWriter, value); } diff --git a/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs b/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs new file mode 100644 index 00000000..81edf638 --- /dev/null +++ b/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs @@ -0,0 +1,158 @@ +#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 + +#if !SILVERLIGHT +using System; +using System.ComponentModel; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq.ComponentModel +{ + /// + /// Represents a view of a . + /// + public class JPropertyDescriptor : PropertyDescriptor + { + private readonly Type _propertyType; + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// Type of the property. + public JPropertyDescriptor(string name, Type propertyType) + : base(name, null) + { + ValidationUtils.ArgumentNotNull(name, "name"); + ValidationUtils.ArgumentNotNull(propertyType, "propertyType"); + + _propertyType = propertyType; + } + + private static JObject CastInstance(object instance) + { + return (JObject)instance; + } + + /// + /// When overridden in a derived class, returns whether resetting an object changes its value. + /// + /// + /// true if resetting the component changes its value; otherwise, false. + /// + /// The component to test for reset capability. + /// + public override bool CanResetValue(object component) + { + return false; + } + + /// + /// When overridden in a derived class, gets the current value of the property on a component. + /// + /// + /// The value of a property for a given component. + /// + /// The component with the property for which to retrieve the value. + /// + public override object GetValue(object component) + { + JToken token = CastInstance(component)[Name]; + if (token is JValue) + return ((JValue) token).Value; + + return token; + } + + /// + /// When overridden in a derived class, resets the value for this property of the component to the default value. + /// + /// The component with the property value that is to be reset to the default value. + /// + public override void ResetValue(object component) + { + } + + /// + /// When overridden in a derived class, sets the value of the component to a different value. + /// + /// The component with the property value that is to be set. + /// The new value. + /// + public override void SetValue(object component, object value) + { + JToken token = (value is JToken) ? (JToken) value : new JValue(value); + + CastInstance(component)[Name] = token; + } + + /// + /// When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted. + /// + /// + /// true if the property should be persisted; otherwise, false. + /// + /// The component with the property to be examined for persistence. + /// + public override bool ShouldSerializeValue(object component) + { + return false; + } + + /// + /// When overridden in a derived class, gets the type of the component this property is bound to. + /// + /// + /// A that represents the type of component this property is bound to. When the or methods are invoked, the object specified might be an instance of this type. + /// + public override Type ComponentType + { + get { return typeof(JObject); } + } + + /// + /// When overridden in a derived class, gets a value indicating whether this property is read-only. + /// + /// + /// true if the property is read-only; otherwise, false. + /// + public override bool IsReadOnly + { + get { return false; } + } + + /// + /// When overridden in a derived class, gets the type of the property. + /// + /// + /// A that represents the type of the property. + /// + public override Type PropertyType + { + get { return _propertyType; } + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/ComponentModel/JTypeDescriptionProvider.cs b/Src/Newtonsoft.Json/Linq/ComponentModel/JTypeDescriptionProvider.cs new file mode 100644 index 00000000..c5f5fc12 --- /dev/null +++ b/Src/Newtonsoft.Json/Linq/ComponentModel/JTypeDescriptionProvider.cs @@ -0,0 +1,47 @@ +#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 + +#if !PocketPC && !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Linq.ComponentModel +{ + internal class JTypeDescriptionProvider : TypeDescriptionProvider + { + public override ICustomTypeDescriptor GetTypeDescriptor(Type type, object instance) + { + JObject o = instance as JObject; + if (o != null) + return new JTypeDescriptor(o); + + return base.GetTypeDescriptor(type, instance); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/ComponentModel/JTypeDescriptor.cs b/Src/Newtonsoft.Json/Linq/ComponentModel/JTypeDescriptor.cs new file mode 100644 index 00000000..604e9b95 --- /dev/null +++ b/Src/Newtonsoft.Json/Linq/ComponentModel/JTypeDescriptor.cs @@ -0,0 +1,209 @@ +#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 + +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq.ComponentModel +{ + /// + /// Represents a view of a . + /// + public class JTypeDescriptor : ICustomTypeDescriptor + { + private readonly JObject _value; + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public JTypeDescriptor(JObject value) + { + ValidationUtils.ArgumentNotNull(value, "value"); + _value = value; + } + + /// + /// Returns the properties for this instance of a component. + /// + /// + /// A that represents the properties for this component instance. + /// + public virtual PropertyDescriptorCollection GetProperties() + { + return GetProperties(null); + } + + private static Type GetTokenPropertyType(JToken token) + { + if (token is JValue) + { + JValue v = (JValue) token; + return (v.Value != null) ? v.Value.GetType() : typeof (object); + } + + return token.GetType(); + } + + /// + /// Returns the properties for this instance of a component using the attribute array as a filter. + /// + /// An array of type that is used as a filter. + /// + /// A that represents the filtered properties for this component instance. + /// + public virtual PropertyDescriptorCollection GetProperties(Attribute[] attributes) + { + PropertyDescriptorCollection descriptors = new PropertyDescriptorCollection(null); + + if (_value != null) + { + foreach (KeyValuePair propertyValue in _value) + { + descriptors.Add(new JPropertyDescriptor(propertyValue.Key, GetTokenPropertyType(propertyValue.Value))); + } + } + + return descriptors; + } + + /// + /// Returns a collection of custom attributes for this instance of a component. + /// + /// + /// An containing the attributes for this object. + /// + public AttributeCollection GetAttributes() + { + return AttributeCollection.Empty; + } + + /// + /// Returns the class name of this instance of a component. + /// + /// + /// The class name of the object, or null if the class does not have a name. + /// + public string GetClassName() + { + return null; + } + + /// + /// Returns the name of this instance of a component. + /// + /// + /// The name of the object, or null if the object does not have a name. + /// + public string GetComponentName() + { + return null; + } + + /// + /// Returns a type converter for this instance of a component. + /// + /// + /// A that is the converter for this object, or null if there is no for this object. + /// + public TypeConverter GetConverter() + { + return new TypeConverter(); + } + + /// + /// Returns the default event for this instance of a component. + /// + /// + /// An that represents the default event for this object, or null if this object does not have events. + /// + public EventDescriptor GetDefaultEvent() + { + return null; + } + + /// + /// Returns the default property for this instance of a component. + /// + /// + /// A that represents the default property for this object, or null if this object does not have properties. + /// + public PropertyDescriptor GetDefaultProperty() + { + return null; + } + + /// + /// Returns an editor of the specified type for this instance of a component. + /// + /// A that represents the editor for this object. + /// + /// An of the specified type that is the editor for this object, or null if the editor cannot be found. + /// + public object GetEditor(Type editorBaseType) + { + return null; + } + + /// + /// Returns the events for this instance of a component using the specified attribute array as a filter. + /// + /// An array of type that is used as a filter. + /// + /// An that represents the filtered events for this component instance. + /// + public EventDescriptorCollection GetEvents(Attribute[] attributes) + { + return EventDescriptorCollection.Empty; + } + + /// + /// Returns the events for this instance of a component. + /// + /// + /// An that represents the events for this component instance. + /// + public EventDescriptorCollection GetEvents() + { + return EventDescriptorCollection.Empty; + } + + /// + /// Returns an object that contains the property described by the specified property descriptor. + /// + /// A that represents the property whose owner is to be found. + /// + /// An that represents the owner of the specified property. + /// + public object GetPropertyOwner(PropertyDescriptor pd) + { + return null; + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index d9cdcaa3..8a1106f0 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -204,7 +204,7 @@ namespace Newtonsoft.Json.Linq { if (IsMultiContent(content)) { - IEnumerable enumerable = content as IEnumerable; + IEnumerable enumerable = (IEnumerable)content; JToken multiPrevious = previous; foreach (object c in enumerable) diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index db0042d7..2aebaefe 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -25,19 +25,39 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; -using System.Text; using System.IO; using Newtonsoft.Json.Utilities; using System.Globalization; +#if !PocketPC && !SILVERLIGHT +using Newtonsoft.Json.Linq.ComponentModel; +#endif namespace Newtonsoft.Json.Linq { /// /// Represents a JSON object. /// - public class JObject : JContainer, IDictionary +#if !PocketPC && !SILVERLIGHT + [TypeDescriptionProvider(typeof(JTypeDescriptionProvider))] +#endif + public class JObject : JContainer, IDictionary, INotifyPropertyChanged +#if !PocketPC && !SILVERLIGHT + , INotifyPropertyChanging +#endif { + /// + /// Occurs when a property value changes. + /// + public event PropertyChangedEventHandler PropertyChanged; +#if !PocketPC && !SILVERLIGHT + /// + /// Occurs when a property value is changing. + /// + public event PropertyChangingEventHandler PropertyChanging; +#endif + /// /// Initializes a new instance of the class. /// @@ -171,12 +191,42 @@ namespace Newtonsoft.Json.Linq { JProperty property = Property(propertyName); if (property != null) - property.Value = value; + { + if (!IsTokenUnchanged(property.Value, value)) + { +#if !PocketPC && !SILVERLIGHT + OnPropertyChanging(property.Name); +#endif + property.Value = value; + OnPropertyChanged(property.Name); + } + } else + { +#if !PocketPC && !SILVERLIGHT + OnPropertyChanging(propertyName); +#endif Add(new JProperty(propertyName, value)); + OnPropertyChanged(propertyName); + } } } + private static bool IsTokenUnchanged(JToken currentValue, JToken newValue) + { + JValue v1 = currentValue as JValue; + if (v1 != null) + { + // null will get turned into a JValue of type null + if (v1.Type == JTokenType.Null && newValue == null) + return true; + + return v1.Equals(newValue); + } + + return false; + } + /// /// Loads an from a . /// @@ -219,7 +269,6 @@ namespace Newtonsoft.Json.Linq return Load(jsonReader); } - /// /// Creates a from an object. /// @@ -246,7 +295,6 @@ namespace Newtonsoft.Json.Linq return (JObject)token; } - internal override void ValidateObject(JToken o, JToken previous) { ValidationUtils.ArgumentNotNull(o, "o"); @@ -418,5 +466,19 @@ namespace Newtonsoft.Json.Linq yield return new KeyValuePair(property.Name, property.Value); } } + + protected virtual void OnPropertyChanged(string propertyName) + { + if (PropertyChanged != null) + PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + +#if !PocketPC && !SILVERLIGHT + protected virtual void OnPropertyChanging(string propertyName) + { + if (PropertyChanging != null) + PropertyChanging(this, new PropertyChangingEventArgs(propertyName)); + } +#endif } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index 01d435e5..97c88667 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -35,7 +35,7 @@ namespace Newtonsoft.Json.Linq /// /// Represents a value in JSON (string, integer, date, etc). /// - public class JValue : JToken + public class JValue : JToken, IEquatable { private JTokenType _valueType; private object _value; @@ -327,5 +327,50 @@ namespace Newtonsoft.Json.Linq return _valueType.GetHashCode() ^ valueHashCode; } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// + /// true if the current object is equal to the parameter; otherwise, false. + /// + /// An object to compare with this object. + public bool Equals(JValue other) + { + if (other == null) + return false; + + return (_value == other._value); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// + /// The parameter is null. + /// + public override bool Equals(object obj) + { + if (obj == null) + return false; + + JValue otherValue = obj as JValue; + if (otherValue != null) + return Equals(otherValue); + + return base.Equals(obj); + } + + public override int GetHashCode() + { + if (_value == null) + return 0; + + return _value.GetHashCode(); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj index c44903bf..d9a7a5a7 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj @@ -70,6 +70,9 @@ + + + @@ -121,6 +124,7 @@ + @@ -138,10 +142,12 @@ + + + - - + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index e8306d94..9c118e63 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -81,6 +81,9 @@ + + + @@ -139,6 +142,7 @@ + @@ -155,11 +159,13 @@ + + + - - + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index 4cf3632b..1069caf5 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -82,6 +82,9 @@ + + + @@ -117,9 +120,10 @@ + - - + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 9e7613e9..78f2b6e3 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -90,6 +90,9 @@ + + + @@ -148,6 +151,7 @@ + @@ -164,11 +168,13 @@ + + + - - + diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 482ea5bf..96b9f689 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -183,6 +183,10 @@ namespace Newtonsoft.Json.Serialization { contract.OnDeserialized = method; } + if (IsValidCallback(method, parameters, typeof(OnErrorAttribute), contract.OnError, ref prevAttributeType)) + { + contract.OnError = method; + } } #endif } @@ -249,8 +253,16 @@ namespace Newtonsoft.Json.Serialization if (method.ReturnType != typeof(void)) throw new Exception("Serialization Callback '{1}' in type '{0}' must return void.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method)); - if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != typeof(StreamingContext)) - throw new Exception("Serialization Callback '{1}' in type '{0}' must have a single parameter of type '{2}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method, typeof(StreamingContext))); + if (attributeType == typeof(OnErrorAttribute)) + { + if (parameters == null || parameters.Length != 2 || parameters[0].ParameterType != typeof(StreamingContext) || parameters[1].ParameterType != typeof(ErrorContext)) + throw new Exception("Serialization Error Callback '{1}' in type '{0}' must have two parameters of type '{2}' and '{3}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method, typeof (StreamingContext), typeof(ErrorContext))); + } + else + { + if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != typeof(StreamingContext)) + throw new Exception("Serialization Callback '{1}' in type '{0}' must have a single parameter of type '{2}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method, typeof(StreamingContext))); + } prevAttributeType = attributeType; diff --git a/Src/Newtonsoft.Json/Serialization/ErrorContext.cs b/Src/Newtonsoft.Json/Serialization/ErrorContext.cs new file mode 100644 index 00000000..1dff26df --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/ErrorContext.cs @@ -0,0 +1,68 @@ +#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 + +#if !PocketPC && !SILVERLIGHT && !NET20 +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Provides information surrounding an error. + /// + public class ErrorContext + { + internal ErrorContext(object originalObject, object member, Exception error) + { + OriginalObject = originalObject; + Member = member; + Error = error; + } + + /// + /// Gets or sets the error. + /// + /// The error. + public Exception Error { get; private set; } + /// + /// Gets the original object that caused the error. + /// + /// The original object that caused the error. + public object OriginalObject { get; private set; } + /// + /// Gets the member that caused the error. + /// + /// The member that caused the error. + public object Member { get; private set; } + /// + /// Gets or sets a value indicating whether this is handled. + /// + /// true if handled; otherwise, false. + public bool Handled { get; set; } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonContract.cs b/Src/Newtonsoft.Json/Serialization/JsonContract.cs index 6655fe24..f02984df 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonContract.cs @@ -47,7 +47,8 @@ namespace Newtonsoft.Json.Serialization public bool? IsReference { get; set; } #if !PocketPC && !SILVERLIGHT - private static readonly object[] SerializationEventParameterValues = new object[] { new StreamingContext(StreamingContextStates.All) }; + private static readonly StreamingContext SerializationStreamingContextParameter = new StreamingContext(StreamingContextStates.All); + private static readonly object[] SerializationEventParameterValues = new object[] { SerializationStreamingContextParameter }; /// /// Gets or sets the method called immediately after deserialization of the object. @@ -69,6 +70,8 @@ namespace Newtonsoft.Json.Serialization /// /// The method called before serialization of the object. public MethodInfo OnSerializing { get; set; } + + public MethodInfo OnError { get; set; } #endif internal void InvokeOnSerializing(object o) @@ -103,6 +106,14 @@ namespace Newtonsoft.Json.Serialization #endif } +#if !PocketPC && !SILVERLIGHT && !NET20 + internal void InvokeOnError(object o, ErrorContext errorContext) + { + if (OnError != null && o == errorContext.OriginalObject) + OnError.Invoke(o, new object[] { SerializationStreamingContextParameter, errorContext }); + } +#endif + internal JsonContract(Type underlyingType) { ValidationUtils.ArgumentNotNull(underlyingType, "underlyingType"); diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs new file mode 100644 index 00000000..c3cd4feb --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs @@ -0,0 +1,57 @@ +#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 + +using System; +using System.Net; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + internal abstract class JsonSerializerInternalBase + { +#if !PocketPC && !SILVERLIGHT && !NET20 + private ErrorContext _currentErrorContext; + + protected ErrorContext GetErrorContext(object originalObject, object member, Exception error) + { + if (_currentErrorContext == null) + _currentErrorContext = new ErrorContext(originalObject, member, error); + + if (_currentErrorContext.Error != error) + throw new InvalidOperationException("Current error context error is different to requested error."); + + return _currentErrorContext; + } + + protected void ClearErrorContext() + { + if (_currentErrorContext == null) + throw new InvalidOperationException("Could not clear error context. Error context is already null."); + + _currentErrorContext = null; + } +#endif + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs similarity index 86% rename from Src/Newtonsoft.Json/Serialization/JsonSerializerReader.cs rename to Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index c55cb05a..a665c0fb 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -35,12 +35,12 @@ using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json.Serialization { - internal class JsonSerializerReader + internal class JsonSerializerInternalReader : JsonSerializerInternalBase { internal readonly JsonSerializer _serializer; private JsonSerializerProxy _internalSerializer; - public JsonSerializerReader(JsonSerializer serializer) + public JsonSerializerInternalReader(JsonSerializer serializer) { ValidationUtils.ArgumentNotNull(serializer, "serializer"); @@ -79,7 +79,7 @@ namespace Newtonsoft.Json.Serialization else if (contract is JsonObjectContract) PopulateObject(target, reader, (JsonObjectContract) contract, id); else - throw new Exception("dfsdfsdf"); + throw new JsonSerializationException("Expected a JsonObjectContract or JsonDictionaryContract for type '{0}', got '{1}'.".FormatWith(CultureInfo.InvariantCulture, objectType, contract.GetType())); } } @@ -292,17 +292,16 @@ namespace Newtonsoft.Json.Serialization return PopulateDictionary(CollectionUtils.CreateDictionaryWrapper(existingValue), reader, (JsonDictionaryContract) contract, id); } - else if (contract is JsonObjectContract) + + if (contract is JsonObjectContract) { if (existingValue == null) return CreateAndPopulateObject(reader, (JsonObjectContract) contract, id); return PopulateObject(existingValue, reader, (JsonObjectContract) contract, id); } - else - { - throw new JsonSerializationException("Expected a JsonObjectContract or JsonDictionaryContract for type '{0}', got '{1}'.".FormatWith(CultureInfo.InvariantCulture, objectType, contract.GetType())); - } + + throw new JsonSerializationException("Expected a JsonObjectContract or JsonDictionaryContract for type '{0}', got '{1}'.".FormatWith(CultureInfo.InvariantCulture, objectType, contract.GetType())); } private JsonArrayContract GetArrayContract(Type objectType) @@ -400,7 +399,8 @@ namespace Newtonsoft.Json.Serialization bool useExistingValue = false; if ((_serializer.ObjectCreationHandling == ObjectCreationHandling.Auto || _serializer.ObjectCreationHandling == ObjectCreationHandling.Reuse) - && (reader.TokenType == JsonToken.StartArray || reader.TokenType == JsonToken.StartObject)) + && (reader.TokenType == JsonToken.StartArray || reader.TokenType == JsonToken.StartObject) + && property.Readable) { currentValue = ReflectionUtils.GetMemberValue(property.Member, target); @@ -457,7 +457,22 @@ namespace Newtonsoft.Json.Serialization object keyValue = EnsureType(reader.Value, contract.DictionaryKeyType); CheckedRead(reader); - dictionary.Add(keyValue, CreateValue(reader, contract.DictionaryValueType, null, null)); + try + { + dictionary.Add(keyValue, CreateValue(reader, contract.DictionaryValueType, null, null)); + } + catch (Exception ex) + { +#if !PocketPC && !SILVERLIGHT && !NET20 + ErrorContext errorContext = GetErrorContext(dictionary.UnderlyingDictionary, keyValue, ex); + contract.InvokeOnError(dictionary.UnderlyingDictionary, errorContext); + + if (errorContext.Handled) + ClearErrorContext(); + else +#endif + throw; + } break; case JsonToken.EndObject: contract.InvokeOnDeserialized(dictionary.UnderlyingDictionary); @@ -481,6 +496,8 @@ namespace Newtonsoft.Json.Serialization #if !PocketPC && !SILVERLIGHT if (contract.OnSerializing != null && isTemporaryListReference) throw new JsonSerializationException("Cannot call OnSerializing on an array or readonly list: {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + if (contract.OnError != null && isTemporaryListReference) + throw new JsonSerializationException("Cannot call OnError on an array or readonly list: {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); #endif PopulateList(l, reader, reference, contract); @@ -505,9 +522,24 @@ namespace Newtonsoft.Json.Serialization case JsonToken.Comment: break; default: - object value = CreateValue(reader, contract.CollectionItemType, null, null); + try + { + object value = CreateValue(reader, contract.CollectionItemType, null, null); - list.Add(value); + list.Add(value); + } + catch (Exception ex) + { +#if !PocketPC && !SILVERLIGHT && !NET20 + ErrorContext errorContext = GetErrorContext(list, list.Count, ex); + contract.InvokeOnError(list, errorContext); + + if (errorContext.Handled) + ClearErrorContext(); + else +#endif + throw; + } break; } } @@ -537,11 +569,15 @@ namespace Newtonsoft.Json.Serialization { Type objectType = contract.UnderlyingType; - // object should have a single constructor - ConstructorInfo c = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).SingleOrDefault(); + ConstructorInfo[] constructors = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance); - if (c == null) + if (constructors.Length == 0) throw new JsonSerializationException("Could not find a public constructor for type {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + if (constructors.Length > 1) + throw new JsonSerializationException("Unable to determine which constructor to use for type {0}. A class with no default constructor should have only one constructor with arguments.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + // object should have a single constructor + ConstructorInfo c = constructors[0]; // create a dictionary to put retrieved values into IDictionary propertyValues = contract.Properties.Where(p => !p.Ignored).ToDictionary(kv => kv, kv => (object)null); @@ -631,8 +667,23 @@ namespace Newtonsoft.Json.Serialization if (reader.TokenType != JsonToken.Null) SetRequiredProperty(memberName, requiredProperties); - - SetObjectMember(reader, newObject, contract, memberName); + + try + { + SetObjectMember(reader, newObject, contract, memberName); + } + catch (Exception ex) + { +#if !PocketPC && !SILVERLIGHT && !NET20 + ErrorContext errorContext = GetErrorContext(newObject, memberName, ex); + contract.InvokeOnError(newObject, errorContext); + + if (errorContext.Handled) + ClearErrorContext(); + else +#endif + throw; + } break; case JsonToken.EndObject: foreach (KeyValuePair requiredProperty in requiredProperties) diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs similarity index 83% rename from Src/Newtonsoft.Json/Serialization/JsonSerializerWriter.cs rename to Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 27e6498e..34e9d330 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -36,12 +36,12 @@ using System.Runtime.Serialization; namespace Newtonsoft.Json.Serialization { - internal class JsonSerializerWriter + internal class JsonSerializerInternalWriter : JsonSerializerInternalBase { internal readonly JsonSerializer _serializer; private JsonSerializerProxy _internalSerializer; private List _serializeStack; - + private List SerializeStack { get @@ -53,7 +53,7 @@ namespace Newtonsoft.Json.Serialization } } - public JsonSerializerWriter(JsonSerializer serializer) + public JsonSerializerInternalWriter(JsonSerializer serializer) { ValidationUtils.ArgumentNotNull(serializer, "serializer"); @@ -287,8 +287,23 @@ namespace Newtonsoft.Json.Serialization foreach (JsonProperty property in contract.Properties) { - if (!property.Ignored && property.Readable) - WriteMemberInfoProperty(writer, value, property); + try + { + if (!property.Ignored && property.Readable) + WriteMemberInfoProperty(writer, value, property); + } + catch (Exception ex) + { +#if !PocketPC && !SILVERLIGHT && !NET20 + ErrorContext errorContext = GetErrorContext(value, property.Member.Name, ex); + contract.InvokeOnError(value, errorContext); + + if (errorContext.Handled) + ClearErrorContext(); + else +#endif + throw; + } } writer.WriteEndObject(); @@ -366,18 +381,33 @@ namespace Newtonsoft.Json.Serialization for (int i = 0; i < values.Count; i++) { - object value = values[i]; - - if (ShouldWriteReference(value, null)) + try { - WriteReference(writer, value); + object value = values[i]; + + if (ShouldWriteReference(value, null)) + { + WriteReference(writer, value); + } + else + { + if (!CheckForCircularReference(value, null)) + continue; + + SerializeValue(writer, value, null); + } } - else + catch (Exception ex) { - if (!CheckForCircularReference(value, null)) - continue; +#if !PocketPC && !SILVERLIGHT && !NET20 + ErrorContext errorContext = GetErrorContext(values, i, ex); + contract.InvokeOnError(values, errorContext); - SerializeValue(writer, value, null); + if (errorContext.Handled) + ClearErrorContext(); + else +#endif + throw; } } @@ -414,20 +444,37 @@ namespace Newtonsoft.Json.Serialization foreach (DictionaryEntry entry in values) { string propertyName = entry.Key.ToString(); - object value = entry.Value; - if (ShouldWriteReference(value, null)) + try { - writer.WritePropertyName(propertyName); - WriteReference(writer, value); + object value = entry.Value; + + if (ShouldWriteReference(value, null)) + { + writer.WritePropertyName(propertyName); + WriteReference(writer, value); + } + else + { + if (!CheckForCircularReference(value, null)) + continue; + + writer.WritePropertyName(propertyName); + + SerializeValue(writer, value, null); + } } - else + catch (Exception ex) { - if (!CheckForCircularReference(value, null)) - continue; +#if !PocketPC && !SILVERLIGHT && !NET20 + ErrorContext errorContext = GetErrorContext(values, propertyName, ex); + contract.InvokeOnError(values, errorContext); - writer.WritePropertyName(propertyName); - SerializeValue(writer, value, null); + if (errorContext.Handled) + ClearErrorContext(); + else +#endif + throw; } } diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs index f647e88e..9fbf2604 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs @@ -31,8 +31,8 @@ namespace Newtonsoft.Json.Serialization { internal class JsonSerializerProxy : JsonSerializer { - private readonly JsonSerializerReader _serializerReader; - private readonly JsonSerializerWriter _serializerWriter; + private readonly JsonSerializerInternalReader _serializerReader; + private readonly JsonSerializerInternalWriter _serializerWriter; private readonly JsonSerializer _serializer; public override IReferenceResolver ReferenceResolver @@ -100,7 +100,7 @@ namespace Newtonsoft.Json.Serialization set { _serializer.Binder = value; } } - public JsonSerializerProxy(JsonSerializerReader serializerReader) + public JsonSerializerProxy(JsonSerializerInternalReader serializerReader) { ValidationUtils.ArgumentNotNull(serializerReader, "serializerReader"); @@ -108,7 +108,7 @@ namespace Newtonsoft.Json.Serialization _serializer = serializerReader._serializer; } - public JsonSerializerProxy(JsonSerializerWriter serializerWriter) + public JsonSerializerProxy(JsonSerializerInternalWriter serializerWriter) { ValidationUtils.ArgumentNotNull(serializerWriter, "serializerWriter"); diff --git a/Src/Newtonsoft.Json/Serialization/OnErrorAttribute.cs b/Src/Newtonsoft.Json/Serialization/OnErrorAttribute.cs new file mode 100644 index 00000000..bbb8b77d --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/OnErrorAttribute.cs @@ -0,0 +1,39 @@ +#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 + +#if !PocketPC && !SILVERLIGHT && !NET20 +using System; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// When applied to a method, specifies that the method is called when an error occurs serializing an object. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class OnErrorAttribute : Attribute + { + } +} +#endif \ No newline at end of file