-Added support to deserializing to IEnumerable<T>, IList<T>, IDictionary<K, V>

-Added populating members when creating object from non default constructor
-Added case best match when deserializing properties
-Added ToString(Formatting, JsonConverters[]) overload to JToken
-Added SerializeObject(object, Formatting, JsonConverter[]) overload to JsonConvert
-Added IMappingResolver. Property added to JsonSerializer, JsonSerializerSettings, JsonSchemaGenerator
-Added CamelCaseMappingResolver
This commit is contained in:
JamesNK
2009-03-14 00:38:28 +00:00
parent e5eaa14990
commit 9fd47e6334
24 changed files with 873 additions and 206 deletions
+109 -7
View File
@@ -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<Name>(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<string, int>
{
{"one", 1},
{"two", 2},
{"three", 3}
};
o.IListProperty = new List<int>
{
1, 2, 3
};
o.IEnumerableProperty = new List<int>
{
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<GenericListAndDictionaryInterfaceProperties>(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<SomeClass>(@"{""SomeProp"":1}");
string json = @"{
""firstName"": ""firstName"",
""FirstName"": ""FirstName"",
""LastName"": ""LastName"",
""lastName"": ""lastName"",
}";
PropertyCase o = JsonConvert.DeserializeObject<PropertyCase>(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<SubKlass>(json);
string newJson = JsonConvert.SerializeObject(ii);
Assert.AreEqual(@"{""SubProp"":""my subprop"",""SuperProp"":""overrided superprop""}", newJson);
}
}
}
+14 -1
View File
@@ -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()
{
@@ -58,6 +58,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Serialization\CamelCaseMappingResolverTests.cs" />
<Compile Include="JsonValidatingReaderTests.cs" />
<Compile Include="Schema\ExtensionsTests.cs" />
<Compile Include="Schema\JsonSchemaModelBuilderTests.cs" />
@@ -75,6 +76,7 @@
<Compile Include="TestObjects\DefaultValueAttributeTestClass.cs" />
<Compile Include="TestObjects\DoubleClass.cs" />
<Compile Include="TestObjects\GenericImpl.cs" />
<Compile Include="TestObjects\GenericListAndDictionaryInterfaceProperties.cs" />
<Compile Include="TestObjects\GetOnlyPropertyClass.cs" />
<Compile Include="TestObjects\IncompatibleJsonAttributeClass.cs" />
<Compile Include="TestObjects\Invoice.cs" />
@@ -86,6 +88,7 @@
<Compile Include="TestObjects\Name.cs" />
<Compile Include="TestObjects\PersonRaw.cs" />
<Compile Include="TestObjects\PhoneNumber.cs" />
<Compile Include="TestObjects\PrivateMembersClass.cs" />
<Compile Include="TestObjects\RequiredMembersClass.cs" />
<Compile Include="TestObjects\SetOnlyPropertyClass.cs" />
<Compile Include="TestObjects\Article.cs" />
@@ -63,6 +63,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Serialization\CamelCaseMappingResolverTests.cs" />
<Compile Include="JsonValidatingReaderTests.cs" />
<Compile Include="Schema\ExtensionsTests.cs" />
<Compile Include="Schema\JsonSchemaModelBuilderTests.cs" />
@@ -80,6 +81,7 @@
<Compile Include="TestObjects\DefaultValueAttributeTestClass.cs" />
<Compile Include="TestObjects\DoubleClass.cs" />
<Compile Include="TestObjects\GenericImpl.cs" />
<Compile Include="TestObjects\GenericListAndDictionaryInterfaceProperties.cs" />
<Compile Include="TestObjects\GetOnlyPropertyClass.cs" />
<Compile Include="TestObjects\IncompatibleJsonAttributeClass.cs" />
<Compile Include="TestObjects\Invoice.cs" />
@@ -91,6 +93,7 @@
<Compile Include="TestObjects\Name.cs" />
<Compile Include="TestObjects\PersonRaw.cs" />
<Compile Include="TestObjects\PhoneNumber.cs" />
<Compile Include="TestObjects\PrivateMembersClass.cs" />
<Compile Include="TestObjects\RequiredMembersClass.cs" />
<Compile Include="TestObjects\SetOnlyPropertyClass.cs" />
<Compile Include="TestObjects\Article.cs" />
@@ -81,6 +81,7 @@
<Compile Include="Schema\ExtensionsTests.cs" />
<Compile Include="Schema\JsonSchemaModelBuilderTests.cs" />
<Compile Include="Schema\JsonSchemaNodeTests.cs" />
<Compile Include="Serialization\CamelCaseMappingResolverTests.cs" />
<Compile Include="TestObjects\AbstractGenericBase.cs" />
<Compile Include="TestObjects\ArgumentConverterPrecedenceClassConverter.cs" />
<Compile Include="TestObjects\BadJsonPropertyClass.cs" />
@@ -94,6 +95,7 @@
<Compile Include="TestObjects\DefaultValueAttributeTestClass.cs" />
<Compile Include="TestObjects\DoubleClass.cs" />
<Compile Include="TestObjects\GenericImpl.cs" />
<Compile Include="TestObjects\GenericListAndDictionaryInterfaceProperties.cs" />
<Compile Include="TestObjects\GetOnlyPropertyClass.cs" />
<Compile Include="TestObjects\IncompatibleJsonAttributeClass.cs" />
<Compile Include="TestObjects\Invoice.cs" />
@@ -105,6 +107,7 @@
<Compile Include="TestObjects\Name.cs" />
<Compile Include="TestObjects\PersonRaw.cs" />
<Compile Include="TestObjects\PhoneNumber.cs" />
<Compile Include="TestObjects\PrivateMembersClass.cs" />
<Compile Include="TestObjects\RequiredMembersClass.cs" />
<Compile Include="TestObjects\SetOnlyPropertyClass.cs" />
<Compile Include="TestObjects\Article.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<string> errors = new List<string>();
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);
}
}
}
@@ -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<Person>(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<PrivateMembersClass>(@"{
""_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));
}
}
}
@@ -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<int> IEnumerableProperty { get; set; }
public IList<int> IListProperty { get; set; }
public IDictionary<string, int> IDictionaryProperty { get; set; }
}
}
@@ -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;
}
}
+13 -1
View File
@@ -467,12 +467,24 @@ namespace Newtonsoft.Json
/// <param name="converters">A collection converters used while serializing.</param>
/// <returns>A JSON string representation of the object.</returns>
public static string SerializeObject(object value, params JsonConverter[] converters)
{
return SerializeObject(value, Formatting.None, converters);
}
/// <summary>
/// Serializes the specified object to a JSON string using a collection of <see cref="JsonConverter"/>.
/// </summary>
/// <param name="value">The object to serialize.</param>
/// <param name="formatting">Indicates how the output is formatted.</param>
/// <param name="converters">A collection converters used while serializing.</param>
/// <returns>A JSON string representation of the object.</returns>
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);
}
/// <summary>
+197 -101
View File
@@ -53,6 +53,7 @@ namespace Newtonsoft.Json
private DefaultValueHandling _defaultValueHandling;
private int _level;
private JsonConverterCollection _converters;
private IMappingResolver _mappingResolver;
/// <summary>
/// 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
/// <summary>
@@ -170,7 +177,7 @@ namespace Newtonsoft.Json
if (settings != null)
{
if (!CollectionUtils.IsNullOrEmpty<JsonConverter>(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<JsonMemberMapping, object> 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<ParameterInfo, object> 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<ParameterInfo, object> constructorParameters = c.GetParameters().ToDictionary(p => p, p => (object)null);
IDictionary<JsonMemberMapping, object> remainingMappingValues = new Dictionary<JsonMemberMapping, object>();
foreach (KeyValuePair<JsonMemberMapping, object> 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<JsonMemberMapping, object> 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<string, bool> requiredMappings =
memberMappings.Where(m => m.Required).ToDictionary(m => m.MappingName, m => false);
//List<RequiredMemberContext> 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<string, bool> requiredMappings)
{
// first attempt to find exact case match
// then attempt case insensitive match
if (requiredMappings.ContainsKey(memberName))
{
requiredMappings[memberName] = true;
}
else
{
foreach (KeyValuePair<string, bool> requiredMapping in requiredMappings)
{
if (string.Compare(requiredMapping.Key, requiredMapping.Key, StringComparison.OrdinalIgnoreCase) == 0)
{
requiredMappings[requiredMapping.Key] = true;
break;
}
}
}
}
#endregion
#region Serialize
@@ -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
/// </summary>
/// <value>The converters.</value>
public IList<JsonConverter> Converters { get; set; }
public IMappingResolver MappingResolver { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="JsonSerializerSettings"/> class.
+5 -4
View File
@@ -339,20 +339,21 @@ namespace Newtonsoft.Json.Linq
/// </returns>
public override string ToString()
{
return ToString(null);
return ToString(Formatting.Indented);
}
/// <summary>
/// Returns the indented JSON for this token using any given converters.
/// Returns the JSON for this token using the given formatting and converters.
/// </summary>
/// <param name="formatting">Indicates how the output is formatted.</param>
/// <param name="converters">A collection of <see cref="JsonConverter"/> which will be used when writing the token.</param>
/// <returns></returns>
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);
@@ -108,6 +108,9 @@
<Compile Include="Schema\UndefinedSchemaIdHandling.cs" />
<Compile Include="Schema\ValidationEventArgs.cs" />
<Compile Include="Schema\ValidationEventHandler.cs" />
<Compile Include="Serialization\CamelCaseMappingResolver.cs" />
<Compile Include="Serialization\DefaultMappingResolver.cs" />
<Compile Include="Serialization\IMappingResolver.cs" />
<Compile Include="Serialization\JsonMemberMapping.cs" />
<Compile Include="Serialization\JsonMemberMappingCollection.cs" />
<Compile Include="MissingMemberHandling.cs" />
@@ -97,6 +97,9 @@
<Compile Include="ObjectCreationHandling.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ReferenceLoopHandling.cs" />
<Compile Include="Serialization\CamelCaseMappingResolver.cs" />
<Compile Include="Serialization\DefaultMappingResolver.cs" />
<Compile Include="Serialization\IMappingResolver.cs" />
<Compile Include="StringBuffer.cs" />
<Compile Include="Utilities\CollectionUtils.cs" />
<Compile Include="Utilities\CollectionWrapper.cs" />
@@ -126,6 +126,9 @@
<Compile Include="Schema\UndefinedSchemaIdHandling.cs" />
<Compile Include="Schema\ValidationEventArgs.cs" />
<Compile Include="Schema\ValidationEventHandler.cs" />
<Compile Include="Serialization\CamelCaseMappingResolver.cs" />
<Compile Include="Serialization\DefaultMappingResolver.cs" />
<Compile Include="Serialization\IMappingResolver.cs" />
<Compile Include="Serialization\JsonMemberMapping.cs" />
<Compile Include="Serialization\JsonMemberMappingCollection.cs" />
<Compile Include="MissingMemberHandling.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<string, JsonSchema>();
foreach (JsonMemberMapping mapping in mappings)
@@ -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;
}
}
}
@@ -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<Type, JsonMemberMappingCollection> TypeMemberMappingsCache = new Dictionary<Type, JsonMemberMappingCollection>();
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<MemberInfo> GetSerializableMembers(Type objectType)
{
return ReflectionUtils.GetFieldsAndProperties(objectType, MemberSearchFlags);
}
private JsonMemberMappingCollection CreateMemberMappings(Type objectType)
{
MemberSerialization memberSerialization = JsonTypeReflector.GetObjectMemberSerialization(objectType);
List<MemberInfo> 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<JsonPropertyAttribute>(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<DefaultValueAttribute>(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;
}
}
}
@@ -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
{
/// <summary>
/// Resolves member mappings for a type.
/// </summary>
public interface IMappingResolver
{
/// <summary>
/// Returns a collection of <see cref="JsonMemberMapping"/> for the specified <see cref="Type"/>
/// to be used during serialization.</see>
/// </summary>
/// <param name="type">The type to be resolved.</param>
/// <returns>A collection of <see cref="JsonMemberMapping"/> to be used during serialization.</returns>
JsonMemberMappingCollection ResolveMappings(Type type);
}
}
@@ -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;
}
}
}
@@ -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<ICustomAttributeProvider, Type> ConverterTypeCache = new Dictionary<ICustomAttributeProvider, Type>();
private static readonly Dictionary<Type, JsonMemberMappingCollection> TypeMemberMappingsCache = new Dictionary<Type, JsonMemberMappingCollection>();
private static readonly Dictionary<Type, JsonContainerAttribute> TypeContainerAttributeCache = new Dictionary<Type, JsonContainerAttribute>();
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<MemberInfo> 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<JsonPropertyAttribute>(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<DefaultValueAttribute>(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<MemberInfo> GetSerializableMembers(Type objectType)
{
return ReflectionUtils.GetFieldsAndProperties(objectType, BindingFlags.Public | BindingFlags.Instance);
}
}
}
}
@@ -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");
@@ -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));