diff --git a/Build/build.ps1 b/Build/build.ps1 index 7270fe6f..db021a26 100644 --- a/Build/build.ps1 +++ b/Build/build.ps1 @@ -15,7 +15,8 @@ $builds = @( @{Name = "Newtonsoft.Json"; TestsName = "Newtonsoft.Json.Tests"; Constants=""; FinalDir="DotNet"; Framework="net-4.0"}, @{Name = "Newtonsoft.Json.Silverlight"; TestsName = "Newtonsoft.Json.Tests.Silverlight"; Constants="SILVERLIGHT"; FinalDir="Silverlight"; Framework="net-2.0"}, - @{Name = "Newtonsoft.Json.Net20"; TestsName = "Newtonsoft.Json.Tests.Net20"; Constants="NET20"; FinalDir="DotNet20"; Framework="net-2.0"} + @{Name = "Newtonsoft.Json.Net20"; TestsName = "Newtonsoft.Json.Tests.Net20"; Constants="NET20"; FinalDir="DotNet20"; Framework="net-2.0"}, + @{Name = "Newtonsoft.Json.Net35"; TestsName = "Newtonsoft.Json.Tests.Net35"; Constants="NET35"; FinalDir="DotNet35"; Framework="net-2.0"} ) } diff --git a/Src/Newtonsoft.Json.Net35.sln b/Src/Newtonsoft.Json.Net35.sln new file mode 100644 index 00000000..38664c16 --- /dev/null +++ b/Src/Newtonsoft.Json.Net35.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json.Net35", "Newtonsoft.Json\Newtonsoft.Json.Net35.csproj", "{A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json.Tests.Net35", "Newtonsoft.Json.Tests\Newtonsoft.Json.Tests.Net35.csproj", "{3E6E2335-B079-4B5B-A65A-9D586914BCBB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Any CPU.Build.0 = Release|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs new file mode 100644 index 00000000..3ff59abf --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs @@ -0,0 +1,164 @@ +#if !(NET35 || NET20 || SILVERLIGHT) +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Linq; +using NUnit.Framework; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class DynamicTests : TestFixtureBase + { + [Test] + public void JObjectPropertyNames() + { + JObject o = new JObject( + new JProperty("ChildValue", "blah blah")); + + dynamic d = o; + + d.First = "A value!"; + + Assert.AreEqual(new JValue("A value!"), d.First); + Assert.AreEqual("A value!", (string)d.First); + + d.First = null; + Assert.AreEqual(JTokenType.Null, d.First.Type); + + Assert.IsTrue(d.Remove("First")); + Assert.IsNull(d.First); + + JValue v1 = d.ChildValue; + JValue v2 = d["ChildValue"]; + Assert.AreEqual(v1, v2); + + JValue newValue1 = new JValue("Blah blah"); + d.NewValue = newValue1; + JValue newValue2 = d.NewValue; + + Assert.IsTrue(ReferenceEquals(newValue1, newValue2)); + } + + [Test] + public void JObjectEnumerator() + { + JObject o = new JObject( + new JProperty("ChildValue", "blah blah")); + + dynamic d = o; + + foreach (JProperty value in d) + { + Assert.AreEqual("ChildValue", value.Name); + Assert.AreEqual("blah blah", (string)value.Value); + } + + foreach (dynamic value in d) + { + Assert.AreEqual("ChildValue", value.Name); + Assert.AreEqual("blah blah", (string)value.Value); + } + } + + [Test] + public void JObjectPropertyNameWithJArray() + { + JObject o = new JObject( + new JProperty("ChildValue", "blah blah")); + + dynamic d = o; + + d.First = new JArray(); + d.First.Add("Hi"); + + Assert.AreEqual(1, d.First.Count); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Could not determine JSON object type for type System.String[].")] + public void JObjectPropertyNameWithNonToken() + { + dynamic d = new JObject(); + + d.First = new [] {"One", "II", "3"}; + } + + [Test] + public void JObjectMethods() + { + JObject o = new JObject( + new JProperty("ChildValue", "blah blah")); + + dynamic d = o; + + d.Add("NewValue", 1); + + object count = d.Count; + + Assert.IsNull(count); + Assert.IsNull(d["Count"]); + + JToken v; + Assert.IsTrue(d.TryGetValue("ChildValue", out v)); + Assert.AreEqual("blah blah", (string)v); + } + + [Test] + public void JObjectGetDynamicPropertyNames() + { + JObject o = new JObject( + new JProperty("ChildValue", "blah blah"), + new JProperty("Hello Joe", null)); + + dynamic d = o; + + List memberNames = o.GetDynamicMemberNames().ToList(); + + Assert.AreEqual(2, memberNames.Count); + Assert.AreEqual("ChildValue", memberNames[0]); + Assert.AreEqual("Hello Joe", memberNames[1]); + } + + [Test] + public void JValueConvert() + { + AssertValueConverted(true); + AssertValueConverted(true); + AssertValueConverted(Encoding.UTF8.GetBytes("blah")); + AssertValueConverted(new DateTime(2000, 12, 20, 23, 59, 2, DateTimeKind.Utc)); + AssertValueConverted(new DateTime(2000, 12, 20, 23, 59, 2, DateTimeKind.Utc)); + AssertValueConverted(new DateTimeOffset(2000, 12, 20, 23, 59, 2, TimeSpan.FromHours(1))); + AssertValueConverted(new DateTimeOffset(2000, 12, 20, 23, 59, 2, TimeSpan.FromHours(1))); + AssertValueConverted(99.9m); + AssertValueConverted(99.9m); + AssertValueConverted(99.9); + AssertValueConverted(99.9); + AssertValueConverted(99.9f); + AssertValueConverted(99.9f); + AssertValueConverted(int.MinValue); + AssertValueConverted(int.MinValue); + AssertValueConverted(long.MaxValue); + AssertValueConverted(long.MaxValue); + AssertValueConverted(short.MaxValue); + AssertValueConverted(short.MaxValue); + AssertValueConverted("blah"); + AssertValueConverted(uint.MinValue); + AssertValueConverted(uint.MinValue); + AssertValueConverted(ulong.MaxValue); + AssertValueConverted(ulong.MaxValue); + AssertValueConverted(ushort.MinValue); + AssertValueConverted(ushort.MinValue); + } + + private static void AssertValueConverted(T value) + { + JValue v = new JValue(value); + dynamic d = v; + + Assert.AreEqual(value, (T)d); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml b/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml index f1d089fd..d43ba51e 100644 --- a/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml +++ b/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml @@ -1,11 +1,10 @@ - - + - + @@ -29,7 +28,7 @@
- +
diff --git a/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml.layout b/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml.layout index 8f631b12..c629c33c 100644 --- a/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml.layout +++ b/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml.layout @@ -20,7 +20,7 @@ - + @@ -40,7 +40,7 @@ - + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj new file mode 100644 index 00000000..55e858ce --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj @@ -0,0 +1,321 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {3E6E2335-B079-4B5B-A65A-9D586914BCBB} + Library + Properties + Newtonsoft.Json.Tests.Net35 + Newtonsoft.Json.Tests.Net35 + + + + + + + + + + + 3.5 + + + v3.5 + false + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + true + full + false + bin\Debug\DotNet35\ + TRACE;DEBUG;NET35 + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\DotNet35\ + TRACE;NET35 + prompt + 4 + AllRules.ruleset + + + + False + ..\Lib\NUnit\DotNet\nunit.framework.dll + + + + 3.5 + + + 3.5 + + + + 3.5 + + + 3.5 + + + + 3.0 + + + + 3.0 + + + 3.5 + + + + 3.5 + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + True + True + LinqToSqlClasses.dbml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MSLinqToSQLGenerator + LinqToSqlClasses.designer.cs + Designer + + + + + + + + LinqToSqlClasses.dbml + + + + + Always + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Newtonsoft.Json.Net35 + + + + + \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index dc9423ff..5f00323a 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -65,6 +65,7 @@ AllRules.ruleset + False ..\Lib\NUnit\DotNet\nunit.framework.dll @@ -134,11 +135,13 @@ + + diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index d31ab1d1..7667682a 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -1,4 +1,4 @@ -#if !SILVERLIGHT && !PocketPC && !NET20 +#if !SILVERLIGHT && !PocketPC && !NET20 && !NET35 using System; using System.Collections.Generic; using System.Globalization; diff --git a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs index db5ced4c..8cb6a69c 100644 --- a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs +++ b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs @@ -346,11 +346,11 @@ namespace Newtonsoft.Json.Tests.Schema return base.CreateContract(objectType); } - protected override IList CreateProperties(JsonObjectContract contract) + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { - IList properties = base.CreateProperties(contract); + IList properties = base.CreateProperties(type, memberSerialization); - JsonPropertyCollection c = new JsonPropertyCollection(contract); + JsonPropertyCollection c = new JsonPropertyCollection(type); CollectionUtils.AddRange(c, (IEnumerable)properties.Where(m => m.PropertyName != "Root")); return c; diff --git a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs index 24221cb3..563ef620 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs @@ -18,12 +18,12 @@ namespace Newtonsoft.Json.Tests.Serialization _startingWithChar = startingWithChar; } - protected override IList CreateProperties(JsonObjectContract contract) + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { - IList properties = base.CreateProperties(contract); + IList properties = base.CreateProperties(type, memberSerialization); // only serializer properties that start with the specified character - properties = + properties = properties.Where(p => p.PropertyName.StartsWith(_startingWithChar.ToString())).ToList(); return properties; diff --git a/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs new file mode 100644 index 00000000..7268ddd4 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs @@ -0,0 +1,183 @@ +#if !(NET35 || NET20 || SILVERLIGHT) +using System; +using System.Collections; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.Formatters; +using System.Text; +using Newtonsoft.Json.Utilities; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class DynamicTests : TestFixtureBase + { + public class DynamicChildObject + { + public string Text { get; set; } + public int Integer { get; set; } + } + + public class TestDynamicObject : DynamicObject + { + private readonly Dictionary _members; + + public int Int; + public DynamicChildObject ChildObject { get; set; } + + internal Dictionary Members + { + get { return _members; } + } + + public TestDynamicObject() + { + _members = new Dictionary(); + } + + public override IEnumerable GetDynamicMemberNames() + { + return _members.Keys.Union(new[] { "Int", "ChildObject" }); + } + + public override bool TryConvert(ConvertBinder binder, out object result) + { + Type targetType = binder.Type; + + if (targetType == typeof(IDictionary) || + targetType == typeof(IDictionary)) + { + result = new Dictionary(_members); + return true; + } + else + { + return base.TryConvert(binder, out result); + } + } + + public override bool TryDeleteMember(DeleteMemberBinder binder) + { + return _members.Remove(binder.Name); + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + return _members.TryGetValue(binder.Name, out result); + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + _members[binder.Name] = value; + return true; + } + } + + public class ErrorSettingDynamicObject : DynamicObject + { + public override bool TrySetMember(SetMemberBinder binder, object value) + { + return false; + } + } + + [Test] + public void SerializeDynamicObject() + { + TestDynamicObject dynamicObject = new TestDynamicObject(); + + dynamic d = dynamicObject; + d.Int = 1; + d.Decimal = 99.9d; + d.ChildObject = new DynamicChildObject(); + + Dictionary values = new Dictionary(); + + foreach (string memberName in dynamicObject.GetDynamicMemberNames()) + { + object value; + dynamicObject.TryGetMember(memberName, out value); + + values.Add(memberName, value); + } + + Assert.AreEqual(d.Int, values["Int"]); + Assert.AreEqual(d.Decimal, values["Decimal"]); + Assert.AreEqual(d.ChildObject, values["ChildObject"]); + + string json = JsonConvert.SerializeObject(dynamicObject, Formatting.Indented); + Assert.AreEqual(@"{ + ""Decimal"": 99.9, + ""Int"": 1, + ""ChildObject"": { + ""Text"": null, + ""Integer"": 0 + } +}", json); + + TestDynamicObject newDynamicObject = JsonConvert.DeserializeObject(json); + d = newDynamicObject; + + Assert.AreEqual(99.9, d.Decimal); + Assert.AreEqual(1, d.Int); + Assert.AreEqual(dynamicObject.ChildObject.Integer, d.ChildObject.Integer); + Assert.AreEqual(dynamicObject.ChildObject.Text, d.ChildObject.Text); + } + + [Test] + public void sdfsdf() + { + ErrorSettingDynamicObject d = JsonConvert.DeserializeObject("{'hi':5}"); + } + + [Test] + public void SerializeDynamicObjectWithObjectTracking() + { + dynamic o = new ExpandoObject(); + o.Text = "Text!"; + o.Integer = int.MaxValue; + o.DynamicChildObject = new DynamicChildObject + { + Integer = int.MinValue, + Text = "Child text!" + }; + + string json = JsonConvert.SerializeObject(o, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); + + Console.WriteLine(json); + + string dynamicChildObjectTypeName = ReflectionUtils.GetTypeName(typeof(DynamicChildObject), FormatterAssemblyStyle.Simple); + + Assert.AreEqual(@"{ + ""$type"": ""System.Dynamic.ExpandoObject, System.Core"", + ""Text"": ""Text!"", + ""Integer"": 2147483647, + ""DynamicChildObject"": { + ""$type"": """ + dynamicChildObjectTypeName + @""", + ""Text"": ""Child text!"", + ""Integer"": -2147483648 + } +}", json); + + dynamic n = JsonConvert.DeserializeObject(json, null, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); + + Assert.IsInstanceOfType(typeof(ExpandoObject), n); + Assert.AreEqual("Text!", n.Text); + Assert.AreEqual(int.MaxValue, n.Integer); + + Assert.IsInstanceOfType(typeof(DynamicChildObject), n.DynamicChildObject); + Assert.AreEqual("Child text!", n.DynamicChildObject.Text); + Assert.AreEqual(int.MinValue, n.DynamicChildObject.Integer); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/EntitiesSerializationTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/EntitiesSerializationTests.cs index 2150e6ee..a391ff02 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/EntitiesSerializationTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/EntitiesSerializationTests.cs @@ -23,7 +23,7 @@ // OTHER DEALINGS IN THE SOFTWARE. #endregion -#if !PocketPC && !SILVERLIGHT && !NET20 +#if !(NET35 || NET20 || SILVERLIGHT) using System; using System.Collections.Generic; using System.Data; diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 9990612b..32b4e070 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; #if !SILVERLIGHT && !PocketPC && !NET20 using System.ComponentModel.DataAnnotations; +using System.Runtime.CompilerServices; using System.Web.Script.Serialization; #endif using System.Text; @@ -52,6 +53,10 @@ using System.Reflection; using System.Xml.Linq; using System.Text.RegularExpressions; using System.Collections.Specialized; +using System.Linq.Expressions; +#endif +#if !(NET35 || NET20 || SILVERLIGHT) +using System.Dynamic; #endif namespace Newtonsoft.Json.Tests.Serialization @@ -3019,7 +3024,7 @@ keyword such as type of business."" PosDouble p = (PosDouble)value; if (p != null) - writer.WriteRawValue(String.Format("new PosD({0},{1})", p.X, p.Y)); + writer.WriteRawValue(String.Format(CultureInfo.InvariantCulture, "new PosD({0},{1})", p.X, p.Y)); else writer.WriteNull(); } @@ -3573,5 +3578,45 @@ keyword such as type of business."" ""event"": ""derived"" }", json); } + +#if !(NET35 || NET20 || SILVERLIGHT) + [Test] + public void SerializeExpandoObject() + { + dynamic expando = new ExpandoObject(); + expando.Int = 1; + expando.Decimal = 99.9d; + expando.Complex = new ExpandoObject(); + expando.Complex.String = "I am a string"; + expando.Complex.DateTime = new DateTime(2000, 12, 20, 18, 55, 0, DateTimeKind.Utc); + + string json = JsonConvert.SerializeObject(expando, Formatting.Indented); + Assert.AreEqual(@"{ + ""Int"": 1, + ""Decimal"": 99.9, + ""Complex"": { + ""String"": ""I am a string"", + ""DateTime"": ""\/Date(977338500000)\/"" + } +}", json); + + IDictionary newExpando = JsonConvert.DeserializeObject(json); + + Assert.IsInstanceOfType(typeof(long), newExpando["Int"]); + Assert.AreEqual(expando.Int, newExpando["Int"]); + + Assert.IsInstanceOfType(typeof(double), newExpando["Decimal"]); + Assert.AreEqual(expando.Decimal, newExpando["Decimal"]); + + Assert.IsInstanceOfType(typeof(JObject), newExpando["Complex"]); + JObject o = (JObject)newExpando["Complex"]; + + Assert.IsInstanceOfType(typeof(string), ((JValue)o["String"]).Value); + Assert.AreEqual(expando.Complex.String, (string)o["String"]); + + Assert.IsInstanceOfType(typeof(DateTime), ((JValue)o["DateTime"]).Value); + Assert.AreEqual(expando.Complex.DateTime, (DateTime)o["DateTime"]); + } +#endif } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs index 213bb048..e6af2f84 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs @@ -195,7 +195,7 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, DateTimeKind.Utc), c[2]); Assert.AreEqual(3, errors.Count); -#if !(NET20 || SILVERLIGHT) +#if !(NET20 || NET35 || SILVERLIGHT) Assert.AreEqual("The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.", errors[0]); #else Assert.AreEqual("The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.", errors[0]); diff --git a/Src/Newtonsoft.Json.Tests/SilverlightTests.cs b/Src/Newtonsoft.Json.Tests/SilverlightTests.cs index edce32ee..e1df7816 100644 --- a/Src/Newtonsoft.Json.Tests/SilverlightTests.cs +++ b/Src/Newtonsoft.Json.Tests/SilverlightTests.cs @@ -4,6 +4,7 @@ using NUnit.Framework; namespace Newtonsoft.Json.Tests { + // todo: need to fix this to get WP unit tests running off right dlls #if SILVERLIGHT [TestFixture] public class SilverlightTests diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index 079fee6a..957e1f6a 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -27,6 +27,10 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; +#if !(NET35 || NET20 || SILVERLIGHT) +using System.Dynamic; +using System.Linq.Expressions; +#endif using System.Linq; using System.IO; using Newtonsoft.Json.Utilities; @@ -650,5 +654,50 @@ namespace Newtonsoft.Json.Linq } #endregion #endif + +#if !(NET35 || NET20 || SILVERLIGHT) + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + protected override DynamicMetaObject GetMetaObject(Expression parameter) + { + return new DynamicProxyMetaObject(parameter, new JObjectDynamicProxy(this), true); + } + + private class JObjectDynamicProxy : DynamicProxy + { + public JObjectDynamicProxy(JObject value) : base(value) + { + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + // result can be null + result = Value[binder.Name]; + return true; + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + JToken v = value as JToken; + + // this can throw an error if value isn't a valid for a JValue + if (v == null) + v = new JValue(value); + + Value[binder.Name] = v; + return true; + } + + public override IEnumerable GetDynamicMemberNames() + { + return Value.Properties().Select(p => p.Name); + } + } +#endif } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index ea88bbc1..796d58b5 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -25,6 +25,10 @@ using System; using System.Collections.Generic; +#if !(NET35 || NET20 || SILVERLIGHT) +using System.Dynamic; +using System.Linq.Expressions; +#endif using System.Linq; using System.IO; using Newtonsoft.Json.Utilities; @@ -39,7 +43,10 @@ namespace Newtonsoft.Json.Linq /// Represents an abstract JSON token. /// public abstract class JToken : IJEnumerable, IJsonLineInfo - { +#if !(NET35 || NET20 || SILVERLIGHT) + , IDynamicMetaObjectProvider +#endif + { private JContainer _parent; internal JToken _next; private static JTokenEqualityComparer _equalityComparer; @@ -561,6 +568,35 @@ namespace Newtonsoft.Json.Linq return Convert.ToInt32(v.Value, CultureInfo.InvariantCulture); } + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator short(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, false)) + throw new ArgumentException("Can not convert {0} to Int16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return Convert.ToInt16(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator ushort(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, false)) + throw new ArgumentException("Can not convert {0} to UInt16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return Convert.ToUInt16(v.Value, CultureInfo.InvariantCulture); + } + /// /// Performs an explicit conversion from to . /// @@ -578,6 +614,41 @@ namespace Newtonsoft.Json.Linq return (v.Value != null) ? (int?)Convert.ToInt32(v.Value, CultureInfo.InvariantCulture) : null; } + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator short?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, true)) + throw new ArgumentException("Can not convert {0} to Int16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (v.Value != null) ? (short?)Convert.ToInt16(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator ushort?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, true)) + throw new ArgumentException("Can not convert {0} to UInt16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (v.Value != null) ? (ushort?)Convert.ToInt16(v.Value, CultureInfo.InvariantCulture) : null; + } + /// /// Performs an explicit conversion from to . /// @@ -851,6 +922,17 @@ namespace Newtonsoft.Json.Linq return new JValue(value); } + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(short value) + { + return new JValue(value); + } + /// /// Performs an implicit conversion from to . /// @@ -922,6 +1004,17 @@ namespace Newtonsoft.Json.Linq return new JValue(value); } + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(short? value) + { + return new JValue(value); + } + /// /// Performs an implicit conversion from to . /// @@ -1179,5 +1272,31 @@ namespace Newtonsoft.Json.Linq JPath p = new JPath(path); return p.Evaluate(this, errorWhenNoMatch); } + +#if !(NET35 || NET20 || SILVERLIGHT) + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + protected virtual DynamicMetaObject GetMetaObject(Expression parameter) + { + return new DynamicProxyMetaObject(parameter, new DynamicProxy(this), true); + } + + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) + { + return GetMetaObject(parameter); + } +#endif } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj new file mode 100644 index 00000000..5edb4bc1 --- /dev/null +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -0,0 +1,265 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Library + Properties + Newtonsoft.Json.Net35 + Newtonsoft.Json.Net35 + false + + + + + + + + + + + + + 3.5 + + + false + v3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + + + true + full + false + bin\Debug\DotNet35\ + TRACE;DEBUG;CODE_ANALYSIS;NET35 + prompt + 4 + bin\Debug\DotNet35\Newtonsoft.Json.Net35.xml + true + + + Newtonsoft.Json.ruleset + + + pdbonly + true + bin\Release\DotNet35\ + TRACE;NET35 + prompt + 4 + bin\Release\DotNet35\Newtonsoft.Json.Net35.xml + AllRules.ruleset + + + + + 3.5 + + + + 3.0 + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index e11fde6c..b0dfbe60 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -165,6 +165,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 5d576f97..153a3e87 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -102,9 +102,11 @@ + + @@ -197,6 +199,8 @@ + + diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 2c3fba42..df6333de 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -46,6 +46,8 @@ using System.Security; [assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.Compact")] #elif NET20 [assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.Net20")] +#elif NET35 +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.Net35")] #else [assembly: InternalsVisibleTo("Newtonsoft.Json.Tests")] #endif diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 6f5adf7b..676acbd2 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -27,6 +27,9 @@ using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; +#if !(NET35 || NET20 || SILVERLIGHT) +using System.Dynamic; +#endif using System.Globalization; using System.Linq; using System.Reflection; @@ -270,7 +273,7 @@ namespace Newtonsoft.Json.Serialization InitializeContract(contract); contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(objectType); - contract.Properties.AddRange(CreateProperties(contract)); + contract.Properties.AddRange(CreateProperties(contract.UnderlyingType, contract.MemberSerialization)); if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic) contract.ParametrizedConstructor = GetParametrizedConstructor(objectType); @@ -443,6 +446,23 @@ namespace Newtonsoft.Json.Serialization } #endif +#if !(NET35 || NET20 || SILVERLIGHT) + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonDynamicContract CreateDynamicContract(Type objectType) + { + JsonDynamicContract contract = new JsonDynamicContract(objectType); + InitializeContract(contract); + + contract.Properties.AddRange(CreateProperties(objectType, MemberSerialization.OptOut)); + + return contract; + } +#endif + /// /// Creates a for the given type. /// @@ -489,6 +509,11 @@ namespace Newtonsoft.Json.Serialization return CreateISerializableContract(objectType); #endif +#if !(NET35 || NET20 || SILVERLIGHT) + if (typeof(IDynamicMetaObjectProvider).IsAssignableFrom(objectType)) + return CreateDynamicContract(objectType); +#endif + return CreateObjectContract(objectType); } @@ -563,21 +588,22 @@ namespace Newtonsoft.Json.Serialization } /// - /// Creates properties for the given . + /// Creates properties for the given . /// - /// The contract to create properties for. - /// Properties for the given . - protected virtual IList CreateProperties(JsonObjectContract contract) + /// The type to create properties for. + /// /// The member serialization mode for the type. + /// Properties for the given . + protected virtual IList CreateProperties(Type type, MemberSerialization memberSerialization) { - List members = GetSerializableMembers(contract.UnderlyingType); + List members = GetSerializableMembers(type); if (members == null) throw new JsonSerializationException("Null collection of seralizable members returned."); - JsonPropertyCollection properties = new JsonPropertyCollection(contract); + JsonPropertyCollection properties = new JsonPropertyCollection(type); foreach (MemberInfo member in members) { - JsonProperty property = CreateProperty(contract, member); + JsonProperty property = CreateProperty(member, memberSerialization); if (property != null) properties.AddProperty(property); @@ -604,10 +630,10 @@ namespace Newtonsoft.Json.Serialization /// /// Creates a for the given . /// - /// The member's declaring types . + /// The member's parent . /// The member to create a for. /// A created for the given . - protected virtual JsonProperty CreateProperty(JsonObjectContract contract, MemberInfo member) + protected virtual JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = new JsonProperty(); property.PropertyType = ReflectionUtils.GetMemberUnderlyingType(member); @@ -652,7 +678,7 @@ namespace Newtonsoft.Json.Serialization property.Required = Required.Default; property.Ignored = (hasIgnoreAttribute || - (contract.MemberSerialization == MemberSerialization.OptIn + (memberSerialization == MemberSerialization.OptIn && propertyAttribute == null #if !PocketPC && !NET20 && dataMemberAttribute == null diff --git a/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs b/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs new file mode 100644 index 00000000..ade8c8a8 --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.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 + +#if !(NET35 || NET20 || SILVERLIGHT) +using System; +using System.Collections.Generic; +using System.Reflection; +using Newtonsoft.Json.Utilities; +using System.Collections; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonDynamicContract : JsonContract + { + /// + /// Gets the object's properties. + /// + /// The object's properties. + public JsonPropertyCollection Properties { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonDynamicContract(Type underlyingType) + : base(underlyingType) + { + Properties = new JsonPropertyCollection(UnderlyingType); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs index bb078963..e52539f9 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs @@ -58,7 +58,7 @@ namespace Newtonsoft.Json.Serialization public JsonObjectContract(Type underlyingType) : base(underlyingType) { - Properties = new JsonPropertyCollection(this); + Properties = new JsonPropertyCollection(UnderlyingType); } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs b/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs index 8bf17c95..e65fe139 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs @@ -37,16 +37,16 @@ namespace Newtonsoft.Json.Serialization /// public class JsonPropertyCollection : KeyedCollection { - private readonly JsonObjectContract _contract; + private readonly Type _type; /// /// Initializes a new instance of the class. /// - /// The contract. - public JsonPropertyCollection(JsonObjectContract contract) + /// The type. + public JsonPropertyCollection(Type type) { - ValidationUtils.ArgumentNotNull(contract, "contract"); - _contract = contract; + ValidationUtils.ArgumentNotNull(type, "type"); + _type = type; } /// @@ -75,7 +75,7 @@ namespace Newtonsoft.Json.Serialization if (!existingProperty.Ignored) throw new JsonSerializationException( - "A member with the name '{0}' already exists on '{1}'. Use the JsonPropertyAttribute to specify another name.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName, _contract.UnderlyingType)); + "A member with the name '{0}' already exists on '{1}'. Use the JsonPropertyAttribute to specify another name.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName, _type)); // remove ignored property so it can be replaced in collection Remove(existingProperty); diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index c140241a..4ca5afbc 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -27,6 +27,9 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +#if !(NET35 || NET20 || SILVERLIGHT) +using System.Dynamic; +#endif using System.Globalization; using System.Linq; using System.Reflection; @@ -402,6 +405,14 @@ namespace Newtonsoft.Json.Serialization return CreateISerializable(reader, serializableContract, id); } #endif + +#if !(NET35 || NET20 || SILVERLIGHT) + JsonDynamicContract dynamicContract = contract as JsonDynamicContract; + if (dynamicContract != null) + { + return CreateDynamic(reader, dynamicContract, id); + } +#endif throw new JsonSerializationException("Cannot deserialize JSON object into type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); } @@ -717,6 +728,7 @@ namespace Newtonsoft.Json.Serialization if (id != null) Serializer.ReferenceResolver.AddReference(id, createdObject); + // these are together because OnDeserializing takes an object but for an ISerializable the object is full created in the constructor contract.InvokeOnDeserializing(createdObject, Serializer.Context); contract.InvokeOnDeserialized(createdObject, Serializer.Context); @@ -724,6 +736,64 @@ namespace Newtonsoft.Json.Serialization } #endif +#if !(NET35 || NET20 || SILVERLIGHT) + private object CreateDynamic(JsonReader reader, JsonDynamicContract contract, string id) + { + IDynamicMetaObjectProvider newObject = null; + + if (contract.UnderlyingType.IsInterface || contract.UnderlyingType.IsAbstract) + throw new JsonSerializationException("Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (contract.DefaultCreator != null && + (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) + { + newObject = (IDynamicMetaObjectProvider)contract.DefaultCreator(); + } + + if (id != null) + Serializer.ReferenceResolver.AddReference(id, newObject); + + contract.InvokeOnDeserializing(newObject, Serializer.Context); + + bool exit = false; + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string memberName = reader.Value.ToString(); + if (!reader.Read()) + throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + + // first attempt to find a settable property, otherwise fall back to a dynamic set without type + JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName); + if (property != null && property.Writable && !property.Ignored) + { + SetPropertyValue(property, reader, newObject); + } + else + { + object value = (JsonReader.IsPrimitiveToken(reader.TokenType)) + ? reader.Value + : CreateObject(reader, typeof(IDynamicMetaObjectProvider), GetContractSafe(typeof(IDynamicMetaObjectProvider), null), null, null); + + newObject.TrySetMember(memberName, value); + } + break; + case JsonToken.EndObject: + exit = true; + break; + default: + throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); + } + } while (!exit && reader.Read()); + + contract.InvokeOnDeserialized(newObject, Serializer.Context); + + return newObject; + } +#endif + private object CreateAndPopulateObject(JsonReader reader, JsonObjectContract contract, string id) { object newObject = null; diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index b2f63202..fa25fb05 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -27,6 +27,9 @@ using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; +#if !(NET35 || NET20 || SILVERLIGHT) +using System.Dynamic; +#endif using System.Globalization; using System.Linq; using System.Reflection; @@ -131,6 +134,12 @@ namespace Newtonsoft.Json.Serialization { SerializeISerializable(writer, (ISerializable) value, (JsonISerializableContract) valueContract); } +#endif +#if !(NET35 || NET20 || SILVERLIGHT) + else if (valueContract is JsonDynamicContract) + { + SerializeDynamic(writer, (IDynamicMetaObjectProvider) value, (JsonDynamicContract) valueContract); + } #endif } @@ -463,18 +472,30 @@ namespace Newtonsoft.Json.Serialization } #endif - //private bool ShouldWriteTypeProperty(JsonProperty member, JsonContract contract, TypeNameHandling typeFlag) - //{ - // if (HasFlag(((member != null) ? member.TypeNameHandling : null) ?? Serializer.TypeNameHandling, typeFlag)) - // return true; +#if !(NET35 || NET20 || SILVERLIGHT) + private void SerializeDynamic(JsonWriter writer, IDynamicMetaObjectProvider value, JsonDynamicContract contract) + { + contract.InvokeOnSerializing(value, Serializer.Context); + SerializeStack.Add(value); - // if ((((member != null) ? member.TypeNameHandling : null) ?? Serializer.TypeNameHandling) == TypeNameHandling.Auto) + writer.WriteStartObject(); - // || (member != null - // && (member.TypeNameHandling ?? Serializer.TypeNameHandling) == TypeNameHandling.Auto - // && contract.UnderlyingType != member.PropertyType) - // ) - //} + foreach (string memberName in value.GetDynamicMemberNames()) + { + object memberValue; + if (DynamicUtils.TryGetMember(value, memberName, out memberValue)) + { + writer.WritePropertyName(memberName); + SerializeValue(writer, memberValue, GetContractSafe(memberValue), null, null); + } + } + + writer.WriteEndObject(); + + SerializeStack.RemoveAt(SerializeStack.Count - 1); + contract.InvokeOnSerialized(value, Serializer.Context); + } +#endif private bool ShouldWriteType(TypeNameHandling typeNameHandlingFlag, JsonContract contract, JsonProperty member, JsonContract collectionValueContract) { diff --git a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs index 6c9b8f21..2cc4f734 100644 --- a/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -235,7 +235,7 @@ namespace Newtonsoft.Json.Utilities if (targetType == typeof (Uri)) return new Uri((string) initialValue); if (targetType == typeof (TimeSpan)) -#if !NET20 && !SILVERLIGHT +#if !(NET35 || NET20 || SILVERLIGHT) return TimeSpan.Parse((string) initialValue, CultureInfo.InvariantCulture); #else return TimeSpan.Parse((string)initialValue); diff --git a/Src/Newtonsoft.Json/Utilities/DynamicProxy.cs b/Src/Newtonsoft.Json/Utilities/DynamicProxy.cs new file mode 100644 index 00000000..cadbe938 --- /dev/null +++ b/Src/Newtonsoft.Json/Utilities/DynamicProxy.cs @@ -0,0 +1,94 @@ +#if !(NET35 || NET20 || SILVERLIGHT) +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace Newtonsoft.Json.Utilities +{ + internal class DynamicProxy + { + public T Value { get; private set; } + + public DynamicProxy(T value) + { + Value = value; + } + + public virtual IEnumerable GetDynamicMemberNames() + { + return new string[0]; + } + + public virtual bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result) + { + result = null; + return false; + } + + public virtual bool TryConvert(ConvertBinder binder, out object result) + { + result = null; + return false; + } + + public virtual bool TryCreateInstance(CreateInstanceBinder binder, object[] args, out object result) + { + result = null; + return false; + } + + public virtual bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes) + { + return false; + } + + public virtual bool TryDeleteMember(DeleteMemberBinder binder) + { + return false; + } + + public virtual bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) + { + result = null; + return false; + } + + public virtual bool TryGetMember(GetMemberBinder binder, out object result) + { + result = null; + return false; + } + + public virtual bool TryInvoke(InvokeBinder binder, object[] args, out object result) + { + result = null; + return false; + } + + public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) + { + result = null; + return false; + } + + public virtual bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) + { + return false; + } + + public virtual bool TrySetMember(SetMemberBinder binder, object value) + { + return false; + } + + public virtual bool TryUnaryOperation(UnaryOperationBinder binder, out object result) + { + result = null; + return false; + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs b/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs new file mode 100644 index 00000000..58ba434f --- /dev/null +++ b/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs @@ -0,0 +1,398 @@ +#if !(NET35 || NET20 || SILVERLIGHT) +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Newtonsoft.Json.Utilities +{ + internal sealed class DynamicProxyMetaObject : DynamicMetaObject + { + private readonly DynamicProxy _proxy; + private readonly bool _dontFallbackFirst; + + internal DynamicProxyMetaObject(Expression expression, DynamicProxy proxy, bool dontFallbackFirst) + : base(expression, BindingRestrictions.Empty, proxy.Value) + { + _proxy = proxy; + _dontFallbackFirst = dontFallbackFirst; + } + + private new T Value { get { return (T)base.Value; } } + + private bool IsOverridden(string method) + { + return _proxy.GetType().GetMember(method, MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance).Cast() + .Any(info => + // check that the method overrides the original on DynamicObjectProxy + info.DeclaringType != typeof(DynamicProxy) && + info.GetBaseDefinition().DeclaringType == typeof(DynamicProxy)); + } + + public override DynamicMetaObject BindGetMember(GetMemberBinder binder) + { + return IsOverridden("TryGetMember") + ? CallMethodWithResult("TryGetMember", binder, NoArgs, e => binder.FallbackGetMember(this, e)) + : base.BindGetMember(binder); + } + + public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) + { + return IsOverridden("TrySetMember") + ? CallMethodReturnLast("TrySetMember", binder, GetArgs(value), e => binder.FallbackSetMember(this, value, e)) + : base.BindSetMember(binder, value); + } + + public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) + { + return IsOverridden("TryDeleteMember") + ? CallMethodNoResult("TryDeleteMember", binder, NoArgs, e => binder.FallbackDeleteMember(this, e)) + : base.BindDeleteMember(binder); + } + + + public override DynamicMetaObject BindConvert(ConvertBinder binder) + { + return IsOverridden("TryConvert") + ? CallMethodWithResult("TryConvert", binder, NoArgs, e => binder.FallbackConvert(this, e)) + : base.BindConvert(binder); + } + + public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) + { + if (!IsOverridden("TryInvokeMember")) + return base.BindInvokeMember(binder, args); + + // + // Generate a tree like: + // + // { + // object result; + // TryInvokeMember(payload, out result) + // ? result + // : TryGetMember(payload, out result) + // ? FallbackInvoke(result) + // : fallbackResult + // } + // + // Then it calls FallbackInvokeMember with this tree as the + // "error", giving the language the option of using this + // tree or doing .NET binding. + // + Fallback fallback = e => binder.FallbackInvokeMember(this, args, e); + + DynamicMetaObject call = BuildCallMethodWithResult( + "TryInvokeMember", + binder, + GetArgArray(args), + BuildCallMethodWithResult( + "TryGetMember", + new GetBinderAdapter(binder), + NoArgs, + fallback(null), + e => binder.FallbackInvoke(e, args, null) + ), + null + ); + + return _dontFallbackFirst ? call : fallback(call); + } + + + public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args) + { + return IsOverridden("TryCreateInstance") + ? CallMethodWithResult("TryCreateInstance", binder, GetArgArray(args), e => binder.FallbackCreateInstance(this, args, e)) + : base.BindCreateInstance(binder, args); + } + + public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) + { + return IsOverridden("TryInvoke") + ? CallMethodWithResult("TryInvoke", binder, GetArgArray(args), e => binder.FallbackInvoke(this, args, e)) + : base.BindInvoke(binder, args); + } + + public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) + { + return IsOverridden("TryBinaryOperation") + ? CallMethodWithResult("TryBinaryOperation", binder, GetArgs(arg), e => binder.FallbackBinaryOperation(this, arg, e)) + : base.BindBinaryOperation(binder, arg); + } + + public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder) + { + return IsOverridden("TryUnaryOperation") + ? CallMethodWithResult("TryUnaryOperation", binder, NoArgs, e => binder.FallbackUnaryOperation(this, e)) + : base.BindUnaryOperation(binder); + } + + public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) + { + return IsOverridden("TryGetIndex") + ? CallMethodWithResult("TryGetIndex", binder, GetArgArray(indexes), e => binder.FallbackGetIndex(this, indexes, e)) + : base.BindGetIndex(binder, indexes); + } + + public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) + { + return IsOverridden("TrySetIndex") + ? CallMethodReturnLast("TrySetIndex", binder, GetArgArray(indexes, value), e => binder.FallbackSetIndex(this, indexes, value, e)) + : base.BindSetIndex(binder, indexes, value); + } + + public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes) + { + return IsOverridden("TryDeleteIndex") + ? CallMethodNoResult("TryDeleteIndex", binder, GetArgArray(indexes), e => binder.FallbackDeleteIndex(this, indexes, e)) + : base.BindDeleteIndex(binder, indexes); + } + + private delegate DynamicMetaObject Fallback(DynamicMetaObject errorSuggestion); + + private readonly static Expression[] NoArgs = new Expression[0]; + + private static Expression[] GetArgs(params DynamicMetaObject[] args) + { + return args.Select(arg => Expression.Convert(arg.Expression, typeof(object))).ToArray(); + } + + private static Expression[] GetArgArray(DynamicMetaObject[] args) + { + return new[] { Expression.NewArrayInit(typeof(object), GetArgs(args)) }; + } + + private static Expression[] GetArgArray(DynamicMetaObject[] args, DynamicMetaObject value) + { + return new Expression[] + { + Expression.NewArrayInit(typeof(object), GetArgs(args)), + Expression.Convert(value.Expression, typeof(object)) + }; + } + + private static ConstantExpression Constant(DynamicMetaObjectBinder binder) + { + Type t = binder.GetType(); + while (!t.IsVisible) + t = t.BaseType; + return Expression.Constant(binder, t); + } + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic that returns a result + /// + private DynamicMetaObject CallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback, Fallback fallbackInvoke = null) + { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + DynamicMetaObject fallbackResult = fallback(null); + + DynamicMetaObject callDynamic = BuildCallMethodWithResult(methodName, binder, args, fallbackResult, fallbackInvoke); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + + return _dontFallbackFirst ? callDynamic : fallback(callDynamic); + } + + private DynamicMetaObject BuildCallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, DynamicMetaObject fallbackResult, Fallback fallbackInvoke) + { + // + // Build a new expression like: + // { + // object result; + // TryGetMember(payload, out result) ? fallbackInvoke(result) : fallbackResult + // } + // + ParameterExpression result = Expression.Parameter(typeof(object), null); + + + Expression[] callArgs = new Expression[args.Length + 2]; + Array.Copy(args, 0, callArgs, 1, args.Length); + callArgs[0] = Constant(binder); + callArgs[callArgs.Length - 1] = result; + + DynamicMetaObject resultMO = new DynamicMetaObject(result, BindingRestrictions.Empty); + + // Need to add a conversion if calling TryConvert + if (binder.ReturnType != typeof (object)) + { + UnaryExpression convert = Expression.Convert(resultMO.Expression, binder.ReturnType); + // will always be a cast or unbox + + resultMO = new DynamicMetaObject(convert, resultMO.Restrictions); + } + + if (fallbackInvoke != null) + resultMO = fallbackInvoke(resultMO); + + DynamicMetaObject callDynamic = new DynamicMetaObject( + Expression.Block( + new[] {result}, + Expression.Condition( + Expression.Call( + Expression.Constant(_proxy), + typeof(DynamicProxy).GetMethod(methodName), + callArgs + ), + resultMO.Expression, + fallbackResult.Expression, + binder.ReturnType + ) + ), + GetRestrictions().Merge(resultMO.Restrictions).Merge(fallbackResult.Restrictions) + ); + + return callDynamic; + } + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic, but uses one of the arguments for + /// the result. + /// + private DynamicMetaObject CallMethodReturnLast(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) + { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + DynamicMetaObject fallbackResult = fallback(null); + + // + // Build a new expression like: + // { + // object result; + // TrySetMember(payload, result = value) ? result : fallbackResult + // } + // + ParameterExpression result = Expression.Parameter(typeof(object), null); + Expression[] callArgs = AddFirst(args, Constant(binder)); + callArgs[args.Length] = Expression.Assign(result, callArgs[args.Length]); + + DynamicMetaObject callDynamic = new DynamicMetaObject( + Expression.Block( + new[] { result }, + Expression.Condition( + Expression.Call( + Expression.Constant(_proxy), + typeof(DynamicProxy).GetMethod(methodName), + callArgs + ), + result, + fallbackResult.Expression, + typeof(object) + ) + ), + GetRestrictions().Merge(fallbackResult.Restrictions) + ); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + return _dontFallbackFirst ? callDynamic : fallback(callDynamic); + } + + internal static TItem[] AddFirst(IList list, TItem item) + { + TItem[] res = new TItem[list.Count + 1]; + res[0] = item; + list.CopyTo(res, 1); + return res; + } + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic, but uses one of the arguments for + /// the result. + /// + private DynamicMetaObject CallMethodNoResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) + { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + DynamicMetaObject fallbackResult = fallback(null); + + // + // Build a new expression like: + // if (TryDeleteMember(payload)) { } else { fallbackResult } + // + DynamicMetaObject callDynamic = new DynamicMetaObject( + Expression.Condition( + Expression.Call( + Expression.Constant(_proxy), + typeof(DynamicProxy).GetMethod(methodName), + AddFirst(args, Constant(binder)) + ), + Expression.Empty(), + fallbackResult.Expression, + typeof (void) + ), + GetRestrictions().Merge(fallbackResult.Restrictions) + ); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + return _dontFallbackFirst ? callDynamic : fallback(callDynamic); + } + + /// + /// Returns a Restrictions object which includes our current restrictions merged + /// with a restriction limiting our type + /// + private BindingRestrictions GetRestrictions() + { + // ReSharper disable CompareNonConstrainedGenericWithNull + return Value == null && HasValue // ReSharper restore CompareNonConstrainedGenericWithNull + ? BindingRestrictions.GetInstanceRestriction(Expression, null) + : BindingRestrictions.GetTypeRestriction(Expression, LimitType); + } + + public override IEnumerable GetDynamicMemberNames() + { + return _proxy.GetDynamicMemberNames(); + } + + // It is okay to throw NotSupported from this binder. This object + // is only used by DynamicObject.GetMember--it is not expected to + // (and cannot) implement binding semantics. It is just so the DO + // can use the Name and IgnoreCase properties. + private sealed class GetBinderAdapter : GetMemberBinder + { + internal GetBinderAdapter(InvokeMemberBinder binder) : + base(binder.Name, binder.IgnoreCase) + { + } + + public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) + { + throw new NotSupportedException(); + } + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs b/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs new file mode 100644 index 00000000..75b9ac3c --- /dev/null +++ b/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs @@ -0,0 +1,189 @@ +#if !(NET35 || NET20 || SILVERLIGHT) +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class DynamicUtils + { + internal static class BinderWrapper + { + private const string BinderTypeName = "Microsoft.CSharp.RuntimeBinder.Binder, Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; + private const string CSharpArgumentInfoTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo, Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; + private const string CSharpArgumentInfoFlagsTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; + private const string CSharpBinderFlagsTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; + + private static object _getCSharpArgumentInfoArray; + private static object _setCSharpArgumentInfoArray; + private static MethodCall _getMemberCall; + private static MethodCall _setMemberCall; + private static bool _init; + + private static void Init() + { + if (!_init) + { + Type binderType = Type.GetType(BinderTypeName, false); + if (binderType == null) + throw new Exception("Could not resolve type '{0}'. You may need to add a reference to Microsoft.CSharp.dll to work with dynamic types.".FormatWith(CultureInfo.InvariantCulture, BinderTypeName)); + + // None + _getCSharpArgumentInfoArray = CreateSharpArgumentInfoArray(0); + // None, Constant | UseCompileTimeType + _setCSharpArgumentInfoArray = CreateSharpArgumentInfoArray(0, 3); + CreateMemberCalls(); + + _init = true; + } + } + + private static object CreateSharpArgumentInfoArray(params int[] values) + { + Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName); + Type csharpArgumentInfoFlags = Type.GetType(CSharpArgumentInfoFlagsTypeName); + + Array a = Array.CreateInstance(csharpArgumentInfoType, values.Length); + + for (int i = 0; i < values.Length; i++) + { + MethodInfo createArgumentInfoMethod = csharpArgumentInfoType.GetMethod("Create", BindingFlags.Public | BindingFlags.Static, null, new[] { csharpArgumentInfoFlags, typeof(string) }, null); + object arg = createArgumentInfoMethod.Invoke(null, new object[] { 0, null }); + a.SetValue(arg, i); + } + + return a; + } + + private static void CreateMemberCalls() + { + Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName); + Type csharpBinderFlagsType = Type.GetType(CSharpBinderFlagsTypeName); + Type binderType = Type.GetType(BinderTypeName); + + Type csharpArgumentInfoTypeEnumerableType = typeof(IEnumerable<>).MakeGenericType(csharpArgumentInfoType); + + MethodInfo getMemberMethod = binderType.GetMethod("GetMember", BindingFlags.Public | BindingFlags.Static, null, new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType }, null); + _getMemberCall = DynamicReflectionDelegateFactory.Instance.CreateMethodCall(getMemberMethod); + + MethodInfo setMemberMethod = binderType.GetMethod("SetMember", BindingFlags.Public | BindingFlags.Static, null, new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType }, null); + _setMemberCall = DynamicReflectionDelegateFactory.Instance.CreateMethodCall(setMemberMethod); + } + + public static CallSiteBinder GetMember(string name, Type context) + { + Init(); + return (CallSiteBinder)_getMemberCall(null, 0, name, context, _getCSharpArgumentInfoArray); + } + + public static CallSiteBinder SetMember(string name, Type context) + { + Init(); + return (CallSiteBinder)_setMemberCall(null, 0, name, context, _setCSharpArgumentInfoArray); + } + } + + public static bool TryGetMember(this IDynamicMetaObjectProvider dynamicProvider, string name, out object value) + { + GetMemberBinder getMemberBinder = (GetMemberBinder) BinderWrapper.GetMember(name, typeof (DynamicUtils)); + + CallSite> callSite = CallSite>.Create(new NoThrowGetBinderMember(getMemberBinder)); + + object result = callSite.Target(callSite, dynamicProvider); + + if (!ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult)) + { + value = result; + return true; + } + else + { + value = null; + return false; + } + } + + public static bool TrySetMember(this IDynamicMetaObjectProvider dynamicProvider, string name, object value) + { + SetMemberBinder binder = (SetMemberBinder) BinderWrapper.SetMember(name, value.GetType()); + + var setterSite = CallSite>.Create(new NoThrowSetBinderMember(binder)); + + object result = setterSite.Target(setterSite, dynamicProvider, value); + + return !ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult); + } + + public static IEnumerable GetDynamicMemberNames(this IDynamicMetaObjectProvider dynamicProvider) + { + DynamicMetaObject metaObject = dynamicProvider.GetMetaObject(Expression.Constant(dynamicProvider)); + return metaObject.GetDynamicMemberNames(); + } + + internal class NoThrowGetBinderMember : GetMemberBinder + { + private readonly GetMemberBinder _innerBinder; + + public NoThrowGetBinderMember(GetMemberBinder innerBinder) + : base(innerBinder.Name, innerBinder.IgnoreCase) + { + _innerBinder = innerBinder; + } + + public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) + { + DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { }); + + NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor(); + Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression); + + DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions); + return finalMetaObject; + } + } + + internal class NoThrowSetBinderMember : SetMemberBinder + { + private readonly SetMemberBinder _innerBinder; + + public NoThrowSetBinderMember(SetMemberBinder innerBinder) + : base(innerBinder.Name, innerBinder.IgnoreCase) + { + _innerBinder = innerBinder; + } + + public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion) + { + DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { value }); + + NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor(); + Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression); + + DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions); + return finalMetaObject; + } + } + + + internal class NoThrowExpressionVisitor : ExpressionVisitor + { + internal static readonly object ErrorResult = new object(); + + protected override Expression VisitConditional(ConditionalExpression node) + { + // if the result of a test is to throw an error, rewrite to result an error result value + if (node.IfFalse.NodeType == ExpressionType.Throw) + return Expression.Condition(node.Test, node.IfTrue, Expression.Constant(ErrorResult)); + + return base.VisitConditional(node); + } + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/DynamicWrapper.cs b/Src/Newtonsoft.Json/Utilities/DynamicWrapper.cs index 6c80c652..8e888190 100644 --- a/Src/Newtonsoft.Json/Utilities/DynamicWrapper.cs +++ b/Src/Newtonsoft.Json/Utilities/DynamicWrapper.cs @@ -53,7 +53,14 @@ namespace Newtonsoft.Json.Utilities private static byte[] GetStrongKey() { - using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Newtonsoft.Json.Dynamic.snk")) + string name; +#if NET35 + name = "Newtonsoft.Json.Net35.Dynamic.snk"; +#else + name = "Newtonsoft.Json.Dynamic.snk"; +#endif + + using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name)) { if (stream == null) throw new MissingManifestResourceException("Should have a Newtonsoft.Json.Dynamic.snk as an embedded resource."); diff --git a/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs b/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs index e112ab44..11ae6904 100644 --- a/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs +++ b/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs @@ -35,6 +35,8 @@ namespace Newtonsoft.Json.Utilities public override MethodCall CreateMethodCall(MethodBase method) { + ValidationUtils.ArgumentNotNull(method, "method"); + ConstructorInfo c = method as ConstructorInfo; if (c != null) return (o, a) => c.Invoke(a); @@ -44,6 +46,8 @@ namespace Newtonsoft.Json.Utilities public override Func CreateDefaultConstructor(Type type) { + ValidationUtils.ArgumentNotNull(type, "type"); + if (type.IsValueType) return () => (T)ReflectionUtils.CreateInstance(type); @@ -54,21 +58,29 @@ namespace Newtonsoft.Json.Utilities public override Func CreateGet(PropertyInfo propertyInfo) { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + return o => propertyInfo.GetValue(o, null); } public override Func CreateGet(FieldInfo fieldInfo) { + ValidationUtils.ArgumentNotNull(fieldInfo, "fieldInfo"); + return o => fieldInfo.GetValue(o); } public override Action CreateSet(FieldInfo fieldInfo) { + ValidationUtils.ArgumentNotNull(fieldInfo, "fieldInfo"); + return (o, v) => fieldInfo.SetValue(o, v); } public override Action CreateSet(PropertyInfo propertyInfo) { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + return (o, v) => propertyInfo.SetValue(o, v, null); } }