diff --git a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs index 410d4764..47fd0df9 100644 --- a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs +++ b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs @@ -36,6 +36,8 @@ using Newtonsoft.Json.Schema; using System.IO; using System.Linq; using Newtonsoft.Json.Linq; +using System.Text; +using Extensions=Newtonsoft.Json.Schema.Extensions; namespace Newtonsoft.Json.Tests.Schema { @@ -52,12 +54,26 @@ namespace Newtonsoft.Json.Tests.Schema Assert.AreEqual(@"{ ""type"": ""object"", ""additionalProperties"": { - ""type"": ""array"", + ""type"": [ + ""array"", + ""null"" + ], ""items"": { ""type"": ""string"" } } }", json); + + Dictionary> value = new Dictionary> + { + {"HasValue", new List() { "first", "second"}}, + {"NoValue", null} + }; + + string valueJson = JsonConvert.SerializeObject(value, Formatting.Indented); + JObject o = JObject.Parse(valueJson); + + Assert.IsTrue(o.IsValid(schema)); } #if !PocketPC @@ -279,10 +295,49 @@ namespace Newtonsoft.Json.Tests.Schema JsonSchema schema = generator.Generate(typeof(Type)); Assert.AreEqual(JsonSchemaType.String, schema.Type); + + string json = JsonConvert.SerializeObject(typeof(Version), Formatting.Indented); + + JValue v = new JValue(json); + Assert.IsTrue(v.IsValid(schema)); + } + +#if !SILVERLIGHT && !PocketPC + [Test] + public void GenerateSchemaForISerializable() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + + JsonSchema schema = generator.Generate(typeof(Exception)); + + Assert.AreEqual(JsonSchemaType.Object, schema.Type); + Assert.AreEqual(true, schema.AllowAdditionalProperties); + Assert.AreEqual(null, schema.Properties); + } +#endif + + [Test] + public void GenerateSchemaForDBNull() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + + JsonSchema schema = generator.Generate(typeof(DBNull)); + + Assert.AreEqual(JsonSchemaType.Null, schema.Type); } public class CustomDirectoryInfoMapper : DefaultContractResolver { + protected override JsonContract CreateContract(Type objectType) + { + if (objectType == typeof(DirectoryInfo)) + return base.CreateObjectContract(objectType); + + return base.CreateContract(objectType); + } + protected override IList CreateProperties(JsonObjectContract contract) { IList properties = base.CreateProperties(contract); @@ -299,6 +354,7 @@ namespace Newtonsoft.Json.Tests.Schema { JsonSchemaGenerator generator = new JsonSchemaGenerator(); generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + generator.ContractResolver = new CustomDirectoryInfoMapper(); JsonSchema schema = generator.Generate(typeof(DirectoryInfo), true); @@ -324,9 +380,6 @@ namespace Newtonsoft.Json.Tests.Schema ""Exists"": { ""type"": ""boolean"" }, - ""Root"": { - ""$ref"": ""System.IO.DirectoryInfo"" - }, ""FullName"": { ""type"": [ ""string"", @@ -374,9 +427,7 @@ namespace Newtonsoft.Json.Tests.Schema List errors = new List(); jsonWriter.Token.Validate(schema, (sender, args) => errors.Add(args.Message)); - Assert.AreEqual(2, errors.Count); - Assert.AreEqual("Non-optional properties are missing from object: Root.", errors[0]); - Assert.AreEqual("Non-optional properties are missing from object: Root.", errors[1]); + Assert.AreEqual(0, errors.Count); } [Test] @@ -386,64 +437,34 @@ namespace Newtonsoft.Json.Tests.Schema generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; generator.ContractResolver = new CamelCasePropertyNamesContractResolver(); - JsonSchema schema = generator.Generate(typeof (DirectoryInfo), true); + JsonSchema schema = generator.Generate(typeof (Version), true); string json = schema.ToString(); Assert.AreEqual(@"{ - ""id"": ""System.IO.DirectoryInfo"", + ""id"": ""System.Version"", ""type"": [ ""object"", ""null"" ], ""additionalProperties"": false, ""properties"": { - ""name"": { - ""type"": [ - ""string"", - ""null"" - ] + ""major"": { + ""type"": ""integer"" }, - ""parent"": { - ""$ref"": ""System.IO.DirectoryInfo"" + ""minor"": { + ""type"": ""integer"" }, - ""exists"": { - ""type"": ""boolean"" + ""build"": { + ""type"": ""integer"" }, - ""root"": { - ""$ref"": ""System.IO.DirectoryInfo"" + ""revision"": { + ""type"": ""integer"" }, - ""fullName"": { - ""type"": [ - ""string"", - ""null"" - ] + ""majorRevision"": { + ""type"": ""integer"" }, - ""extension"": { - ""type"": [ - ""string"", - ""null"" - ] - }, - ""creationTime"": { - ""type"": ""string"" - }, - ""creationTimeUtc"": { - ""type"": ""string"" - }, - ""lastAccessTime"": { - ""type"": ""string"" - }, - ""lastAccessTimeUtc"": { - ""type"": ""string"" - }, - ""lastWriteTime"": { - ""type"": ""string"" - }, - ""lastWriteTimeUtc"": { - ""type"": ""string"" - }, - ""attributes"": { + ""minorRevision"": { ""type"": ""integer"" } } diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 96045e07..d0f5e7f5 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -1981,14 +1981,31 @@ keyword such as type of business."" } [Test] - [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Could not create string property name from type 'Newtonsoft.Json.Tests.TestObjects.Person'.")] public void SerializePersonKeyedDictionary() { Dictionary dictionary = new Dictionary(); dictionary.Add(new Person { Name = "p1" }, 1); dictionary.Add(new Person { Name = "p2" }, 2); - JsonConvert.SerializeObject(dictionary, Formatting.Indented); + string json = JsonConvert.SerializeObject(dictionary, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Newtonsoft.Json.Tests.TestObjects.Person"": 1, + ""Newtonsoft.Json.Tests.TestObjects.Person"": 2 +}", json); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Could not convert string 'Newtonsoft.Json.Tests.TestObjects.Person' to dictionary key type Newtonsoft.Json.Tests.TestObjects.Person. Create a TypeConverter to convert from the string to the key type object.")] + public void DeserializePersonKeyedDictionary() + { + string json = + @"{ + ""Newtonsoft.Json.Tests.TestObjects.Person"": 1, + ""Newtonsoft.Json.Tests.TestObjects.Person"": 2 +}"; + + JsonConvert.DeserializeObject>(json); } [Test] @@ -2314,5 +2331,288 @@ keyword such as type of business."" Assert.AreEqual(123, item.Value); } + + public abstract class Animal + { + public abstract string Name { get; } + } + + public class Human : Animal + { + public override string Name + { + get { return typeof(Human).Name; } + } + + public string Ethnicity { get; set; } + } + +#if !NET20 && !PocketPC + public class DataContractJsonSerializerTestClass + { + public TimeSpan TimeSpanProperty { get; set; } + public Guid GuidProperty { get; set; } + public Animal AnimalProperty { get; set; } + public Exception ExceptionProperty { get; set; } + } + + [Test] + public void DataContractJsonSerializerTest() + { + Exception ex = new Exception("Blah blah blah"); + + DataContractJsonSerializerTestClass c = new DataContractJsonSerializerTestClass(); + c.TimeSpanProperty = new TimeSpan(200, 20, 59, 30, 900); + c.GuidProperty = new Guid("66143115-BE2A-4a59-AF0A-348E1EA15B1E"); + c.AnimalProperty = new Human() {Ethnicity = "European"}; + c.ExceptionProperty = ex; + + MemoryStream ms = new MemoryStream(); + DataContractJsonSerializer serializer = new DataContractJsonSerializer( + typeof(DataContractJsonSerializerTestClass), + new Type[] { typeof(Human) }); + serializer.WriteObject(ms, c); + + byte[] jsonBytes = ms.ToArray(); + string json = Encoding.UTF8.GetString(jsonBytes, 0, jsonBytes.Length); + + Console.WriteLine(JObject.Parse(json).ToString()); + Console.WriteLine(); + + Console.WriteLine(JsonConvert.SerializeObject(c, Formatting.Indented, new JsonSerializerSettings + { + // TypeNameHandling = TypeNameHandling.Objects + })); + } +#endif + + public class ModelStateDictionary : IDictionary + { + + private readonly Dictionary _innerDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public ModelStateDictionary() + { + } + + public ModelStateDictionary(ModelStateDictionary dictionary) + { + if (dictionary == null) + { + throw new ArgumentNullException("dictionary"); + } + + foreach (var entry in dictionary) + { + _innerDictionary.Add(entry.Key, entry.Value); + } + } + + public int Count + { + get + { + return _innerDictionary.Count; + } + } + + public bool IsReadOnly + { + get + { + return ((IDictionary)_innerDictionary).IsReadOnly; + } + } + + public ICollection Keys + { + get + { + return _innerDictionary.Keys; + } + } + + public T this[string key] + { + get + { + T value; + _innerDictionary.TryGetValue(key, out value); + return value; + } + set + { + _innerDictionary[key] = value; + } + } + + public ICollection Values + { + get + { + return _innerDictionary.Values; + } + } + + public void Add(KeyValuePair item) + { + ((IDictionary)_innerDictionary).Add(item); + } + + public void Add(string key, T value) + { + _innerDictionary.Add(key, value); + } + + public void Clear() + { + _innerDictionary.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return ((IDictionary)_innerDictionary).Contains(item); + } + + public bool ContainsKey(string key) + { + return _innerDictionary.ContainsKey(key); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((IDictionary)_innerDictionary).CopyTo(array, arrayIndex); + } + + public IEnumerator> GetEnumerator() + { + return _innerDictionary.GetEnumerator(); + } + + public void Merge(ModelStateDictionary dictionary) + { + if (dictionary == null) + { + return; + } + + foreach (var entry in dictionary) + { + this[entry.Key] = entry.Value; + } + } + + public bool Remove(KeyValuePair item) + { + return ((IDictionary)_innerDictionary).Remove(item); + } + + public bool Remove(string key) + { + return _innerDictionary.Remove(key); + } + + public bool TryGetValue(string key, out T value) + { + return _innerDictionary.TryGetValue(key, out value); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_innerDictionary).GetEnumerator(); + } + } + + [Test] + public void SerializeNonIDictionary() + { + ModelStateDictionary modelStateDictionary = new ModelStateDictionary(); + modelStateDictionary.Add("key", "value"); + + string json = JsonConvert.SerializeObject(modelStateDictionary); + + Assert.AreEqual(@"{""key"":""value""}", json); + + ModelStateDictionary newModelStateDictionary = JsonConvert.DeserializeObject>(json); + Assert.AreEqual(1, newModelStateDictionary.Count); + Assert.AreEqual("value", newModelStateDictionary["key"]); + } + +#if !SILVERLIGHT && !PocketPC + public class ISerializableTestObject : ISerializable + { + internal string _stringValue; + internal int _intValue; + internal DateTimeOffset _dateTimeOffsetValue; + internal Person _personValue; + internal Person _nullPersonValue; + + public ISerializableTestObject(string stringValue, int intValue, DateTimeOffset dateTimeOffset, Person personValue) + { + _stringValue = stringValue; + _intValue = intValue; + _dateTimeOffsetValue = dateTimeOffset; + _personValue = personValue; + } + + protected ISerializableTestObject(SerializationInfo info, StreamingContext context) + { + _stringValue = info.GetString("stringValue"); + _intValue = info.GetInt32("intValue"); + _dateTimeOffsetValue = (DateTimeOffset)info.GetValue("dateTimeOffsetValue", typeof(DateTimeOffset)); + _personValue = (Person)info.GetValue("personValue", typeof(Person)); + _nullPersonValue = (Person)info.GetValue("nullPersonValue", typeof(Person)); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("stringValue", _stringValue); + info.AddValue("intValue", _intValue); + info.AddValue("dateTimeOffsetValue", _dateTimeOffsetValue); + info.AddValue("personValue", _personValue); + info.AddValue("nullPersonValue", _nullPersonValue); + } + } + + [Test] + public void SerializeISerializableTestObject() + { + Person person = new Person(); + person.BirthDate = new DateTime(2000, 1, 1, 1, 1, 1, DateTimeKind.Utc); + person.LastModified = person.BirthDate; + person.Department = "Department!"; + person.Name = "Name!"; + + DateTimeOffset dateTimeOffset = new DateTimeOffset(2000, 12, 20, 22, 59, 59, TimeSpan.FromHours(2)); + string dateTimeOffsetText; +#if !NET20 + dateTimeOffsetText = @"\/Date(977345999000+0200)\/"; +#else + dateTimeOffsetText = @"12/20/2000 22:59:59 +02:00"; +#endif + + ISerializableTestObject o = new ISerializableTestObject("String!", int.MinValue, dateTimeOffset, person); + + string json = JsonConvert.SerializeObject(o, Formatting.Indented); + Assert.AreEqual(@"{ + ""stringValue"": ""String!"", + ""intValue"": -2147483648, + ""dateTimeOffsetValue"": """ + dateTimeOffsetText + @""", + ""personValue"": { + ""Name"": ""Name!"", + ""BirthDate"": ""\/Date(946688461000)\/"", + ""LastModified"": ""\/Date(946688461000)\/"" + }, + ""nullPersonValue"": null +}", json); + + ISerializableTestObject o2 = JsonConvert.DeserializeObject(json); + Assert.AreEqual("String!", o2._stringValue); + Assert.AreEqual(int.MinValue, o2._intValue); + Assert.AreEqual(dateTimeOffset, o2._dateTimeOffsetValue); + Assert.AreEqual("Name!", o2._personValue.Name); + Assert.AreEqual(null, o2._nullPersonValue); + } +#endif } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 01678d84..45b6a533 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -472,6 +472,9 @@ namespace Newtonsoft.Json internal static bool IsJsonPrimitiveType(Type type) { + if (ReflectionUtils.IsNullableType(type)) + type = Nullable.GetUnderlyingType(type); + #if !PocketPC && !NET20 if (type == typeof(DateTimeOffset)) return true; diff --git a/Src/Newtonsoft.Json/JsonValidatingReader.cs b/Src/Newtonsoft.Json/JsonValidatingReader.cs index 3498bbd1..23a1cd4f 100644 --- a/Src/Newtonsoft.Json/JsonValidatingReader.cs +++ b/Src/Newtonsoft.Json/JsonValidatingReader.cs @@ -70,9 +70,9 @@ namespace Newtonsoft.Json _schema = schema; if (_schema != null && _schema.Properties != null) - { _requiredProperties = GetRequiredProperties(_schema).Distinct().ToDictionary(p => p, p => false); - } + else + _requiredProperties = new Dictionary(); } private IEnumerable GetRequiredProperties(JsonSchemaModel schema) @@ -178,7 +178,7 @@ namespace Newtonsoft.Json throw new Exception("CurrentPropertyName has not been set on scope."); JsonSchemaModel propertySchema; - if (CurrentSchema.Properties.TryGetValue(_currentScope.CurrentPropertyName, out propertySchema)) + if (CurrentSchema.Properties != null && CurrentSchema.Properties.TryGetValue(_currentScope.CurrentPropertyName, out propertySchema)) return propertySchema; return (CurrentSchema.AllowAdditionalProperties) ? CurrentSchema.AdditionalProperties : null; @@ -552,7 +552,7 @@ namespace Newtonsoft.Json if (_currentScope.RequiredProperties.ContainsKey(propertyName)) _currentScope.RequiredProperties[propertyName] = true; - if (!schema.Properties.ContainsKey(propertyName)) + if (schema.Properties != null && !schema.Properties.ContainsKey(propertyName)) { IList definedProperties = schema.Properties.Select(p => p.Key).ToList(); diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj index d974e421..a4cbca53 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Compact.csproj @@ -151,6 +151,8 @@ + + @@ -160,7 +162,9 @@ + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj index 60a0dac3..d01d2488 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -160,6 +160,8 @@ + + @@ -178,8 +180,10 @@ + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index b6868ddf..3526ee86 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -135,6 +135,8 @@ + + @@ -144,6 +146,8 @@ + + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 9ab83edf..6bd17992 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -92,6 +92,8 @@ + + @@ -178,6 +180,7 @@ + @@ -185,6 +188,7 @@ + diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs index 44010c13..f7a903b4 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs @@ -233,10 +233,11 @@ namespace Newtonsoft.Json.Schema CurrentSchema.Title = GetTitle(type); CurrentSchema.Description = GetDescription(type); - if (CollectionUtils.IsDictionaryType(type)) + JsonContract contract = ContractResolver.ResolveContract(type); + + if (contract is JsonDictionaryContract) { - // TODO: include null - CurrentSchema.Type = JsonSchemaType.Object; + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); Type keyType; Type valueType; @@ -251,10 +252,9 @@ namespace Newtonsoft.Json.Schema } } } - else if (CollectionUtils.IsCollectionType(type)) + else if (contract is JsonArrayContract) { - // TODO: include null - CurrentSchema.Type = JsonSchemaType.Array; + CurrentSchema.Type = AddNullType(JsonSchemaType.Array, valueRequired); CurrentSchema.Id = GetTypeId(type, false); @@ -268,37 +268,11 @@ namespace Newtonsoft.Json.Schema CurrentSchema.Items.Add(GenerateInternal(collectionItemType, (!allowNullItem) ? Required.Always : Required.Default)); } } - else + else if (contract is JsonPrimitiveContract) { CurrentSchema.Type = GetJsonSchemaType(type, valueRequired); - if (HasFlag(CurrentSchema.Type, JsonSchemaType.Object)) - { - CurrentSchema.Id = GetTypeId(type, false); - - JsonObjectContract contract = ContractResolver.ResolveContract(type) as JsonObjectContract; - - if (contract == null) - throw new Exception("Could not resolve contract for '{0}'.".FormatWith(CultureInfo.InvariantCulture, type)); - - CurrentSchema.Properties = new Dictionary(); - foreach (JsonProperty property in contract.Properties) - { - if (!property.Ignored) - { - JsonSchema propertySchema = GenerateInternal(property.PropertyType, property.Required); - - if (property.DefaultValue != null) - propertySchema.Default = JToken.FromObject(property.DefaultValue); - - CurrentSchema.Properties.Add(property.PropertyName, propertySchema); - } - } - - if (type.IsSealed) - CurrentSchema.AllowAdditionalProperties = false; - } - else if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum && !type.IsDefined(typeof(FlagsAttribute), true)) + if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum && !type.IsDefined(typeof(FlagsAttribute), true)) { CurrentSchema.Enum = new List(); CurrentSchema.Options = new Dictionary(); @@ -313,10 +287,71 @@ namespace Newtonsoft.Json.Schema } } } + else if (contract is JsonObjectContract) + { + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); + CurrentSchema.Id = GetTypeId(type, false); + GenerateObjectSchema(type, (JsonObjectContract)contract); + } +#if !SILVERLIGHT && !PocketPC + else if (contract is JsonISerializableContract) + { + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); + CurrentSchema.Id = GetTypeId(type, false); + GenerateISerializableContract(type, (JsonISerializableContract) contract); + } +#endif + else if (contract is JsonStringContract) + { + JsonSchemaType schemaType = (!ReflectionUtils.IsNullable(contract.UnderlyingType)) + ? JsonSchemaType.String + : AddNullType(JsonSchemaType.String, valueRequired); + + CurrentSchema.Type = schemaType; + } + else + { + throw new Exception("Unexpected contract type: {0}".FormatWith(CultureInfo.InvariantCulture, contract)); + } return Pop().Schema; } + private JsonSchemaType AddNullType(JsonSchemaType type, Required valueRequired) + { + if (valueRequired != Required.Always) + return type | JsonSchemaType.Null; + + return type; + } + + private void GenerateObjectSchema(Type type, JsonObjectContract contract) + { + CurrentSchema.Properties = new Dictionary(); + foreach (JsonProperty property in contract.Properties) + { + if (!property.Ignored) + { + JsonSchema propertySchema = GenerateInternal(property.PropertyType, property.Required); + + if (property.DefaultValue != null) + propertySchema.Default = JToken.FromObject(property.DefaultValue); + + CurrentSchema.Properties.Add(property.PropertyName, propertySchema); + } + } + + if (type.IsSealed) + CurrentSchema.AllowAdditionalProperties = false; + } + +#if !SILVERLIGHT && !PocketPC + private void GenerateISerializableContract(Type type, JsonISerializableContract contract) + { + CurrentSchema.AllowAdditionalProperties = true; + } +#endif + internal static bool HasFlag(JsonSchemaType? value, JsonSchemaType flag) { // default value is Any diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index efd251e2..95c1f117 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -315,33 +315,90 @@ namespace Newtonsoft.Json.Serialization return contract; } - private JsonContract CreateContract(Type objectType) +#if !SILVERLIGHT && !PocketPC + protected virtual JsonISerializableContract CreateISerializableContract(Type objectType) + { + JsonISerializableContract contract = new JsonISerializableContract(objectType); + InitializeContract(contract); + + ConstructorInfo constructorInfo = objectType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new [] {typeof (SerializationInfo), typeof (StreamingContext)}, null); + if (constructorInfo != null) + contract.ISerializableCreator = LateBoundDelegateFactory.CreateMethodHandler(constructorInfo); + + return contract; + } +#endif + + protected virtual JsonStringContract CreateStringContract(Type objectType) + { + JsonStringContract contract = new JsonStringContract(objectType); + InitializeContract(contract); + + return contract; + } + + protected virtual JsonContract CreateContract(Type objectType) { if (JsonConvert.IsJsonPrimitiveType(objectType)) - { return CreatePrimitiveContract(objectType); - } + if (JsonTypeReflector.GetJsonObjectAttribute(objectType) != null) - { return CreateObjectContract(objectType); - } + + if (JsonTypeReflector.GetJsonArrayAttribute(objectType) != null) + return CreateArrayContract(objectType); + if (objectType.IsSubclassOf(typeof(JToken))) - { return CreateLinqContract(objectType); - } + + bool canConvertToString = CanConvertToString(objectType); + +#if !SILVERLIGHT && !PocketPC + if (typeof(ISerializable).IsAssignableFrom(objectType) && !canConvertToString) + return CreateISerializableContract(objectType); +#endif if (CollectionUtils.IsDictionaryType(objectType)) - { return CreateDictionaryContract(objectType); - } + if (typeof(IEnumerable).IsAssignableFrom(objectType)) - { return CreateArrayContract(objectType); - } + + if (canConvertToString) + return CreateStringContract(objectType); return CreateObjectContract(objectType); } + internal static bool CanConvertToString(Type type) + { +#if !PocketPC + TypeConverter converter = ConvertUtils.GetConverter(type); + + // use the objectType's TypeConverter if it has one and can convert to a string + if (converter != null +#if !SILVERLIGHT + && !(converter is ComponentConverter) + && !(converter is ReferenceConverter) +#endif + && converter.GetType() != typeof(TypeConverter)) + { + if (converter.CanConvertTo(typeof(string))) + return true; + } +#endif + + if (type == typeof(Type) || type.IsSubclassOf(typeof(Type))) + return true; + +#if SILVERLIGHT || PocketPC + if (type == typeof(Guid) || type == typeof(Uri) || type == typeof(TimeSpan)) + return true; +#endif + + return false; + } + private static bool IsValidCallback(MethodInfo method, ParameterInfo[] parameters, Type attributeType, MethodInfo currentCallback, ref Type prevAttributeType) { if (!method.IsDefined(attributeType, false)) diff --git a/Src/Newtonsoft.Json/Serialization/JsonFormatterConverter.cs b/Src/Newtonsoft.Json/Serialization/JsonFormatterConverter.cs new file mode 100644 index 00000000..36b25633 --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/JsonFormatterConverter.cs @@ -0,0 +1,154 @@ +#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 + +#if !SILVERLIGHT && !PocketPC +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Serialization +{ + internal class JsonFormatterConverter : IFormatterConverter + { + private readonly JsonSerializer _serializer; + + public JsonFormatterConverter(JsonSerializer serializer) + { + ValidationUtils.ArgumentNotNull(serializer, "serializer"); + + _serializer = serializer; + } + + private T GetTokenValue(object value) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + JValue v = (JValue)value; + return (T)System.Convert.ChangeType(v.Value, typeof(T), CultureInfo.InvariantCulture); + } + + public object Convert(object value, Type type) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + JToken token = value as JToken; + if (token == null) + throw new ArgumentException("Value is not a JToken.", "value"); + + return _serializer.Deserialize(token.CreateReader(), type); + } + + public object Convert(object value, TypeCode typeCode) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + if (value is JValue) + value = ((JValue) value).Value; + + return System.Convert.ChangeType(value, typeCode, CultureInfo.InvariantCulture); + } + + public bool ToBoolean(object value) + { + return GetTokenValue(value); + } + + public byte ToByte(object value) + { + return GetTokenValue(value); + } + + public char ToChar(object value) + { + return GetTokenValue(value); + } + + public DateTime ToDateTime(object value) + { + return GetTokenValue(value); + } + + public decimal ToDecimal(object value) + { + return GetTokenValue(value); + } + + public double ToDouble(object value) + { + return GetTokenValue(value); + } + + public short ToInt16(object value) + { + return GetTokenValue(value); + } + + public int ToInt32(object value) + { + return GetTokenValue(value); + } + + public long ToInt64(object value) + { + return GetTokenValue(value); + } + + public sbyte ToSByte(object value) + { + return GetTokenValue(value); + } + + public float ToSingle(object value) + { + return GetTokenValue(value); + } + + public string ToString(object value) + { + return GetTokenValue(value); + } + + public ushort ToUInt16(object value) + { + return GetTokenValue(value); + } + + public uint ToUInt32(object value) + { + return GetTokenValue(value); + } + + public ulong ToUInt64(object value) + { + return GetTokenValue(value); + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs b/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs new file mode 100644 index 00000000..aa12accc --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs @@ -0,0 +1,53 @@ +#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 + +#if !SILVERLIGHT && !PocketPC +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonISerializableContract : JsonContract + { + public MemberHandler ISerializableCreator { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonISerializableContract(Type underlyingType) + : base(underlyingType) + { + CreatedType = underlyingType; + } + } +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index 47b4038f..33672f8f 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -39,6 +39,9 @@ namespace Newtonsoft.Json.Serialization internal class JsonSerializerInternalReader : JsonSerializerInternalBase { private JsonSerializerProxy _internalSerializer; +#if !SILVERLIGHT && !PocketPC + private JsonFormatterConverter _formatterConverter; +#endif public JsonSerializerInternalReader(JsonSerializer serializer) : base(serializer) { @@ -122,6 +125,16 @@ namespace Newtonsoft.Json.Serialization return _internalSerializer; } +#if !SILVERLIGHT && !PocketPC + private JsonFormatterConverter GetFormatterConverter() + { + if (_formatterConverter == null) + _formatterConverter = new JsonFormatterConverter(GetInternalSerializer()); + + return _formatterConverter; + } +#endif + private JToken CreateJToken(JsonReader reader, JsonContract contract) { ValidationUtils.ArgumentNotNull(reader, "reader"); @@ -320,21 +333,31 @@ namespace Newtonsoft.Json.Serialization if (contract == null) throw new JsonSerializationException("Could not resolve type '{0}' to a JsonContract.".FormatWith(CultureInfo.InvariantCulture, objectType)); - if (contract is JsonDictionaryContract) + JsonDictionaryContract dictionaryContract = contract as JsonDictionaryContract; + if (dictionaryContract != null) { if (existingValue == null) - return CreateAndPopulateDictionary(reader, (JsonDictionaryContract) contract, id); + return CreateAndPopulateDictionary(reader, dictionaryContract, id); - return PopulateDictionary(CollectionUtils.CreateDictionaryWrapper(existingValue), reader, (JsonDictionaryContract) contract, id); + return PopulateDictionary(dictionaryContract.CreateWrapper(existingValue), reader, dictionaryContract, id); } - - if (contract is JsonObjectContract) + + JsonObjectContract objectContract = contract as JsonObjectContract; + if (objectContract != null) { if (existingValue == null) - return CreateAndPopulateObject(reader, (JsonObjectContract) contract, id); - - return PopulateObject(existingValue, reader, (JsonObjectContract) contract, id); + return CreateAndPopulateObject(reader, objectContract, id); + + return PopulateObject(existingValue, reader, objectContract, id); } + +#if !SILVERLIGHT && !PocketPC + JsonISerializableContract serializableContract = contract as JsonISerializableContract; + if (serializableContract != null) + { + return CreateISerializable(reader, serializableContract, id); + } +#endif throw new JsonSerializationException("Cannot deserialize JSON object into type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); } @@ -367,7 +390,7 @@ namespace Newtonsoft.Json.Serialization if (existingValue == null) value = CreateAndPopulateList(reader, reference, arrayContract); else - value = PopulateList(CollectionUtils.CreateCollectionWrapper(existingValue), reader, reference, arrayContract); + value = PopulateList(arrayContract.CreateWrapper(existingValue), reader, reference, arrayContract); } else { @@ -501,7 +524,16 @@ namespace Newtonsoft.Json.Serialization switch (reader.TokenType) { case JsonToken.PropertyName: - object keyValue = EnsureType(reader.Value, contract.DictionaryKeyType); + object keyValue; + try + { + keyValue = EnsureType(reader.Value, contract.DictionaryKeyType); + } + catch (Exception ex) + { + throw new JsonSerializationException("Could not convert string '{0}' to dictionary key type {1}. Create a TypeConverter to convert from the string to the key type object.".FormatWith(CultureInfo.InvariantCulture, reader.Value, contract.DictionaryKeyType), ex); + } + CheckedRead(reader); try @@ -588,6 +620,48 @@ namespace Newtonsoft.Json.Serialization throw new JsonSerializationException("Unexpected end when deserializing array."); } +#if !SILVERLIGHT && !PocketPC + private object CreateISerializable(JsonReader reader, JsonISerializableContract contract, string id) + { + Type objectType = contract.UnderlyingType; + + SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, GetFormatterConverter()); + + bool exit = false; + do + { + 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)); + + serializationInfo.AddValue(memberName, JToken.ReadFrom(reader)); + break; + case JsonToken.EndObject: + exit = true; + break; + default: + throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); + } + } while (!exit && reader.Read()); + + if (contract.ISerializableCreator == null) + throw new JsonSerializationException("ISerializable type '{0}' does not have a valid constructor.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + object createdObject = contract.ISerializableCreator(null, new object[] { serializationInfo, Serializer.Context }); + + if (id != null) + Serializer.ReferenceResolver.AddReference(id, createdObject); + + contract.InvokeOnDeserializing(createdObject, Serializer.Context); + contract.InvokeOnDeserialized(createdObject, Serializer.Context); + + return createdObject; + } +#endif + private object CreateAndPopulateObject(JsonReader reader, JsonObjectContract contract, string id) { object newObject = null; diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 1898cd5b..59d0517c 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -101,9 +101,9 @@ namespace Newtonsoft.Json.Serialization { writer.WriteValue(value); } - else if (value is JToken) + else if (contract is JsonStringContract) { - ((JToken) value).WriteTo(writer, (Serializer.Converters != null) ? Serializer.Converters.ToArray() : null); + SerializeString(writer, value, (JsonStringContract) contract); } else if (contract is JsonObjectContract) { @@ -111,7 +111,8 @@ namespace Newtonsoft.Json.Serialization } else if (contract is JsonDictionaryContract) { - SerializeDictionary(writer, (IDictionary) value, (JsonDictionaryContract) contract); + JsonDictionaryContract dictionaryContract = (JsonDictionaryContract) contract; + SerializeDictionary(writer, dictionaryContract.CreateWrapper(value), dictionaryContract); } else if (contract is JsonArrayContract) { @@ -130,6 +131,16 @@ namespace Newtonsoft.Json.Serialization CultureInfo.InvariantCulture, value.GetType())); } } + else if (contract is JsonLinqContract) + { + ((JToken)value).WriteTo(writer, (Serializer.Converters != null) ? Serializer.Converters.ToArray() : null); + } +#if !SILVERLIGHT && !PocketPC + else if (contract is JsonISerializableContract) + { + SerializeISerializable(writer, (ISerializable) value, (JsonISerializableContract) contract); + } +#endif } private bool ShouldWriteReference(object value, JsonProperty property, JsonContract contract) @@ -264,18 +275,20 @@ namespace Newtonsoft.Json.Serialization return false; } - private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract) + private void SerializeString(JsonWriter writer, object value, JsonStringContract contract) { contract.InvokeOnSerializing(value, Serializer.Context); string s; - if (TryConvertToString(value, contract.UnderlyingType, out s)) - { - writer.WriteValue(s); - contract.InvokeOnSerialized(value, Serializer.Context); + TryConvertToString(value, contract.UnderlyingType, out s); + writer.WriteValue(s); - return; - } + contract.InvokeOnSerialized(value, Serializer.Context); + } + + private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract) + { + contract.InvokeOnSerializing(value, Serializer.Context); SerializeStack.Add(value); writer.WriteStartObject(); @@ -429,22 +442,46 @@ namespace Newtonsoft.Json.Serialization contract.InvokeOnSerialized(values, Serializer.Context); } - private void SerializeDictionary(JsonWriter writer, IDictionary values, JsonDictionaryContract contract) +#if !SILVERLIGHT && !PocketPC + private void SerializeISerializable(JsonWriter writer, ISerializable value, JsonISerializableContract contract) { - contract.InvokeOnSerializing(values, Serializer.Context); + contract.InvokeOnSerializing(value, Serializer.Context); + SerializeStack.Add(value); - SerializeStack.Add(values); + writer.WriteStartObject(); + + SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, new FormatterConverter()); + value.GetObjectData(serializationInfo, Serializer.Context); + + foreach (SerializationEntry serializationEntry in serializationInfo) + { + writer.WritePropertyName(serializationEntry.Name); + SerializeValue(writer, serializationEntry.Value, null, GetContractSafe(serializationEntry.Value)); + } + + writer.WriteEndObject(); + + SerializeStack.RemoveAt(SerializeStack.Count - 1); + contract.InvokeOnSerialized(value, Serializer.Context); + } +#endif + + private void SerializeDictionary(JsonWriter writer, IWrappedDictionary values, JsonDictionaryContract contract) + { + contract.InvokeOnSerializing(values.UnderlyingDictionary, Serializer.Context); + + SerializeStack.Add(values.UnderlyingDictionary); writer.WriteStartObject(); bool isReference = contract.IsReference ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); if (isReference) { writer.WritePropertyName(JsonTypeReflector.IdPropertyName); - writer.WriteValue(Serializer.ReferenceResolver.GetReference(values)); + writer.WriteValue(Serializer.ReferenceResolver.GetReference(values.UnderlyingDictionary)); } if (HasFlag(Serializer.TypeNameHandling, TypeNameHandling.Objects)) { - WriteTypeProperty(writer, values.GetType()); + WriteTypeProperty(writer, values.UnderlyingDictionary.GetType()); } int initialDepth = writer.Top; @@ -475,7 +512,7 @@ namespace Newtonsoft.Json.Serialization } catch (Exception ex) { - if (IsErrorHandled(values, contract, propertyName, ex)) + if (IsErrorHandled(values.UnderlyingDictionary, contract, propertyName, ex)) HandleError(writer, initialDepth); else throw; @@ -485,7 +522,7 @@ namespace Newtonsoft.Json.Serialization writer.WriteEndObject(); SerializeStack.RemoveAt(SerializeStack.Count - 1); - contract.InvokeOnSerialized(values, Serializer.Context); + contract.InvokeOnSerialized(values.UnderlyingDictionary, Serializer.Context); } private string GetPropertyName(DictionaryEntry entry) @@ -496,8 +533,8 @@ namespace Newtonsoft.Json.Serialization return Convert.ToString(entry.Key, CultureInfo.InvariantCulture); else if (TryConvertToString(entry.Key, entry.Key.GetType(), out propertyName)) return propertyName; - - throw new JsonSerializationException("Could not create string property name from type '{0}'.".FormatWith(CultureInfo.InvariantCulture, entry.Key.GetType())); + else + return entry.Key.ToString(); } private void HandleError(JsonWriter writer, int initialDepth) diff --git a/Src/Newtonsoft.Json/Serialization/JsonStringContract.cs b/Src/Newtonsoft.Json/Serialization/JsonStringContract.cs new file mode 100644 index 00000000..bf1bda81 --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/JsonStringContract.cs @@ -0,0 +1,48 @@ +#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 +{ + /// + /// Contract details for a used by the . + /// + public class JsonStringContract : JsonContract + { + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonStringContract(Type underlyingType) + : base(underlyingType) + { + CreatedType = underlyingType; + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs index c82a9691..dceed5bf 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -83,6 +83,11 @@ namespace Newtonsoft.Json.Serialization return GetJsonContainerAttribute(type) as JsonObjectAttribute; } + public static JsonArrayAttribute GetJsonArrayAttribute(Type type) + { + return GetJsonContainerAttribute(type) as JsonArrayAttribute; + } + #if !PocketPC && !NET20 public static DataContractAttribute GetDataContractAttribute(Type type) { diff --git a/Src/Newtonsoft.Json/Serialization/MemberHandler.cs b/Src/Newtonsoft.Json/Serialization/MemberHandler.cs new file mode 100644 index 00000000..eb66dc2f --- /dev/null +++ b/Src/Newtonsoft.Json/Serialization/MemberHandler.cs @@ -0,0 +1,4 @@ +namespace Newtonsoft.Json.Serialization +{ + public delegate object MemberHandler(T target, params object[] args); +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/DictionaryWrapper.cs b/Src/Newtonsoft.Json/Utilities/DictionaryWrapper.cs index ab886a2d..d67734d6 100644 --- a/Src/Newtonsoft.Json/Utilities/DictionaryWrapper.cs +++ b/Src/Newtonsoft.Json/Utilities/DictionaryWrapper.cs @@ -248,11 +248,11 @@ namespace Newtonsoft.Json.Utilities return _dictionary.Contains(key); } - private struct DictionaryEnumerator : IDictionaryEnumerator + private struct DictionaryEnumerator : IDictionaryEnumerator { - private IEnumerator _e; + private readonly IEnumerator> _e; - public DictionaryEnumerator(IEnumerator e) + public DictionaryEnumerator(IEnumerator> e) { ValidationUtils.ArgumentNotNull(e, "e"); _e = e; @@ -275,7 +275,7 @@ namespace Newtonsoft.Json.Utilities public object Current { - get { return _e.Current; } + get { return new DictionaryEntry(_e.Current.Key, _e.Current.Value); } } public bool MoveNext() @@ -292,7 +292,7 @@ namespace Newtonsoft.Json.Utilities IDictionaryEnumerator IDictionary.GetEnumerator() { if (_genericDictionary != null) - return new DictionaryEnumerator(_genericDictionary.GetEnumerator()); + return new DictionaryEnumerator(_genericDictionary.GetEnumerator()); else return _dictionary.GetEnumerator(); } diff --git a/Src/Newtonsoft.Json/Utilities/LateBoundDelegateFactory.cs b/Src/Newtonsoft.Json/Utilities/LateBoundDelegateFactory.cs index fee1a3a5..7ea3a340 100644 --- a/Src/Newtonsoft.Json/Utilities/LateBoundDelegateFactory.cs +++ b/Src/Newtonsoft.Json/Utilities/LateBoundDelegateFactory.cs @@ -31,6 +31,7 @@ using System.Reflection; using System.Reflection.Emit; using System.Text; using System.Globalization; +using Newtonsoft.Json.Serialization; namespace Newtonsoft.Json.Utilities { diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index c6e05b96..2903156d 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -32,8 +32,6 @@ using System.Globalization; namespace Newtonsoft.Json.Utilities { - internal delegate object MemberHandler(T target, params object[] args); - internal static class ReflectionUtils { public static Type GetObjectType(object v)