From 3536c768730d7dbfe642874404ab43b2ce6b2256 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sun, 6 Oct 2013 12:32:56 +1300 Subject: [PATCH] -Added support for serializing extension data -Added support for reference loop handling and type name handling to extension data -Bug fixes --- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/JsonSerializerTest.cs | 238 +++++++++++++++++- .../JsonExtensionDataAttribute.cs | 8 + .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/DefaultContractResolver.cs | 174 +++++++++---- .../Serialization/JsonContract.cs | 9 +- .../Serialization/JsonObjectContract.cs | 5 + .../JsonSerializerInternalReader.cs | 10 +- .../JsonSerializerInternalWriter.cs | 51 +++- .../ExpressionReflectionDelegateFactory.cs | 4 + 10 files changed, 434 insertions(+), 69 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index f8db56aa..97774fe0 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.6.16604")] +[assembly: AssemblyFileVersion("5.0.6.16606")] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 3b886dfb..3cfc3a8e 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -534,6 +534,27 @@ namespace Newtonsoft.Json.Tests.Serialization public IDictionary ExtensionData; } + public class PublicNoReadExtensionDataAttributeTestClass + { + public string Name { get; set; } + [JsonExtensionData(ReadData = false)] + public IDictionary ExtensionData; + } + + public class PublicNoWriteExtensionDataAttributeTestClass + { + public string Name { get; set; } + [JsonExtensionData(WriteData = false)] + public IDictionary ExtensionData; + } + + public class PublicJTokenExtensionDataAttributeTestClass + { + public string Name { get; set; } + [JsonExtensionData] + public IDictionary ExtensionData; + } + [Test] public void DeserializeDirectoryAccount() { @@ -558,9 +579,175 @@ namespace Newtonsoft.Json.Tests.Serialization } }); + Assert.AreEqual(@"{""Name"":""Name!"",""Test"":1}", json); + } + + [Test] + public void SerializePublicExtensionDataNull() + { + string json = JsonConvert.SerializeObject(new PublicExtensionDataAttributeTestClass + { + Name = "Name!" + }); + Assert.AreEqual(@"{""Name"":""Name!""}", json); } + [Test] + public void SerializePublicNoWriteExtensionData() + { + string json = JsonConvert.SerializeObject(new PublicNoWriteExtensionDataAttributeTestClass + { + Name = "Name!", + ExtensionData = new Dictionary + { + { "Test", 1 } + } + }); + + Assert.AreEqual(@"{""Name"":""Name!""}", json); + } + + [Test] + public void DeserializeNoReadPublicExtensionData() + { + PublicNoReadExtensionDataAttributeTestClass c = JsonConvert.DeserializeObject(@"{""Name"":""Name!"",""Test"":1}"); + + Assert.AreEqual(null, c.ExtensionData); + } + + [Test] + public void SerializePublicExtensionDataCircularReference() + { + var c = new PublicExtensionDataAttributeTestClass + { + Name = "Name!", + ExtensionData = new Dictionary + { + { "Test", 1 } + } + }; + c.ExtensionData["Self"] = c; + + string json = JsonConvert.SerializeObject(c, new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All, + Formatting = Formatting.Indented + }); + + Assert.AreEqual(@"{ + ""$id"": ""1"", + ""Name"": ""Name!"", + ""Test"": 1, + ""Self"": { + ""$ref"": ""1"" + } +}", json); + + var c2 = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All + }); + + Assert.AreEqual("Name!", c2.Name); + + PublicExtensionDataAttributeTestClass bizzaroC2 = (PublicExtensionDataAttributeTestClass)c2.ExtensionData["Self"]; + + Assert.AreEqual(c2, bizzaroC2); + Assert.AreEqual(1, (long)bizzaroC2.ExtensionData["Test"]); + } + + [Test] + public void DeserializePublicJTokenExtensionDataCircularReference() + { + string json = @"{ + ""$id"": ""1"", + ""Name"": ""Name!"", + ""Test"": 1, + ""Self"": { + ""$ref"": ""1"" + } +}"; + + var c2 = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All + }); + + Assert.AreEqual("Name!", c2.Name); + + JObject bizzaroC2 = (JObject)c2.ExtensionData["Self"]; + + Assert.AreEqual("Name!", (string)bizzaroC2["Name"]); + Assert.AreEqual(1, (int)bizzaroC2["Test"]); + + Assert.AreEqual(1, (int)c2.ExtensionData["Test"]); + } + + [Test] + public void DeserializePublicExtensionDataTypeNamdHandling() + { + string json = @"{ + ""$id"": ""1"", + ""Name"": ""Name!"", + ""Test"": 1, + ""Self"": { + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.WagePerson, Newtonsoft.Json.Tests"", + ""HourlyWage"": 2.0, + ""Name"": null, + ""BirthDate"": ""0001-01-01T00:00:00"", + ""LastModified"": ""0001-01-01T00:00:00"" + } +}"; + + PublicExtensionDataAttributeTestClass c2 = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects + }); + + Assert.AreEqual("Name!", c2.Name); + + WagePerson bizzaroC2 = (WagePerson)c2.ExtensionData["Self"]; + + Assert.AreEqual(2m, bizzaroC2.HourlyWage); + } + + [Test] + public void SerializePublicExtensionDataTypeNamdHandling() + { + PublicExtensionDataAttributeTestClass c = new PublicExtensionDataAttributeTestClass + { + Name = "Name!", + ExtensionData = new Dictionary + { + { + "Test", new WagePerson + { + HourlyWage = 2.1m + } + } + } + }; + + string json = JsonConvert.SerializeObject(c, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects, + Formatting = Formatting.Indented + }); + + Assert.AreEqual(@"{ + ""$type"": ""Newtonsoft.Json.Tests.Serialization.JsonSerializerTest+PublicExtensionDataAttributeTestClass, Newtonsoft.Json.Tests"", + ""Name"": ""Name!"", + ""Test"": { + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.WagePerson, Newtonsoft.Json.Tests"", + ""HourlyWage"": 2.1, + ""Name"": null, + ""BirthDate"": ""0001-01-01T00:00:00"", + ""LastModified"": ""0001-01-01T00:00:00"" + } +}", json); + } + [Test] public void DeserializePublicExtensionData() { @@ -575,7 +762,7 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.AreEqual("Name!", c.Name); Assert.AreEqual(2, c.ExtensionData.Count); - Assert.AreEqual("NoMatch!", (string)(JValue)c.ExtensionData["NoMatch"]); + Assert.AreEqual("NoMatch!", (string)c.ExtensionData["NoMatch"]); // the ExtensionData property is put into the extension data // inception @@ -603,19 +790,19 @@ namespace Newtonsoft.Json.Tests.Serialization var c = JsonConvert.DeserializeObject("{'first':1,'second':2}"); Assert.AreEqual(2, c.ExtensionData.Count); - Assert.AreEqual(1, (int)(JToken)c.ExtensionData["first"]); - Assert.AreEqual(2, (int)(JToken)c.ExtensionData["second"]); + Assert.AreEqual(1, (long)c.ExtensionData["first"]); + Assert.AreEqual(2, (long)c.ExtensionData["second"]); } [Test] public void MultipleExtensionDataAttributesTest() { - var c = JsonConvert.DeserializeObject("{'first':1,'second':2}"); + var c = JsonConvert.DeserializeObject("{'first':[1],'second':[2]}"); Assert.AreEqual(null, c.ExtensionData1); Assert.AreEqual(2, c.ExtensionData2.Count); - Assert.AreEqual(1, (int)c.ExtensionData2["first"]); - Assert.AreEqual(2, (int)c.ExtensionData2["second"]); + Assert.AreEqual(1, (int)((JArray)c.ExtensionData2["first"])[0]); + Assert.AreEqual(2, (int)((JArray)c.ExtensionData2["second"])[0]); } [Test] @@ -9166,6 +9353,45 @@ Parameter name: value", ]", json); } + public class Foo2 + { + public string Name { get; set; } + } + + [Test] + [Ignore] + public void SerializeMultipleObjectsFromStream() + { + StringReader sr = new StringReader(@"{ ""name"": ""bar"" }{ ""name"": ""baz"" }"); + + var reader1 = new JsonTextReader(sr); + var serializer1 = new JsonSerializer { }; + var foo1 = serializer1.Deserialize(reader1); + Assert.AreEqual("bar", foo1.Name); + + var reader2 = new JsonTextReader(sr); + var serializer2 = new JsonSerializer { }; + var foo2 = serializer2.Deserialize(reader2); + Assert.AreEqual("baz", foo2.Name); + } + +#if !(PORTABLE || PORTABLE40) + [Test] + public void SerializeDictionaryWithStructKey() + { + string json = JsonConvert.SerializeObject( + new Dictionary { { new Size(1, 2), new Size(3, 4) } } + ); + + Assert.AreEqual(@"{""1, 2"":""3, 4""}", json); + + Dictionary d = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(new Size(1, 2), d.Keys.First()); + Assert.AreEqual(new Size(3, 4), d.Values.First()); + } +#endif + [Test] public void DeserializeCustomReferenceResolver() { diff --git a/Src/Newtonsoft.Json/JsonExtensionDataAttribute.cs b/Src/Newtonsoft.Json/JsonExtensionDataAttribute.cs index c916418e..48272f93 100644 --- a/Src/Newtonsoft.Json/JsonExtensionDataAttribute.cs +++ b/Src/Newtonsoft.Json/JsonExtensionDataAttribute.cs @@ -8,5 +8,13 @@ namespace Newtonsoft.Json [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] public class JsonExtensionDataAttribute : Attribute { + public bool WriteData { get; set; } + public bool ReadData { get; set; } + + public JsonExtensionDataAttribute() + { + WriteData = true; + ReadData = true; + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index e449c1c7..640de9c9 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -92,6 +92,6 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.6.16604")] +[assembly: AssemblyFileVersion("5.0.6.16606")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 6f440145..0ac1470a 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -391,32 +391,35 @@ namespace Newtonsoft.Json.Serialization } } - contract.ExtensionDataSetter = GetExtensionDataForType(contract.NonNullableUnderlyingType); + MemberInfo extensionDataMember = GetExtensionDataMemberForType(contract.NonNullableUnderlyingType); + if (extensionDataMember != null) + SetExtensionDataDelegates(contract, extensionDataMember); return contract; } - private ExtensionDataSetter GetExtensionDataForType(Type type) + private MemberInfo GetExtensionDataMemberForType(Type type) { - ExtensionDataSetter extensionDataSetter = null; - - foreach (Type baseType in GetClassHierarchyForType(type)) - { - IList members = new List(); - members.AddRange(baseType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); - members.AddRange(baseType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); - - foreach (MemberInfo member in members) + IEnumerable members = GetClassHierarchyForType(type).SelectMany(baseType => { - MemberTypes memberType = member.MemberType(); + IList m = new List(); + m.AddRange(baseType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + m.AddRange(baseType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return m; + }); + + MemberInfo extensionDataMember = members.LastOrDefault(m => + { + MemberTypes memberType = m.MemberType(); if (memberType != MemberTypes.Property && memberType != MemberTypes.Field) - continue; + return false; // last instance of attribute wins on type if there are multiple - if (!member.IsDefined(typeof(JsonExtensionDataAttribute), false)) - continue; + if (!m.IsDefined(typeof(JsonExtensionDataAttribute), false)) + return false; - Type t = ReflectionUtils.GetMemberUnderlyingType(member); + Type t = ReflectionUtils.GetMemberUnderlyingType(m); Type dictionaryType; if (ReflectionUtils.ImplementsGenericDefinition(t, typeof(IDictionary<,>), out dictionaryType)) @@ -424,39 +427,122 @@ namespace Newtonsoft.Json.Serialization Type keyType = dictionaryType.GetGenericArguments()[0]; Type valueType = dictionaryType.GetGenericArguments()[1]; - // change type to a class if it is the base interface so it can be instantiated if needed - if (ReflectionUtils.IsGenericDefinition(t, typeof(IDictionary<,>))) - t = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); - if (keyType.IsAssignableFrom(typeof(string)) && valueType.IsAssignableFrom(typeof(JToken))) - { - MethodInfo addMethod = t.GetMethod("Add", new [] { keyType, valueType }); - Func getExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateGet(member); - Action setExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateSet(member); - Func createExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor(t); - MethodCall setExtensionDataDictionaryValue = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(addMethod); - - extensionDataSetter = (o, key, value) => - { - object dictionary = getExtensionDataDictionary(o); - if (dictionary == null) - { - dictionary = createExtensionDataDictionary(); - setExtensionDataDictionary(o, dictionary); - } - - setExtensionDataDictionaryValue(dictionary, key, value); - }; - - continue; - } + return true; } - throw new JsonException("Invalid extension data attribute on '{0}'. Member '{1}' type must implement IDictionary.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(member.DeclaringType), member.Name)); - } + throw new JsonException("Invalid extension data attribute on '{0}'. Member '{1}' type must implement IDictionary.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(m.DeclaringType), m.Name)); + }); + + return extensionDataMember; + } + + private static void SetExtensionDataDelegates(JsonObjectContract contract, MemberInfo member) + { + JsonExtensionDataAttribute extensionDataAttribute = ReflectionUtils.GetAttribute(member); + if (extensionDataAttribute == null) + return; + + Type t = ReflectionUtils.GetMemberUnderlyingType(member); + + Type dictionaryType; + ReflectionUtils.ImplementsGenericDefinition(t, typeof(IDictionary<,>), out dictionaryType); + + Type keyType = dictionaryType.GetGenericArguments()[0]; + Type valueType = dictionaryType.GetGenericArguments()[1]; + bool isJTokenValueType = typeof(JToken).IsAssignableFrom(valueType); + + // change type to a class if it is the base interface so it can be instantiated if needed + if (ReflectionUtils.IsGenericDefinition(t, typeof(IDictionary<,>))) + t = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); + + MethodInfo addMethod = t.GetMethod("Add", new[] { keyType, valueType }); + Func getExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateGet(member); + Action setExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateSet(member); + Func createExtensionDataDictionary = JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor(t); + MethodCall setExtensionDataDictionaryValue = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(addMethod); + + ExtensionDataSetter extensionDataSetter = (o, key, value) => + { + object dictionary = getExtensionDataDictionary(o); + if (dictionary == null) + { + dictionary = createExtensionDataDictionary(); + setExtensionDataDictionary(o, dictionary); + } + + // convert object value to JToken so it is compatible with dictionary + // could happen because of primitive types, type name handling and references + if (isJTokenValueType && !(value is JToken)) + value = JToken.FromObject(value); + + setExtensionDataDictionaryValue(dictionary, key, value); + }; + + Type enumerableWrapper = typeof(DictionaryEnumerator<,>).MakeGenericType(keyType, valueType); + ConstructorInfo constructors = enumerableWrapper.GetConstructors().First(); + MethodCall createEnumerableWrapper = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(constructors); + + ExtensionDataGetter extensionDataGetter = o => + { + object dictionary = getExtensionDataDictionary(o); + if (dictionary == null) + return null; + + return (IEnumerable>)createEnumerableWrapper(null, dictionary); + }; + + if (extensionDataAttribute.ReadData) + contract.ExtensionDataSetter = extensionDataSetter; + + if (extensionDataAttribute.WriteData) + contract.ExtensionDataGetter = extensionDataGetter; + } + + internal struct DictionaryEnumerator : IEnumerable>, IEnumerator> + { + private readonly IEnumerator> _e; + + public DictionaryEnumerator(IEnumerable> e) + { + ValidationUtils.ArgumentNotNull(e, "e"); + _e = e.GetEnumerator(); } - return extensionDataSetter; + public bool MoveNext() + { + return _e.MoveNext(); + } + + public void Reset() + { + _e.Reset(); + } + + public KeyValuePair Current + { + get { return new KeyValuePair(_e.Current.Key, _e.Current.Value); } + } + + public void Dispose() + { + _e.Dispose(); + } + + object IEnumerator.Current + { + get { return Current; } + } + + public IEnumerator> GetEnumerator() + { + return this; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this; + } } private ConstructorInfo GetAttributeConstructor(Type objectType) diff --git a/Src/Newtonsoft.Json/Serialization/JsonContract.cs b/Src/Newtonsoft.Json/Serialization/JsonContract.cs index 9de15ed0..c90149a7 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonContract.cs @@ -24,6 +24,7 @@ #endregion using System; +using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Runtime.Serialization; @@ -70,7 +71,13 @@ namespace Newtonsoft.Json.Serialization /// The object to set extension data on. /// The extension data key. /// The extension data value. - public delegate void ExtensionDataSetter(object o, string key, JToken value); + public delegate void ExtensionDataSetter(object o, string key, object value); + + /// + /// Gets extension data for an object during serialization. + /// + /// The object to set extension data on. + public delegate IEnumerable> ExtensionDataGetter(object o); /// /// Contract details for a used by the . diff --git a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs index 342f0e99..8ad93da0 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs @@ -81,6 +81,11 @@ namespace Newtonsoft.Json.Serialization /// public ExtensionDataSetter ExtensionDataSetter { get; set; } + /// + /// Gets or sets the extension data getter. + /// + public ExtensionDataGetter ExtensionDataGetter { get; set; } + private bool? _hasRequiredOrDefaultValueProperties; internal bool HasRequiredOrDefaultValueProperties { diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index fcd45313..bccf9687 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -1733,7 +1733,7 @@ To fix this error either change the environment to be fully trusted, change the if (!reader.Read()) break; - SetExtensionData(contract, reader, memberName, newObject); + SetExtensionData(contract, member, reader, memberName, newObject); continue; } @@ -1749,7 +1749,7 @@ To fix this error either change the environment to be fully trusted, change the // set extension data if property is ignored or readonly if (!SetPropertyValue(property, propertyConverter, contract, member, reader, newObject)) - SetExtensionData(contract, reader, memberName, newObject); + SetExtensionData(contract, member, reader, memberName, newObject); } catch (Exception ex) { @@ -1780,15 +1780,15 @@ To fix this error either change the environment to be fully trusted, change the return newObject; } - private void SetExtensionData(JsonObjectContract contract, JsonReader reader, string memberName, object o) + private void SetExtensionData(JsonObjectContract contract, JsonProperty member, JsonReader reader, string memberName, object o) { if (contract.ExtensionDataSetter != null) { try { - JToken extensionDataValue = JToken.ReadFrom(reader); + object value = CreateValueInternal(reader, null, null, null, contract, member, null); - contract.ExtensionDataSetter(o, memberName, extensionDataValue); + contract.ExtensionDataSetter(o, memberName, value); } catch (Exception ex) { diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 5056479a..77be1f9b 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -393,6 +393,37 @@ namespace Newtonsoft.Json.Serialization } } + if (contract.ExtensionDataGetter != null) + { + IEnumerable> extensionData = contract.ExtensionDataGetter(value); + if (extensionData != null) + { + foreach (KeyValuePair e in extensionData) + { + JsonContract keyContract = GetContractSafe(e.Key); + JsonContract valueContract = GetContractSafe(e.Value); + + bool escape; + string propertyName = GetPropertyName(writer, e.Key, keyContract, out escape); + + if (ShouldWriteReference(e.Value, null, valueContract, contract, member)) + { + writer.WritePropertyName(propertyName); + WriteReference(writer, e.Value); + } + else + { + if (!CheckForCircularReference(writer, e.Value, null, valueContract, contract, member)) + continue; + + writer.WritePropertyName(propertyName); + + SerializeValue(writer, e.Value, valueContract, null, contract, member); + } + } + } + } + writer.WriteEndObject(); _serializeStack.RemoveAt(_serializeStack.Count - 1); @@ -854,7 +885,7 @@ To fix this error either change the environment to be fully trusted, change the foreach (DictionaryEntry entry in values) { bool escape; - string propertyName = GetPropertyName(writer, entry, contract.KeyContract, out escape); + string propertyName = GetPropertyName(writer, entry.Key, contract.KeyContract, out escape); propertyName = (contract.PropertyNameResolver != null) ? contract.PropertyNameResolver(propertyName) @@ -896,20 +927,18 @@ To fix this error either change the environment to be fully trusted, change the OnSerialized(writer, contract, underlyingDictionary); } - private string GetPropertyName(JsonWriter writer, DictionaryEntry entry, JsonContract contract, out bool escape) + private string GetPropertyName(JsonWriter writer, object name, JsonContract contract, out bool escape) { - object key = entry.Key; - string propertyName; - JsonPrimitiveContract primitiveContract = contract as JsonPrimitiveContract; - if (primitiveContract != null) + if (contract.ContractType == JsonContractType.Primitive) { + JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract)contract; if (primitiveContract.TypeCode == PrimitiveTypeCode.DateTime || primitiveContract.TypeCode == PrimitiveTypeCode.DateTimeNullable) { escape = false; StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); - DateTimeUtils.WriteDateTimeString(sw, (DateTime)key, writer.DateFormatHandling, writer.DateFormatString, writer.Culture); + DateTimeUtils.WriteDateTimeString(sw, (DateTime)name, writer.DateFormatHandling, writer.DateFormatString, writer.Culture); return sw.ToString(); } #if !NET20 @@ -917,17 +946,17 @@ To fix this error either change the environment to be fully trusted, change the { escape = false; StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); - DateTimeUtils.WriteDateTimeOffsetString(sw, (DateTimeOffset)key, writer.DateFormatHandling, writer.DateFormatString, writer.Culture); + DateTimeUtils.WriteDateTimeOffsetString(sw, (DateTimeOffset)name, writer.DateFormatHandling, writer.DateFormatString, writer.Culture); return sw.ToString(); } #endif else { escape = true; - return Convert.ToString(key, CultureInfo.InvariantCulture); + return Convert.ToString(name, CultureInfo.InvariantCulture); } } - else if (TryConvertToString(key, key.GetType(), out propertyName)) + else if (TryConvertToString(name, name.GetType(), out propertyName)) { escape = true; return propertyName; @@ -935,7 +964,7 @@ To fix this error either change the environment to be fully trusted, change the else { escape = true; - return key.ToString(); + return name.ToString(); } } diff --git a/Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs b/Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs index 6b584c07..920d7002 100644 --- a/Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs +++ b/Src/Newtonsoft.Json/Utilities/ExpressionReflectionDelegateFactory.cs @@ -87,6 +87,10 @@ namespace Newtonsoft.Json.Utilities else callExpression = Expression.Block(callExpression, Expression.Constant(null)); } + else + { + callExpression = EnsureCastExpression(callExpression, type); + } LambdaExpression lambdaExpression = Expression.Lambda(typeof(MethodCall), callExpression, targetParameterExpression, argsParameterExpression);