-Fixed error when reflecting over type that has multiple index properties inherited

-Added ExpandoObjectConverter that deserializes to primitive values rather than LINQ to JSON objects for ExpandObjects
This commit is contained in:
JamesNK
2011-03-25 22:08:41 +00:00
parent 6a49425267
commit 39056de238
15 changed files with 415 additions and 68 deletions
@@ -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<string> {"First", "Second", "Third"};
o.Object = new Dictionary<string, object>
{
{"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<ExpandoContainer>(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<object>), 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<ExpandoContainer>(json);
Assert.AreEqual(null, c.Expando);
}
}
}
#endif
@@ -107,6 +107,7 @@
<Compile Include="Bson\BsonWriterTests.cs" />
<Compile Include="Bson\BsonReaderTests.cs" />
<Compile Include="Converters\BinaryConverterTests.cs" />
<Compile Include="Converters\ExpandoObjectConverterTests.cs" />
<Compile Include="Converters\RegexConverterTests.cs" />
<Compile Include="Converters\DataTableConverterTests.cs" />
<Compile Include="Converters\DataSetConverterTests.cs" />
@@ -106,6 +106,7 @@
<Compile Include="Converters\CustomCreationConverterTests.cs" />
<Compile Include="Converters\DataSetConverterTests.cs" />
<Compile Include="Converters\DataTableConverterTests.cs" />
<Compile Include="Converters\ExpandoObjectConverterTests.cs" />
<Compile Include="Converters\ObjectIdConverterTests.cs" />
<Compile Include="Converters\RegexConverterTests.cs" />
<Compile Include="Converters\StringEnumConverterTests.cs" />
@@ -70,6 +70,7 @@
<Compile Include="Converters\CustomCreationConverterTests.cs" />
<Compile Include="Converters\DataSetConverterTests.cs" />
<Compile Include="Converters\DataTableConverterTests.cs" />
<Compile Include="Converters\ExpandoObjectConverterTests.cs" />
<Compile Include="Converters\ObjectIdConverterTests.cs" />
<Compile Include="Converters\RegexConverterTests.cs" />
<Compile Include="Converters\StringEnumConverterTests.cs" />
@@ -108,6 +108,7 @@
<Compile Include="Bson\BsonWriterTests.cs" />
<Compile Include="Bson\BsonReaderTests.cs" />
<Compile Include="Converters\BinaryConverterTests.cs" />
<Compile Include="Converters\ExpandoObjectConverterTests.cs" />
<Compile Include="Converters\RegexConverterTests.cs" />
<Compile Include="Converters\DataTableConverterTests.cs" />
<Compile Include="Converters\DataSetConverterTests.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 = @"<?xml version=""1.0"" encoding=""utf-16""?>
//<OptionalOrder xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
// <FirstOrder>First</FirstOrder>
//</OptionalOrder>";
// string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
//<OptionalOrder xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
// <FirstOrder>First</FirstOrder>
//</OptionalOrder>";
// 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 = @"<?xml version=""1.0"" encoding=""utf-16""?>
// //<OptionalOrder xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
// // <FirstOrder>First</FirstOrder>
// //</OptionalOrder>";
// // string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
// //<OptionalOrder xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
// // <FirstOrder>First</FirstOrder>
// //</OptionalOrder>";
// // 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<FamilyDetails>(mikeString);
string mikeString = "{\"Name\": \"Mike Person\"}";
FamilyDetails mike = JsonConvert.DeserializeObject<FamilyDetails>(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<FamilyDetails>(mikeFullDisclosureString);
string mikeFullDisclosureString = "{\"Name\": \"Mike Person\", \"NumberOfChildren\": \"0\"}";
mike = JsonConvert.DeserializeObject<FamilyDetails>(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<string, object> 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 { }
}
}
}
}
+3
View File
@@ -242,6 +242,9 @@ namespace Newtonsoft.Json.Bson
}
}
/// <summary>
/// Changes the <see cref="JsonReader.State"/> to Closed.
/// </summary>
public override void Close()
{
base.Close();
+3
View File
@@ -156,6 +156,9 @@ namespace Newtonsoft.Json.Bson
_propertyName = name;
}
/// <summary>
/// Closes this stream and the underlying stream.
/// </summary>
public override void Close()
{
base.Close();
@@ -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
{
/// <summary>
/// Converts an ExpandoObject to and from JSON.
/// </summary>
public class ExpandoObjectConverter : JsonConverter
{
/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// can write is set to false
}
/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
/// <param name="objectType">Type of the object.</param>
/// <param name="existingValue">The existing value of object being read.</param>
/// <param name="serializer">The calling serializer.</param>
/// <returns>The object value.</returns>
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<object> list = new List<object>();
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<string, object> 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.");
}
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return (objectType == typeof (ExpandoObject));
}
/// <summary>
/// Gets a value indicating whether this <see cref="JsonConverter"/> can write JSON.
/// </summary>
/// <value>
/// <c>true</c> if this <see cref="JsonConverter"/> can write JSON; otherwise, <c>false</c>.
/// </value>
public override bool CanWrite
{
get { return false; }
}
}
}
#endif
@@ -97,6 +97,7 @@
<Compile Include="Converters\CustomCreationConverter.cs" />
<Compile Include="Converters\DateTimeConverterBase.cs" />
<Compile Include="Converters\EntityKeyMemberConverter.cs" />
<Compile Include="Converters\ExpandoObjectConverter.cs" />
<Compile Include="Converters\KeyValuePairConverter.cs" />
<Compile Include="Converters\BsonObjectIdConverter.cs" />
<Compile Include="Converters\RegexConverter.cs" />
@@ -95,6 +95,7 @@
<Compile Include="Converters\DataTableConverter.cs" />
<Compile Include="Converters\DateTimeConverterBase.cs" />
<Compile Include="Converters\EntityKeyMemberConverter.cs" />
<Compile Include="Converters\ExpandoObjectConverter.cs" />
<Compile Include="Converters\IsoDateTimeConverter.cs" />
<Compile Include="Converters\JavaScriptDateTimeConverter.cs" />
<Compile Include="Converters\JsonDateTimeSerializationMode.cs" />
@@ -68,6 +68,7 @@
<Compile Include="Converters\DataTableConverter.cs" />
<Compile Include="Converters\DateTimeConverterBase.cs" />
<Compile Include="Converters\EntityKeyMemberConverter.cs" />
<Compile Include="Converters\ExpandoObjectConverter.cs" />
<Compile Include="Converters\IsoDateTimeConverter.cs" />
<Compile Include="Converters\JavaScriptDateTimeConverter.cs" />
<Compile Include="Converters\JsonDateTimeSerializationMode.cs" />
@@ -96,6 +96,7 @@
<Compile Include="Converters\CustomCreationConverter.cs" />
<Compile Include="Converters\DateTimeConverterBase.cs" />
<Compile Include="Converters\EntityKeyMemberConverter.cs" />
<Compile Include="Converters\ExpandoObjectConverter.cs" />
<Compile Include="Converters\KeyValuePairConverter.cs" />
<Compile Include="Converters\BsonObjectIdConverter.cs" />
<Compile Include="Converters\RegexConverter.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(),
@@ -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;
}
}