diff --git a/Src/Newtonsoft.Json.Tests/Converters/ExpandoObjectConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/ExpandoObjectConverterTests.cs new file mode 100644 index 00000000..f07de570 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Converters/ExpandoObjectConverterTests.cs @@ -0,0 +1,158 @@ +#if !(NET35 || NET20 || WINDOWS_PHONE) + +using System; +using System.Collections.Generic; +#if !SILVERLIGHT && !PocketPC && !NET20 +using System.Data.Linq; +#endif +#if !SILVERLIGHT +using System.Data.SqlTypes; +#endif +using System.Dynamic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class ExpandoObjectConverterTests : TestFixtureBase + { + public class ExpandoContainer + { + public string Before { get; set; } + public ExpandoObject Expando { get; set; } + public string After { get; set; } + } + + [Test] + public void SerializeExpandoObject() + { + ExpandoContainer d = new ExpandoContainer + { + Before = "Before!", + Expando = new ExpandoObject(), + After = "After!" + }; + + dynamic o = d.Expando; + + o.String = "String!"; + o.Integer = 234; + o.Float = 1.23d; + o.List = new List {"First", "Second", "Third"}; + o.Object = new Dictionary + { + {"First", 1} + }; + + string json = JsonConvert.SerializeObject(d, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Before"": ""Before!"", + ""Expando"": { + ""String"": ""String!"", + ""Integer"": 234, + ""Float"": 1.23, + ""List"": [ + ""First"", + ""Second"", + ""Third"" + ], + ""Object"": { + ""First"": 1 + } + }, + ""After"": ""After!"" +}", json); + } + + [Test] + public void SerializeNullExpandoObject() + { + ExpandoContainer d = new ExpandoContainer(); + + string json = JsonConvert.SerializeObject(d, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Before"": null, + ""Expando"": null, + ""After"": null +}", json); + } + + [Test] + public void DeserializeExpandoObject() + { + string json = @"{ + ""Before"": ""Before!"", + ""Expando"": { + ""String"": ""String!"", + ""Integer"": 234, + ""Float"": 1.23, + ""List"": [ + ""First"", + ""Second"", + ""Third"" + ], + ""Object"": { + ""First"": 1 + } + }, + ""After"": ""After!"" +}"; + + ExpandoContainer o = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(o.Before, "Before!"); + Assert.AreEqual(o.After, "After!"); + Assert.IsNotNull(o.Expando); + + dynamic d = o.Expando; + Assert.IsInstanceOfType(typeof(ExpandoObject), d); + + Assert.AreEqual("String!", d.String); + Assert.IsInstanceOfType(typeof(string), d.String); + + Assert.AreEqual(234, d.Integer); + Assert.IsInstanceOfType(typeof(long), d.Integer); + + Assert.AreEqual(1.23, d.Float); + Assert.IsInstanceOfType(typeof(double), d.Float); + + Assert.IsNotNull(d.List); + Assert.AreEqual(3, d.List.Count); + Assert.IsInstanceOfType(typeof(List), d.List); + + Assert.AreEqual("First", d.List[0]); + Assert.IsInstanceOfType(typeof(string), d.List[0]); + + Assert.AreEqual("Second", d.List[1]); + Assert.AreEqual("Third", d.List[2]); + + Assert.IsNotNull(d.Object); + Assert.IsInstanceOfType(typeof(ExpandoObject), d.Object); + + Assert.AreEqual(1, d.Object.First); + Assert.IsInstanceOfType(typeof(long), d.Object.First); + } + + [Test] + public void DeserializeNullExpandoObject() + { + string json = @"{ + ""Before"": null, + ""Expando"": null, + ""After"": null +}"; + + ExpandoContainer c = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(null, c.Expando); + } + + } +} + +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj index fd3f41fb..39811c4b 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj @@ -107,6 +107,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index 325ae313..fe13d1ac 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -106,6 +106,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj index c0d05f99..05a89057 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj @@ -70,6 +70,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index fc75f495..151824d7 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -108,6 +108,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 3042e735..0a8d7bb0 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.Configuration; using System.Runtime.CompilerServices; using System.Threading; using System.Web.Script.Serialization; @@ -3448,45 +3449,45 @@ keyword such as type of business."" Assert.AreEqual(27, deserialized.Age); } -// [Test] -// public void XmlSerializerSpecifiedTrueTest() -// { -// XmlSerializer s = new XmlSerializer(typeof(OptionalOrder)); + // [Test] + // public void XmlSerializerSpecifiedTrueTest() + // { + // XmlSerializer s = new XmlSerializer(typeof(OptionalOrder)); -// StringWriter sw = new StringWriter(); -// s.Serialize(sw, new OptionalOrder() { FirstOrder = "First", FirstOrderSpecified = true }); + // StringWriter sw = new StringWriter(); + // s.Serialize(sw, new OptionalOrder() { FirstOrder = "First", FirstOrderSpecified = true }); -// Console.WriteLine(sw.ToString()); + // Console.WriteLine(sw.ToString()); -// string xml = @" -// -// First -//"; + // string xml = @" + // + // First + //"; -// OptionalOrder o = (OptionalOrder)s.Deserialize(new StringReader(xml)); -// Console.WriteLine(o.FirstOrder); -// Console.WriteLine(o.FirstOrderSpecified); -// } + // OptionalOrder o = (OptionalOrder)s.Deserialize(new StringReader(xml)); + // Console.WriteLine(o.FirstOrder); + // Console.WriteLine(o.FirstOrderSpecified); + // } -// [Test] -// public void XmlSerializerSpecifiedFalseTest() -// { -// XmlSerializer s = new XmlSerializer(typeof(OptionalOrder)); + // [Test] + // public void XmlSerializerSpecifiedFalseTest() + // { + // XmlSerializer s = new XmlSerializer(typeof(OptionalOrder)); -// StringWriter sw = new StringWriter(); -// s.Serialize(sw, new OptionalOrder() { FirstOrder = "First", FirstOrderSpecified = false }); + // StringWriter sw = new StringWriter(); + // s.Serialize(sw, new OptionalOrder() { FirstOrder = "First", FirstOrderSpecified = false }); -// Console.WriteLine(sw.ToString()); + // Console.WriteLine(sw.ToString()); -// // string xml = @" -// // -// // First -// //"; + // // string xml = @" + // // + // // First + // //"; -// // OptionalOrder o = (OptionalOrder)s.Deserialize(new StringReader(xml)); -// // Console.WriteLine(o.FirstOrder); -// // Console.WriteLine(o.FirstOrderSpecified); -// } + // // OptionalOrder o = (OptionalOrder)s.Deserialize(new StringReader(xml)); + // // Console.WriteLine(o.FirstOrder); + // // Console.WriteLine(o.FirstOrderSpecified); + // } public class OptionalOrder { @@ -3501,47 +3502,47 @@ keyword such as type of business."" public class FamilyDetails { - public string Name { get; set; } - public int NumberOfChildren { get; set; } + public string Name { get; set; } + public int NumberOfChildren { get; set; } - [JsonIgnore] - public bool NumberOfChildrenSpecified { get; set; } + [JsonIgnore] + public bool NumberOfChildrenSpecified { get; set; } } [Test] public void SpecifiedExample() { - FamilyDetails joe = new FamilyDetails(); - joe.Name = "Joe Family Details"; - joe.NumberOfChildren = 4; - joe.NumberOfChildrenSpecified = true; + FamilyDetails joe = new FamilyDetails(); + joe.Name = "Joe Family Details"; + joe.NumberOfChildren = 4; + joe.NumberOfChildrenSpecified = true; - FamilyDetails martha = new FamilyDetails(); - martha.Name = "Martha Family Details"; - martha.NumberOfChildren = 3; - martha.NumberOfChildrenSpecified = false; + FamilyDetails martha = new FamilyDetails(); + martha.Name = "Martha Family Details"; + martha.NumberOfChildren = 3; + martha.NumberOfChildrenSpecified = false; - string json = JsonConvert.SerializeObject(new[] { joe, martha }, Formatting.Indented); - //[ - // { - // "Name": "Joe Family Details", - // "NumberOfChildren": 4 - // }, - // { - // "Name": "Martha Family Details" - // } - //] - Console.WriteLine(json); + string json = JsonConvert.SerializeObject(new[] { joe, martha }, Formatting.Indented); + //[ + // { + // "Name": "Joe Family Details", + // "NumberOfChildren": 4 + // }, + // { + // "Name": "Martha Family Details" + // } + //] + Console.WriteLine(json); - string mikeString = "{\"Name\": \"Mike Person\"}"; - FamilyDetails mike = JsonConvert.DeserializeObject(mikeString); + string mikeString = "{\"Name\": \"Mike Person\"}"; + FamilyDetails mike = JsonConvert.DeserializeObject(mikeString); - Console.WriteLine("mikeString specifies number of children: {0}", mike.NumberOfChildrenSpecified); + Console.WriteLine("mikeString specifies number of children: {0}", mike.NumberOfChildrenSpecified); - string mikeFullDisclosureString = "{\"Name\": \"Mike Person\", \"NumberOfChildren\": \"0\"}"; - mike = JsonConvert.DeserializeObject(mikeFullDisclosureString); + string mikeFullDisclosureString = "{\"Name\": \"Mike Person\", \"NumberOfChildren\": \"0\"}"; + mike = JsonConvert.DeserializeObject(mikeFullDisclosureString); - Console.WriteLine("mikeString specifies number of children: {0}", mike.NumberOfChildrenSpecified); + Console.WriteLine("mikeString specifies number of children: {0}", mike.NumberOfChildrenSpecified); } public class DictionaryKey @@ -3783,14 +3784,14 @@ keyword such as type of business."" 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(ExpandoObject), newExpando["Complex"]); + IDictionary o = (ExpandoObject)newExpando["Complex"]; - Assert.IsInstanceOfType(typeof(string), ((JValue)o["String"]).Value); - Assert.AreEqual(expando.Complex.String, (string)o["String"]); + Assert.IsInstanceOfType(typeof(string), o["String"]); + Assert.AreEqual(expando.Complex.String, o["String"]); - Assert.IsInstanceOfType(typeof(DateTime), ((JValue)o["DateTime"]).Value); - Assert.AreEqual(expando.Complex.DateTime, (DateTime)o["DateTime"]); + Assert.IsInstanceOfType(typeof(DateTime), o["DateTime"]); + Assert.AreEqual(expando.Complex.DateTime, o["DateTime"]); } #endif @@ -4074,7 +4075,7 @@ keyword such as type of business."" { public string Value { get; private set; } public string Constructor { get; private set; } - + public PublicConstructorOverridenByJsonConstructor() { Constructor = "NonPublic"; @@ -4107,7 +4108,7 @@ keyword such as type of business."" Value = value; Constructor = "Public Paramatized 1"; } - + [JsonConstructor] public MultipleParamatrizedConstructorsJsonConstructor(string value, int age) { @@ -4218,5 +4219,34 @@ keyword such as type of business."" Assert.IsNull(dateTime); } + + [Test] + public void MultiIndexSuperTest() + { + MultiIndexSuper e = new MultiIndexSuper(); + + string json = JsonConvert.SerializeObject(e, Formatting.Indented); + + Assert.AreEqual(@"{}", json); + } + + public class MultiIndexSuper : MultiIndexBase + { + + } + + public abstract class MultiIndexBase + { + protected internal object this[string propertyName] + { + get { return null; } + set { } + } + protected internal object this[object property] + { + get { return null; } + set { } + } + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Bson/BsonReader.cs b/Src/Newtonsoft.Json/Bson/BsonReader.cs index fa18d617..83ad3cbc 100644 --- a/Src/Newtonsoft.Json/Bson/BsonReader.cs +++ b/Src/Newtonsoft.Json/Bson/BsonReader.cs @@ -242,6 +242,9 @@ namespace Newtonsoft.Json.Bson } } + /// + /// Changes the to Closed. + /// public override void Close() { base.Close(); diff --git a/Src/Newtonsoft.Json/Bson/BsonWriter.cs b/Src/Newtonsoft.Json/Bson/BsonWriter.cs index 4260c90c..7975070b 100644 --- a/Src/Newtonsoft.Json/Bson/BsonWriter.cs +++ b/Src/Newtonsoft.Json/Bson/BsonWriter.cs @@ -156,6 +156,9 @@ namespace Newtonsoft.Json.Bson _propertyName = name; } + /// + /// Closes this stream and the underlying stream. + /// public override void Close() { base.Close(); diff --git a/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs b/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs new file mode 100644 index 00000000..5d88542f --- /dev/null +++ b/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs @@ -0,0 +1,140 @@ +#if !(NET35 || NET20 || WINDOWS_PHONE) + +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts an ExpandoObject to and from JSON. + /// + public class ExpandoObjectConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + // can write is set to false + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return ReadValue(reader); + } + + private object ReadValue(JsonReader reader) + { + while (reader.TokenType == JsonToken.Comment) + { + if (!reader.Read()) + throw new Exception("Unexpected end."); + } + + switch (reader.TokenType) + { + case JsonToken.StartObject: + return ReadObject(reader); + case JsonToken.StartArray: + return ReadList(reader); + default: + if (JsonReader.IsPrimitiveToken(reader.TokenType)) + return reader.Value; + + throw new Exception("Unexpected token when converting ExpandoObject: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + } + + private object ReadList(JsonReader reader) + { + IList list = new List(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.Comment: + break; + default: + object v = ReadValue(reader); + + list.Add(v); + break; + case JsonToken.EndArray: + return list; + } + } + + throw new Exception("Unexpected end."); + } + + private object ReadObject(JsonReader reader) + { + IDictionary expandoObject = new ExpandoObject(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string propertyName = reader.Value.ToString(); + + if (!reader.Read()) + throw new Exception("Unexpected end."); + + object v = ReadValue(reader); + + expandoObject[propertyName] = v; + break; + case JsonToken.Comment: + break; + case JsonToken.EndObject: + return expandoObject; + } + } + + throw new Exception("Unexpected end."); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return (objectType == typeof (ExpandoObject)); + } + + /// + /// Gets a value indicating whether this can write JSON. + /// + /// + /// true if this can write JSON; otherwise, false. + /// + public override bool CanWrite + { + get { return false; } + } + } +} + +#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 index 3c7fe56b..7e590cc3 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -97,6 +97,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index 4a8cd1f4..54f5f071 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -95,6 +95,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj index 4e194ce6..0ef846e1 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj @@ -68,6 +68,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 29590626..3f6d0269 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -96,6 +96,7 @@ + diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index dc23e38a..c93ffa38 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -83,6 +83,9 @@ namespace Newtonsoft.Json.Serialization { #if !PocketPC && !SILVERLIGHT && !NET20 new EntityKeyMemberConverter(), +#endif +#if !(NET35 || NET20 || WINDOWS_PHONE) + new ExpandoObjectConverter(), #endif new BinaryConverter(), new KeyValuePairConverter(), diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index d5f557a9..69a1571a 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -896,7 +896,9 @@ namespace Newtonsoft.Json.Utilities PropertyInfo member = propertyInfos[i]; if (member.DeclaringType != targetType) { - PropertyInfo declaredMember = member.DeclaringType.GetProperty(member.Name, bindingAttr); + Type[] types = member.GetIndexParameters().Select(p => p.ParameterType).ToArray(); + + PropertyInfo declaredMember = member.DeclaringType.GetProperty(member.Name, bindingAttr, null, member.PropertyType, types, null); propertyInfos[i] = declaredMember; } }