diff --git a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs index bf24d244..9c1f1396 100644 --- a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs @@ -777,14 +777,28 @@ keyword such as type of business."" name.pNumbers.Add(p1); name.pNumbers.Add(p2); - string json = JsonConvert.SerializeObject(name); + string json = JsonConvert.SerializeObject(name, Formatting.Indented); + + Assert.AreEqual(@"{ + ""personsName"": ""The Idiot in Next To Me"", + ""pNumbers"": [ + { + ""phoneNumber"": ""555-1212"" + }, + { + ""phoneNumber"": ""444-1212"" + } + ] +}", json); Name newName = JsonConvert.DeserializeObject(json); Assert.AreEqual("The Idiot in Next To Me", newName.personsName); - // not passed in as part of the constructor, values not deserialized - Assert.AreEqual(0, newName.pNumbers.Count); + // not passed in as part of the constructor but assigned to pNumbers property + Assert.AreEqual(2, newName.pNumbers.Count); + Assert.AreEqual("555-1212", newName.pNumbers[0].phoneNumber); + Assert.AreEqual("444-1212", newName.pNumbers[1].phoneNumber); } [Test] @@ -1323,15 +1337,103 @@ keyword such as type of business."" Console.WriteLine(javascriptJson); } - public class SomeClass + public void GenericListAndDictionaryInterfaceProperties() { - public int? SomeProp { get; set; } + GenericListAndDictionaryInterfaceProperties o = new GenericListAndDictionaryInterfaceProperties(); + o.IDictionaryProperty = new Dictionary + { + {"one", 1}, + {"two", 2}, + {"three", 3} + }; + o.IListProperty = new List + { + 1, 2, 3 + }; + o.IEnumerableProperty = new List + { + 4, 5, 6 + }; + + string json = JsonConvert.SerializeObject(o, Formatting.Indented); + + Assert.AreEqual(@"{ + ""IEnumerableProperty"": [ + 4, + 5, + 6 + ], + ""IListProperty"": [ + 1, + 2, + 3 + ], + ""IDictionaryProperty"": { + ""one"": 1, + ""two"": 2, + ""three"": 3 + } +}", json); + + GenericListAndDictionaryInterfaceProperties deserializedObject = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(deserializedObject); + + CollectionAssert.AreEqual(o.IListProperty.ToArray(), deserializedObject.IListProperty.ToArray()); + CollectionAssert.AreEqual(o.IEnumerableProperty.ToArray(), deserializedObject.IEnumerableProperty.ToArray()); + CollectionAssert.AreEqual(o.IDictionaryProperty.ToArray(), deserializedObject.IDictionaryProperty.ToArray()); + } + + public class PropertyCase + { + public string firstName { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string lastName { get; set; } } [Test] - public void Main() + public void DeserializeBestMatchPropertyCase() { - var pokes = JsonConvert.DeserializeObject(@"{""SomeProp"":1}"); + string json = @"{ + ""firstName"": ""firstName"", + ""FirstName"": ""FirstName"", + ""LastName"": ""LastName"", + ""lastName"": ""lastName"", +}"; + + PropertyCase o = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(o); + + Assert.AreEqual("firstName", o.firstName); + Assert.AreEqual("FirstName", o.FirstName); + Assert.AreEqual("LastName", o.LastName); + Assert.AreEqual("lastName", o.lastName); + } + + public class SuperKlass + { + public string SuperProp { get; set; } + public SuperKlass() { SuperProp = "default superprop"; } + } + + public class SubKlass : SuperKlass + { + public string SubProp { get; set; } + public SubKlass(string subprop) { SubProp = subprop; } + } + + public void DeserializePropertiesOnToNonDefaultConstructor() + { + SubKlass i = new SubKlass("my subprop"); + i.SuperProp = "overrided superprop"; + + string json = JsonConvert.SerializeObject(i); + Assert.AreEqual(@"{""SubProp"":""my subprop"",""SuperProp"":""overrided superprop""}", json); + + SubKlass ii = JsonConvert.DeserializeObject(json); + + string newJson = JsonConvert.SerializeObject(ii); + Assert.AreEqual(@"{""SubProp"":""my subprop"",""SuperProp"":""overrided superprop""}", newJson); } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs index b4e4c218..bf3c210a 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs @@ -506,7 +506,7 @@ namespace Newtonsoft.Json.Tests.Linq new JValue(new DateTime(2009, 2, 15, 0, 0, 0, DateTimeKind.Utc)) ); - string json = a.ToString(new IsoDateTimeConverter()); + string json = a.ToString(Formatting.Indented, new IsoDateTimeConverter()); Assert.AreEqual(@"[ ""2009-02-15T00:00:00.0000000Z"" @@ -517,6 +517,19 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual(@"[""2009-02-15T00:00:00.0000000Z""]", json); } + [Test] + public void ToStringWithNoIndenting() + { + JArray a = + new JArray( + new JValue(new DateTime(2009, 2, 15, 0, 0, 0, DateTimeKind.Utc)) + ); + + string json = a.ToString(Formatting.None, new IsoDateTimeConverter()); + + Assert.AreEqual(@"[""2009-02-15T00:00:00.0000000Z""]", json); + } + [Test] public void AddAfterSelf() { diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj index 8047b49b..6f18e175 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Compact.csproj @@ -58,6 +58,7 @@ + @@ -75,6 +76,7 @@ + @@ -86,6 +88,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index e72fba9e..42e89cca 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -63,6 +63,7 @@ + @@ -80,6 +81,7 @@ + @@ -91,6 +93,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj index 0625cd79..be18826d 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -81,6 +81,7 @@ + @@ -94,6 +95,7 @@ + @@ -105,6 +107,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs index 3c6bcb7a..13339062 100644 --- a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs +++ b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs @@ -281,14 +281,27 @@ namespace Newtonsoft.Json.Tests.Schema Assert.AreEqual(JsonSchemaType.String, schema.Type); } - public class CustomDirectoryInfoSerializer : JsonSerializer + //public class CustomDirectoryInfoSerializer : JsonSerializer + //{ + // protected override Newtonsoft.Json.Serialization.JsonMemberMappingCollection GetMemberMappings(Type objectType) + // { + // JsonMemberMappingCollection mappings = base.GetMemberMappings(objectType); + + // JsonMemberMappingCollection c = new JsonMemberMappingCollection(); + // CollectionUtils.AddRange(c, (IEnumerable) mappings.Where(m => m.MappingName != "Root")); + + // return c; + // } + //} + + public class CustomDirectoryInfoMapper : DefaultMappingResolver { - protected override Newtonsoft.Json.Serialization.JsonMemberMappingCollection GetMemberMappings(Type objectType) + public override JsonMemberMappingCollection ResolveMappings(Type type) { - JsonMemberMappingCollection mappings = base.GetMemberMappings(objectType); + JsonMemberMappingCollection mappings = base.ResolveMappings(type); JsonMemberMappingCollection c = new JsonMemberMappingCollection(); - CollectionUtils.AddRange(c, (IEnumerable) mappings.Where(m => m.MappingName != "Root")); + CollectionUtils.AddRange(c, (IEnumerable)mappings.Where(m => m.MappingName != "Root")); return c; } @@ -366,9 +379,10 @@ namespace Newtonsoft.Json.Tests.Schema DirectoryInfo temp = new DirectoryInfo(@"c:\temp"); JsonTokenWriter jsonWriter = new JsonTokenWriter(); - CustomDirectoryInfoSerializer customSerializer = new CustomDirectoryInfoSerializer(); - customSerializer.Converters.Add(new IsoDateTimeConverter()); - customSerializer.Serialize(jsonWriter, temp); + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new IsoDateTimeConverter()); + serializer.MappingResolver = new CustomDirectoryInfoMapper(); + serializer.Serialize(jsonWriter, temp); List errors = new List(); jsonWriter.Token.Validate(schema, (sender, args) => errors.Add(args.Message)); @@ -377,5 +391,76 @@ namespace Newtonsoft.Json.Tests.Schema Assert.AreEqual("Non-optional properties are missing from object: Root.", errors[0]); Assert.AreEqual("Non-optional properties are missing from object: Root.", errors[1]); } + + [Test] + public void GenerateSchemaCamelCase() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + generator.MappingResolver = new CamelCaseMappingResolver(); + + JsonSchema schema = generator.Generate(typeof (DirectoryInfo), true); + + string json = schema.ToString(); + + Assert.AreEqual(@"{ + ""id"": ""System.IO.DirectoryInfo"", + ""type"": [ + ""object"", + ""null"" + ], + ""additionalProperties"": false, + ""properties"": { + ""name"": { + ""type"": [ + ""string"", + ""null"" + ] + }, + ""parent"": { + ""$ref"": ""System.IO.DirectoryInfo"" + }, + ""exists"": { + ""type"": ""boolean"" + }, + ""root"": { + ""$ref"": ""System.IO.DirectoryInfo"" + }, + ""fullName"": { + ""type"": [ + ""string"", + ""null"" + ] + }, + ""extension"": { + ""type"": [ + ""string"", + ""null"" + ] + }, + ""creationTime"": { + ""type"": ""string"" + }, + ""creationTimeUtc"": { + ""type"": ""string"" + }, + ""lastAccessTime"": { + ""type"": ""string"" + }, + ""lastAccessTimeUtc"": { + ""type"": ""string"" + }, + ""lastWriteTime"": { + ""type"": ""string"" + }, + ""lastWriteTimeUtc"": { + ""type"": ""string"" + }, + ""attributes"": { + ""type"": ""integer"" + } + } +}", json); + } } } diff --git a/Src/Newtonsoft.Json.Tests/Serialization/CamelCaseMappingResolverTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/CamelCaseMappingResolverTests.cs new file mode 100644 index 00000000..16e8c40f --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/Serialization/CamelCaseMappingResolverTests.cs @@ -0,0 +1,122 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Serialization; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; +using Newtonsoft.Json.Linq; +using System.Reflection; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class CamelCaseMappingResolverTests : TestFixtureBase + { + [Test] + public void JsonConvertSerializerSettings() + { + Person person = new Person(); + person.BirthDate = new DateTime(2000, 11, 20, 23, 55, 44, DateTimeKind.Utc); + person.LastModified = new DateTime(2000, 11, 20, 23, 55, 44, DateTimeKind.Utc); + person.Name = "Name!"; + + string json = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings + { + MappingResolver = new CamelCaseMappingResolver() + }); + + Assert.AreEqual(@"{ + ""name"": ""Name!"", + ""birthDate"": ""\/Date(974764544000)\/"", + ""lastModified"": ""\/Date(974764544000)\/"" +}", json); + + Person deserializedPerson = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + MappingResolver = new CamelCaseMappingResolver() + }); + + Assert.AreEqual(person.BirthDate, deserializedPerson.BirthDate); + Assert.AreEqual(person.LastModified, deserializedPerson.LastModified); + Assert.AreEqual(person.Name, deserializedPerson.Name); + } + + [Test] + public void JsonTokenWriter() + { + JsonIgnoreAttributeOnClassTestClass ignoreAttributeOnClassTestClass = new JsonIgnoreAttributeOnClassTestClass(); + ignoreAttributeOnClassTestClass.Field = int.MinValue; + + JsonSerializer serializer = new JsonSerializer(); + serializer.MappingResolver = new CamelCaseMappingResolver(); + + JsonTokenWriter writer = new JsonTokenWriter(); + + serializer.Serialize(writer, ignoreAttributeOnClassTestClass); + + JObject o = (JObject) writer.Token; + JProperty p = o.Property("theField"); + + Assert.IsNotNull(p); + Assert.AreEqual(int.MinValue, (int)p.Value); + + string json = o.ToString(); + } + + [Test] + public void MemberSearchFlags() + { + PrivateMembersClass privateMembersClass = new PrivateMembersClass("PrivateString!", "InternalString!"); + + string json = JsonConvert.SerializeObject(privateMembersClass, Formatting.Indented, new JsonSerializerSettings + { + MappingResolver = new CamelCaseMappingResolver { MemberSearchFlags = BindingFlags.NonPublic | BindingFlags.Instance } + }); + + Assert.AreEqual(@"{ + ""_privateString"": ""PrivateString!"", + ""i"": 0, + ""_internalString"": ""InternalString!"" +}", json); + + PrivateMembersClass deserializedPrivateMembersClass = JsonConvert.DeserializeObject(@"{ + ""_privateString"": ""Private!"", + ""i"": -2, + ""_internalString"": ""Internal!"" +}", new JsonSerializerSettings + { + MappingResolver = new CamelCaseMappingResolver { MemberSearchFlags = BindingFlags.NonPublic | BindingFlags.Instance } + }); + + Assert.AreEqual("Private!", ReflectionUtils.GetMemberValue(typeof(PrivateMembersClass).GetField("_privateString", BindingFlags.Instance | BindingFlags.NonPublic), deserializedPrivateMembersClass)); + Assert.AreEqual("Internal!", ReflectionUtils.GetMemberValue(typeof(PrivateMembersClass).GetField("_internalString", BindingFlags.Instance | BindingFlags.NonPublic), deserializedPrivateMembersClass)); + Assert.AreEqual(-2, ReflectionUtils.GetMemberValue(typeof(PrivateMembersClass).GetField("i", BindingFlags.Instance | BindingFlags.NonPublic), deserializedPrivateMembersClass)); + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/GenericListAndDictionaryInterfaceProperties.cs b/Src/Newtonsoft.Json.Tests/TestObjects/GenericListAndDictionaryInterfaceProperties.cs new file mode 100644 index 00000000..5514778d --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/TestObjects/GenericListAndDictionaryInterfaceProperties.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class GenericListAndDictionaryInterfaceProperties + { + public IEnumerable IEnumerableProperty { get; set; } + public IList IListProperty { get; set; } + public IDictionary IDictionaryProperty { get; set; } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/PrivateMembersClass.cs b/Src/Newtonsoft.Json.Tests/TestObjects/PrivateMembersClass.cs new file mode 100644 index 00000000..2b86d176 --- /dev/null +++ b/Src/Newtonsoft.Json.Tests/TestObjects/PrivateMembersClass.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class PrivateMembersClass + { + public PrivateMembersClass(string privateString, string internalString) + { + _privateString = privateString; + _internalString = internalString; + } + + public PrivateMembersClass() + { + } + + private string _privateString; + private int i; + internal string _internalString; + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index c2945d91..5d293189 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -467,12 +467,24 @@ namespace Newtonsoft.Json /// A collection converters used while serializing. /// A JSON string representation of the object. public static string SerializeObject(object value, params JsonConverter[] converters) + { + return SerializeObject(value, Formatting.None, converters); + } + + /// + /// Serializes the specified object to a JSON string using a collection of . + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// A collection converters used while serializing. + /// A JSON string representation of the object. + public static string SerializeObject(object value, Formatting formatting, params JsonConverter[] converters) { JsonSerializerSettings settings = (converters != null && converters.Length > 0) ? new JsonSerializerSettings { Converters = converters } : null; - return SerializeObject(value, Formatting.None, settings); + return SerializeObject(value, formatting, settings); } /// diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 2c0832c9..bdff788c 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -53,6 +53,7 @@ namespace Newtonsoft.Json private DefaultValueHandling _defaultValueHandling; private int _level; private JsonConverterCollection _converters; + private IMappingResolver _mappingResolver; /// /// Get or set how reference loops (e.g. a class referencing itself) is handled. @@ -144,6 +145,12 @@ namespace Newtonsoft.Json return _converters; } } + + public IMappingResolver MappingResolver + { + get { return _mappingResolver; } + set { _mappingResolver = value; } + } #endregion /// @@ -170,7 +177,7 @@ namespace Newtonsoft.Json if (settings != null) { - if (!CollectionUtils.IsNullOrEmpty(settings.Converters)) + if (!CollectionUtils.IsNullOrEmpty(settings.Converters)) jsonSerializer.Converters.AddRange(settings.Converters); jsonSerializer.ReferenceLoopHandling = settings.ReferenceLoopHandling; @@ -178,6 +185,7 @@ namespace Newtonsoft.Json jsonSerializer.ObjectCreationHandling = settings.ObjectCreationHandling; jsonSerializer.NullValueHandling = settings.NullValueHandling; jsonSerializer.DefaultValueHandling = settings.DefaultValueHandling; + jsonSerializer.MappingResolver = settings.MappingResolver; } return jsonSerializer; @@ -363,62 +371,26 @@ namespace Newtonsoft.Json } } - protected virtual JsonMemberMappingCollection GetMemberMappings(Type objectType) + private JsonMemberMappingCollection GetMemberMappings(Type objectType) { ValidationUtils.ArgumentNotNull(objectType, "objectType"); - return JsonTypeReflector.GetMemberMappings(objectType); + if (_mappingResolver != null) + return _mappingResolver.ResolveMappings(objectType); + + return DefaultMappingResolver.Instance.ResolveMappings(objectType); } private void SetObjectMember(JsonReader reader, object target, Type targetType, string memberName) { JsonMemberMappingCollection memberMappings = GetMemberMappings(targetType); - Type memberType; - object value; - // test if a member with memberName exists on the type - // otherwise test if target is a dictionary and assign value with the key if it is - if (memberMappings.Contains(memberName)) + JsonMemberMapping memberMapping; + // attempt exact case match first + // then try match ignoring case + if (memberMappings.TryGetClosestMatchMapping(memberName, out memberMapping)) { - JsonMemberMapping memberMapping = memberMappings[memberName]; - - if (memberMapping.Ignored) - { - reader.Skip(); - return; - } - - // get the member's underlying type - memberType = ReflectionUtils.GetMemberUnderlyingType(memberMapping.Member); - - object currentValue = null; - bool useExistingValue = false; - - if ((_objectCreationHandling == ObjectCreationHandling.Auto || _objectCreationHandling == ObjectCreationHandling.Reuse) - && (reader.TokenType == JsonToken.StartArray || reader.TokenType == JsonToken.StartObject)) - { - currentValue = ReflectionUtils.GetMemberValue(memberMapping.Member, target); - - useExistingValue = (currentValue != null && !memberType.IsArray && !ReflectionUtils.InheritsGenericDefinition(memberType, typeof(ReadOnlyCollection<>))); - } - - - if (!memberMapping.Writable && !useExistingValue) - { - reader.Skip(); - return; - } - - value = CreateObject(reader, memberType, (useExistingValue) ? currentValue : null, JsonTypeReflector.GetConverter(memberMapping.Member, memberType)); - - if (_nullValueHandling == NullValueHandling.Ignore && value == null) - return; - - if (_defaultValueHandling == DefaultValueHandling.Ignore && object.Equals(value, memberMapping.DefaultValue)) - return; - - if (!useExistingValue) - ReflectionUtils.SetMemberValue(memberMapping.Member, target, value); + SetMappingValue(memberMapping, reader, target); } else { @@ -429,8 +401,64 @@ namespace Newtonsoft.Json } } + private void SetMappingValue(JsonMemberMapping memberMapping, JsonReader reader, object target) + { + if (memberMapping.Ignored) + { + reader.Skip(); + return; + } + + // get the member's underlying type + Type memberType = ReflectionUtils.GetMemberUnderlyingType(memberMapping.Member); + + object currentValue = null; + bool useExistingValue = false; + + if ((_objectCreationHandling == ObjectCreationHandling.Auto || _objectCreationHandling == ObjectCreationHandling.Reuse) + && (reader.TokenType == JsonToken.StartArray || reader.TokenType == JsonToken.StartObject)) + { + currentValue = ReflectionUtils.GetMemberValue(memberMapping.Member, target); + + useExistingValue = (currentValue != null && !memberType.IsArray && !ReflectionUtils.InheritsGenericDefinition(memberType, typeof(ReadOnlyCollection<>))); + } + + if (!memberMapping.Writable && !useExistingValue) + { + reader.Skip(); + return; + } + + object value = CreateObject(reader, memberType, (useExistingValue) ? currentValue : null, JsonTypeReflector.GetConverter(memberMapping.Member, memberType)); + + if (!useExistingValue && ShouldSetMappingValue(memberMapping, value)) + ReflectionUtils.SetMemberValue(memberMapping.Member, target, value); + } + + private bool ShouldSetMappingValue(JsonMemberMapping memberMapping, object value) + { + if (_nullValueHandling == NullValueHandling.Ignore && value == null) + return false; + + if (_defaultValueHandling == DefaultValueHandling.Ignore && Equals(value, memberMapping.DefaultValue)) + return false; + + if (!memberMapping.Writable) + return false; + + return true; + } + private object CreateAndPopulateDictionary(JsonReader reader, Type objectType) { + if (IsTypeGenericDictionaryInterface(objectType)) + { + Type keyType; + Type valueType; + ReflectionUtils.GetDictionaryKeyValueTypes(objectType, out keyType, out valueType); + objectType = ReflectionUtils.MakeGenericType(typeof(Dictionary<,>), keyType, valueType); + } + IWrappedDictionary dictionary = CollectionUtils.CreateDictionaryWrapper(Activator.CreateInstance(objectType)); PopulateDictionary(dictionary, reader); @@ -465,6 +493,12 @@ namespace Newtonsoft.Json private object CreateAndPopulateList(JsonReader reader, Type objectType) { + if (IsTypeGenericCollectionInterface(objectType)) + { + Type itemType = ReflectionUtils.GetCollectionItemType(objectType); + objectType = ReflectionUtils.MakeGenericType(typeof(List<>), itemType); + } + return CollectionUtils.CreateAndPopulateList(objectType, l => PopulateList(l, ReflectionUtils.GetCollectionItemType(objectType), reader)); } @@ -489,67 +523,117 @@ namespace Newtonsoft.Json throw new JsonSerializationException("Unexpected end when deserializing array."); } + private bool IsTypeGenericDictionaryInterface(Type type) + { + if (!type.IsGenericType) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + + return (genericDefinition == typeof (IDictionary<,>)); + } + + private bool IsTypeGenericCollectionInterface(Type type) + { + if (!type.IsGenericType) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + + return (genericDefinition == typeof (IList<>) + || genericDefinition == typeof (ICollection<>) + || genericDefinition == typeof (IEnumerable<>)); + } + private object CreateAndPopulateObject(JsonReader reader, Type objectType) { object newObject; if (objectType.IsInterface || objectType.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, objectType)); - } - else if (ReflectionUtils.HasDefaultConstructor(objectType)) + + if (ReflectionUtils.HasDefaultConstructor(objectType)) { newObject = Activator.CreateInstance(objectType); PopulateObject(newObject, reader, objectType); return newObject; } - else + + return CreateObjectFromNonDefaultConstructor(objectType, reader); + } + + private object CreateObjectFromNonDefaultConstructor(Type objectType, JsonReader reader) + { + // object should have a single constructor + ConstructorInfo c = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).SingleOrDefault(); + + if (c == null) + throw new JsonSerializationException("Could not find a public constructor for type {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + // create a dictionary to put retrieved values into + JsonMemberMappingCollection memberMappings = GetMemberMappings(objectType); + IDictionary mappingValues = memberMappings.ToDictionary(kv => kv, kv => (object)null); + + bool exit = false; + while (!exit && reader.Read()) { - ConstructorInfo c = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).SingleOrDefault(); - - if (c == null) - throw new JsonSerializationException("Could not find a public constructor for type {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); - - // create a dictionary to put retrieved values into - IDictionary constructorParameters = c.GetParameters().ToDictionary(p => p, p => (object)null); - JsonMemberMappingCollection mappings = GetMemberMappings(objectType); - - bool exit = false; - while (!exit && reader.Read()) + switch (reader.TokenType) { - 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)); + 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)); - ParameterInfo matchingConstructorParameter = constructorParameters.ForgivingCaseSensitiveFind(kv => kv.Key.Name, memberName).Key; - JsonMemberMapping mapping; + JsonMemberMapping memberMapping; + // attempt exact case match first + // then try match ignoring case + if (memberMappings.TryGetClosestMatchMapping(memberName, out memberMapping)) + { + if (!memberMapping.Ignored) + { + Type memberType = ReflectionUtils.GetMemberUnderlyingType(memberMapping.Member); + mappingValues[memberMapping] = CreateObject(reader, memberType, null, memberMapping.MemberConverter); + } + } + else + { + if (_missingMemberHandling == MissingMemberHandling.Error) + throw new JsonSerializationException("Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, objectType.Name)); - if (matchingConstructorParameter != null && mappings.TryGetMapping(memberName, out mapping) && !mapping.Ignored) - { - constructorParameters[matchingConstructorParameter] = CreateObject(reader, matchingConstructorParameter.ParameterType, null, mapping.MemberConverter); - } - else - { - // skip token and all child tokens - reader.Skip(); - } - break; - case JsonToken.EndObject: - exit = true; - break; - default: - throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); - } + reader.Skip(); + } + break; + case JsonToken.EndObject: + exit = true; + break; + default: + throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); } - - newObject = ReflectionUtils.CreateInstance(objectType, constructorParameters.Values.ToArray()); - - return newObject; } + + IDictionary constructorParameters = c.GetParameters().ToDictionary(p => p, p => (object)null); + IDictionary remainingMappingValues = new Dictionary(); + + foreach (KeyValuePair mappingValue in mappingValues) + { + ParameterInfo matchingConstructorParameter = constructorParameters.ForgivingCaseSensitiveFind(kv => kv.Key.Name, mappingValue.Key.MappingName).Key; + if (matchingConstructorParameter != null) + constructorParameters[matchingConstructorParameter] = mappingValue.Value; + else + remainingMappingValues.Add(mappingValue); + } + + object createdObject = ReflectionUtils.CreateInstance(objectType, constructorParameters.Values.ToArray()); + + // go through unused values and set the newly created object's properties + foreach (KeyValuePair remainingMappingValue in remainingMappingValues) + { + if (ShouldSetMappingValue(remainingMappingValue.Key, remainingMappingValue.Value)) + ReflectionUtils.SetMemberValue(remainingMappingValue.Key.Member, createdObject, remainingMappingValue.Value); + } + + return createdObject; } private object PopulateObject(object newObject, JsonReader reader, Type objectType) @@ -557,11 +641,6 @@ namespace Newtonsoft.Json JsonMemberMappingCollection memberMappings = GetMemberMappings(objectType); Dictionary requiredMappings = memberMappings.Where(m => m.Required).ToDictionary(m => m.MappingName, m => false); - //List requiredMappingContexts = - // memberMappings.Where(m => m.Required).Select(m => new RequiredMemberContext(m.MappingName, false)).ToList(); - - //ObjectContext context = new ObjectContext(newObject, requiredMappingContexts); - //DeserializeStack.Add(context); while (reader.Read()) { @@ -574,10 +653,7 @@ namespace Newtonsoft.Json throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); if (reader.TokenType != JsonToken.Null) - { - if (requiredMappings.ContainsKey(memberName)) - requiredMappings[memberName] = true; - } + SetRequiredMapping(memberName, requiredMappings); SetObjectMember(reader, newObject, objectType, memberName); break; @@ -587,8 +663,6 @@ namespace Newtonsoft.Json if (!requiredMapping.Value) throw new JsonSerializationException("Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, requiredMapping.Key)); } - // TODO: move below, change to RemoveAt - //DeserializeStack.Remove(context); return newObject; default: throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); @@ -597,6 +671,28 @@ namespace Newtonsoft.Json throw new JsonSerializationException("Unexpected end when deserializing object."); } + + private void SetRequiredMapping(string memberName, Dictionary requiredMappings) + { + // first attempt to find exact case match + // then attempt case insensitive match + if (requiredMappings.ContainsKey(memberName)) + { + requiredMappings[memberName] = true; + } + else + { + foreach (KeyValuePair requiredMapping in requiredMappings) + { + if (string.Compare(requiredMapping.Key, requiredMapping.Key, StringComparison.OrdinalIgnoreCase) == 0) + { + requiredMappings[requiredMapping.Key] = true; + break; + } + } + } + } + #endregion #region Serialize diff --git a/Src/Newtonsoft.Json/JsonSerializerSettings.cs b/Src/Newtonsoft.Json/JsonSerializerSettings.cs index 240ba76e..7d23f010 100644 --- a/Src/Newtonsoft.Json/JsonSerializerSettings.cs +++ b/Src/Newtonsoft.Json/JsonSerializerSettings.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json @@ -47,6 +48,7 @@ namespace Newtonsoft.Json /// /// The converters. public IList Converters { get; set; } + public IMappingResolver MappingResolver { get; set; } /// /// Initializes a new instance of the class. diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 24a6096f..365e6284 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -339,20 +339,21 @@ namespace Newtonsoft.Json.Linq /// public override string ToString() { - return ToString(null); + return ToString(Formatting.Indented); } /// - /// Returns the indented JSON for this token using any given converters. + /// Returns the JSON for this token using the given formatting and converters. /// + /// Indicates how the output is formatted. /// A collection of which will be used when writing the token. /// - public string ToString(params JsonConverter[] converters) + public string ToString(Formatting formatting, params JsonConverter[] converters) { using (StringWriter sw = new StringWriter(CultureInfo.InvariantCulture)) { JsonTextWriter jw = new JsonTextWriter(sw); - jw.Formatting = Formatting.Indented; + jw.Formatting = formatting; WriteTo(jw, converters); diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj index a656114a..82166fad 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj @@ -108,6 +108,9 @@ + + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index 9c4d5ae0..359508e4 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -97,6 +97,9 @@ + + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index a9e265b0..683d18bb 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -126,6 +126,9 @@ + + + diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs index 6d142fff..d0e4e5fd 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs @@ -24,20 +24,20 @@ #endregion using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using Newtonsoft.Json.Utilities; using System.Globalization; -using Newtonsoft.Json.Serialization; -using Newtonsoft.Json.Linq; using System.ComponentModel; +using System.Collections.Generic; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Serialization; namespace Newtonsoft.Json.Schema { public class JsonSchemaGenerator { public UndefinedSchemaIdHandling UndefinedSchemaIdHandling { get; set; } + public IMappingResolver MappingResolver { get; set; } private class TypeSchema { @@ -237,7 +237,12 @@ namespace Newtonsoft.Json.Schema { CurrentSchema.Id = GetTypeId(type, false); - JsonMemberMappingCollection mappings = JsonTypeReflector.GetMemberMappings(type); + JsonMemberMappingCollection mappings; + + if (MappingResolver != null) + mappings = MappingResolver.ResolveMappings(type); + else + mappings = DefaultMappingResolver.Instance.ResolveMappings(type); CurrentSchema.Properties = new Dictionary(); foreach (JsonMemberMapping mapping in mappings) diff --git a/Src/Newtonsoft.Json/Serialization/CamelCaseMappingResolver.cs b/Src/Newtonsoft.Json/Serialization/CamelCaseMappingResolver.cs new file mode 100644 index 00000000..567e7c5e --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/CamelCaseMappingResolver.cs @@ -0,0 +1,51 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + public class CamelCaseMappingResolver : DefaultMappingResolver + { + protected override string ResolveMappingName(string mappedName) + { + // lower case the first letter of the passed in name + if (string.IsNullOrEmpty(mappedName)) + return mappedName; + + if (!char.IsUpper(mappedName[0])) + return mappedName; + + string camelCaseName = char.ToLower(mappedName[0]).ToString(); + if (mappedName.Length > 1) + camelCaseName += mappedName.Substring(1); + + return camelCaseName; + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/DefaultMappingResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultMappingResolver.cs new file mode 100644 index 00000000..734fc09c --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/DefaultMappingResolver.cs @@ -0,0 +1,126 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + public class DefaultMappingResolver : IMappingResolver + { + internal static readonly IMappingResolver Instance = new DefaultMappingResolver(); + + private readonly Dictionary TypeMemberMappingsCache = new Dictionary(); + + public BindingFlags MemberSearchFlags { get; set; } + + public DefaultMappingResolver() + { + MemberSearchFlags = BindingFlags.Public | BindingFlags.Instance; + } + + public virtual JsonMemberMappingCollection ResolveMappings(Type type) + { + JsonMemberMappingCollection memberMappings; + + if (TypeMemberMappingsCache.TryGetValue(type, out memberMappings)) + return memberMappings; + + // double check locking to avoid threading issues + lock (TypeMemberMappingsCache) + { + if (TypeMemberMappingsCache.TryGetValue(type, out memberMappings)) + return memberMappings; + + memberMappings = CreateMemberMappings(type); + TypeMemberMappingsCache[type] = memberMappings; + + return memberMappings; + } + } + + protected virtual List GetSerializableMembers(Type objectType) + { + return ReflectionUtils.GetFieldsAndProperties(objectType, MemberSearchFlags); + } + + private JsonMemberMappingCollection CreateMemberMappings(Type objectType) + { + MemberSerialization memberSerialization = JsonTypeReflector.GetObjectMemberSerialization(objectType); + + List members = GetSerializableMembers(objectType); + if (members == null) + throw new JsonSerializationException("Null collection of seralizable members returned."); + + JsonMemberMappingCollection memberMappings = new JsonMemberMappingCollection(); + + foreach (MemberInfo member in members) + { + JsonMemberMapping memberMapping = CreateMemberMapping(memberSerialization, member); + + memberMappings.AddMapping(memberMapping); + } + + return memberMappings; + } + + protected virtual JsonMemberMapping CreateMemberMapping(MemberSerialization memberSerialization, MemberInfo member) + { + JsonPropertyAttribute propertyAttribute = ReflectionUtils.GetAttribute(member, true); + bool hasIgnoreAttribute = member.IsDefined(typeof(JsonIgnoreAttribute), true); + + string mappedName = (propertyAttribute != null && propertyAttribute.PropertyName != null) + ? propertyAttribute.PropertyName + : member.Name; + + string resolvedMappedName = ResolveMappingName(mappedName); + + bool required = (propertyAttribute != null) ? propertyAttribute.IsRequired : false; + + bool ignored = (hasIgnoreAttribute + || (memberSerialization == MemberSerialization.OptIn && propertyAttribute == null)); + + bool readable = ReflectionUtils.CanReadMemberValue(member); + bool writable = ReflectionUtils.CanSetMemberValue(member); + + JsonConverter memberConverter = JsonTypeReflector.GetConverter(member, ReflectionUtils.GetMemberUnderlyingType(member)); + + DefaultValueAttribute defaultValueAttribute = ReflectionUtils.GetAttribute(member, true); + object defaultValue = (defaultValueAttribute != null) ? defaultValueAttribute.Value : null; + + return new JsonMemberMapping(resolvedMappedName, member, ignored, readable, writable, memberConverter, defaultValue, required); + } + + protected virtual string ResolveMappingName(string mappedName) + { + return mappedName; + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/IMappingResolver.cs b/Src/Newtonsoft.Json/Serialization/IMappingResolver.cs new file mode 100644 index 00000000..685e9c28 --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/IMappingResolver.cs @@ -0,0 +1,46 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Resolves member mappings for a type. + /// + public interface IMappingResolver + { + /// + /// Returns a collection of for the specified + /// to be used during serialization. + /// + /// The type to be resolved. + /// A collection of to be used during serialization. + JsonMemberMappingCollection ResolveMappings(Type type); + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonMemberMappingCollection.cs b/Src/Newtonsoft.Json/Serialization/JsonMemberMappingCollection.cs index 57fc5991..11f90808 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonMemberMappingCollection.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonMemberMappingCollection.cs @@ -64,18 +64,29 @@ namespace Newtonsoft.Json.Serialization Add(memberMapping); } - public bool TryGetMapping(string mappingName, out JsonMemberMapping memberMapping) + public bool TryGetClosestMatchMapping(string mappingName, out JsonMemberMapping memberMapping) { - if (Contains(mappingName)) - { - memberMapping = this[mappingName]; + if (TryGetMapping(mappingName, StringComparison.Ordinal, out memberMapping)) return true; - } - else + if (TryGetMapping(mappingName, StringComparison.OrdinalIgnoreCase, out memberMapping)) + return true; + + return false; + } + + public bool TryGetMapping(string mappingName, StringComparison comparisonType, out JsonMemberMapping memberMapping) + { + foreach (JsonMemberMapping mapping in this) { - memberMapping = default(JsonMemberMapping); - return false; + if (string.Compare(mappingName, mapping.MappingName, comparisonType) == 0) + { + memberMapping = mapping; + return true; + } } + + memberMapping = default(JsonMemberMapping); + return false; } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index 41a2a8ee..1b3fe2d7 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -25,11 +25,8 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Globalization; -using System.Linq; using System.Reflection; -using System.Text; using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json.Serialization @@ -37,7 +34,6 @@ namespace Newtonsoft.Json.Serialization internal static class JsonTypeReflector { private static readonly Dictionary ConverterTypeCache = new Dictionary(); - private static readonly Dictionary TypeMemberMappingsCache = new Dictionary(); private static readonly Dictionary TypeContainerAttributeCache = new Dictionary(); public static JsonContainerAttribute GetJsonContainerAttribute(Type type) @@ -70,66 +66,6 @@ namespace Newtonsoft.Json.Serialization return objectAttribute.MemberSerialization; } - public static JsonMemberMappingCollection GetMemberMappings(Type objectType) - { - JsonMemberMappingCollection memberMappings; - - if (TypeMemberMappingsCache.TryGetValue(objectType, out memberMappings)) - return memberMappings; - - // double check locking to avoid threading issues - lock (TypeMemberMappingsCache) - { - if (TypeMemberMappingsCache.TryGetValue(objectType, out memberMappings)) - return memberMappings; - - memberMappings = CreateMemberMappings(objectType); - TypeMemberMappingsCache[objectType] = memberMappings; - - return memberMappings; - } - } - - public static JsonMemberMappingCollection CreateMemberMappings(Type objectType) - { - MemberSerialization memberSerialization = GetObjectMemberSerialization(objectType); - - List members = GetSerializableMembers(objectType); - if (members == null) - throw new JsonSerializationException("Null collection of seralizable members returned."); - - JsonMemberMappingCollection memberMappings = new JsonMemberMappingCollection(); - - foreach (MemberInfo member in members) - { - JsonPropertyAttribute propertyAttribute = ReflectionUtils.GetAttribute(member, true); - bool hasIgnoreAttribute = member.IsDefined(typeof(JsonIgnoreAttribute), true); - - string mappedName = (propertyAttribute != null && propertyAttribute.PropertyName != null) - ? propertyAttribute.PropertyName - : member.Name; - - bool required = (propertyAttribute != null) ? propertyAttribute.IsRequired : false; - - bool ignored = (hasIgnoreAttribute - || (memberSerialization == MemberSerialization.OptIn && propertyAttribute == null)); - - bool readable = ReflectionUtils.CanReadMemberValue(member); - bool writable = ReflectionUtils.CanSetMemberValue(member); - - JsonConverter memberConverter = GetConverter(member, ReflectionUtils.GetMemberUnderlyingType(member)); - - DefaultValueAttribute defaultValueAttribute = ReflectionUtils.GetAttribute(member, true); - object defaultValue = (defaultValueAttribute != null) ? defaultValueAttribute.Value : null; - - JsonMemberMapping memberMapping = new JsonMemberMapping(mappedName, member, ignored, readable, writable, memberConverter, defaultValue, required); - - memberMappings.AddMapping(memberMapping); - } - - return memberMappings; - } - private static Type GetConverterType(ICustomAttributeProvider attributeProvider) { Type converterType; @@ -170,10 +106,5 @@ namespace Newtonsoft.Json.Serialization return null; } - - public static List GetSerializableMembers(Type objectType) - { - return ReflectionUtils.GetFieldsAndProperties(objectType, BindingFlags.Public | BindingFlags.Instance); - } } -} +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs index 55b5effb..cace4bbc 100644 --- a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs @@ -352,6 +352,14 @@ namespace Newtonsoft.Json.Utilities return (IList)ReflectionUtils.CreateGeneric(typeof(List<>), listType); } + public static IDictionary CreateGenericDictionary(Type keyType, Type valueType) + { + ValidationUtils.ArgumentNotNull(keyType, "keyType"); + ValidationUtils.ArgumentNotNull(valueType, "valueType"); + + return (IDictionary)ReflectionUtils.CreateGeneric(typeof(Dictionary<,>), keyType, valueType); + } + public static bool IsListType(Type type) { ValidationUtils.ArgumentNotNull(type, "type"); diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index f67bda32..52d59889 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -224,7 +224,7 @@ namespace Newtonsoft.Json.Utilities { return type.GetElementType(); } - else if (ImplementsGenericDefinition(type, typeof(ICollection<>), out genericListType)) + else if (ImplementsGenericDefinition(type, typeof(IEnumerable<>), out genericListType)) { if (genericListType.IsGenericTypeDefinition) throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type));