diff --git a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs index 0b4b5718..117425f6 100644 --- a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs @@ -39,6 +39,7 @@ using Newtonsoft.Json.Converters; using System.Runtime.Serialization.Json; using Newtonsoft.Json.Utilities; using System.Globalization; +using System.ComponentModel; namespace Newtonsoft.Json.Tests { @@ -1289,5 +1290,34 @@ keyword such as type of business."" string json = JavaScriptConvert.SerializeObject(new GenericImpl()); Assert.AreEqual(@"{""Id"":0}", json); } + + public class DefaultValueAttributeTestClass + { + [DefaultValue("TestProperty1Value")] + public string TestProperty1 { get; set; } + + [DefaultValue(21)] + public int TestField1; + } + + [Test] + public void DefaultValueAttributeTest() + { + string json = JavaScriptConvert.SerializeObject(new DefaultValueAttributeTestClass(), + new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); + Assert.AreEqual(@"{""TestField1"":0,""TestProperty1"":null}", json); + + json = JavaScriptConvert.SerializeObject(new DefaultValueAttributeTestClass { TestField1 = int.MinValue, TestProperty1 = "NotDefault" }, + new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); + Assert.AreEqual(@"{""TestField1"":-2147483648,""TestProperty1"":""NotDefault""}", json); + + json = JavaScriptConvert.SerializeObject(new DefaultValueAttributeTestClass { TestField1 = 21, TestProperty1 = "NotDefault" }, + new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); + Assert.AreEqual(@"{""TestProperty1"":""NotDefault""}", json); + + json = JavaScriptConvert.SerializeObject(new DefaultValueAttributeTestClass { TestField1 = 21, TestProperty1 = "TestProperty1Value" }, + new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); + Assert.AreEqual(@"{}", json); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj index 81b39043..8dbeac7e 100644 --- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj +++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -130,6 +130,7 @@ + diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index ea9664d4..0004545b 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -1,13 +1,17 @@ -using System; +#if !SILVERLIGHT +using System; using System.Collections.Generic; using System.Globalization; -using System.Data; using System.Diagnostics; using Newtonsoft.Json; using System.IO; + using System.Web.Script.Serialization; + using NUnit.Framework; +using System.Runtime.Serialization.Json; +using System.Text; namespace Newtonsoft.Json.Tests { @@ -16,9 +20,16 @@ namespace Newtonsoft.Json.Tests /// public class PerformanceTests : TestFixtureBase { - private DateTime BaseDate = DateTime.Parse("01/01/2000"); + private int Iterations = 100; - //JSONSerializer ser = null; + public enum SerializeMethod + { + JsonNet, + JavaScriptSerializer, + DataContractJsonSerializer + } + + private DateTime BaseDate = DateTime.Parse("01/01/2000"); public string SerializeJsonNet(object value) { @@ -36,10 +47,7 @@ namespace Newtonsoft.Json.Tests StringWriter sw = new StringWriter(); Newtonsoft.Json.JsonTextWriter writer = new JsonTextWriter(sw); - if (true) - writer.Formatting = Formatting.Indented; - else - writer.Formatting = Formatting.None; + writer.Formatting = Formatting.None; writer.QuoteChar = '"'; json.Serialize(writer, value); @@ -50,15 +58,120 @@ namespace Newtonsoft.Json.Tests return output; } + public object DeserializeJsonNet(string json) + { + Type type = typeof(T); + + JsonSerializer serializer = new JsonSerializer(); + + serializer.NullValueHandling = NullValueHandling.Ignore; + + serializer.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace; + serializer.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore; + serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + return serializer.Deserialize(new StringReader(json), type); + } + public string SerializeWebExtensions(object value) { JavaScriptSerializer ser = new JavaScriptSerializer(); - List converters = new List(); - return ser.Serialize(value); } + public object DeserializeWebExtensions(string json) + { + JavaScriptSerializer ser = new JavaScriptSerializer(); + + return ser.Deserialize(json); + } + + public string SerializeDataContract(object value) + { + DataContractJsonSerializer dataContractSerializer + = new DataContractJsonSerializer(value.GetType()); + + MemoryStream ms = new MemoryStream(); + dataContractSerializer.WriteObject(ms, value); + + ms.Seek(0, SeekOrigin.Begin); + + using (StreamReader sr = new StreamReader(ms)) + { + return sr.ReadToEnd(); + } + } + + public object DeserializeDataContract(string json) + { + DataContractJsonSerializer dataContractSerializer + = new DataContractJsonSerializer(typeof(T)); + + MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + return dataContractSerializer.ReadObject(ms); + } + + public void BenchmarkSerializeMethod(SerializeMethod method, object value) + { + Stopwatch timed = new Stopwatch(); + timed.Start(); + + string json = null; + for (int x = 0; x < Iterations; x++) + { + switch (method) + { + case SerializeMethod.JsonNet: + json = JavaScriptConvert.SerializeObject(value); + break; + case SerializeMethod.JavaScriptSerializer: + json = SerializeWebExtensions(value); + break; + case SerializeMethod.DataContractJsonSerializer: + json = SerializeDataContract(value); + break; + } + } + + timed.Stop(); + + Console.WriteLine("Serialize method: {0}", method); + Console.WriteLine("{0} ms", timed.ElapsedMilliseconds); + Console.WriteLine(json); + Console.WriteLine(); + } + + public void BenchmarkDeserializeMethod(SerializeMethod method, string json) + { + Stopwatch timed = new Stopwatch(); + timed.Start(); + + object value = null; + for (int x = 0; x < Iterations; x++) + { + switch (method) + { + case SerializeMethod.JsonNet: + value = DeserializeJsonNet(json); + break; + case SerializeMethod.JavaScriptSerializer: + value = DeserializeWebExtensions(json); + break; + case SerializeMethod.DataContractJsonSerializer: + value = DeserializeDataContract(json); + break; + } + } + + timed.Stop(); + + Console.WriteLine("Serialize method: {0}", method); + Console.WriteLine("{0} ms", timed.ElapsedMilliseconds); + Console.WriteLine(value); + Console.WriteLine(); + } [Test] public void Serialize() @@ -83,19 +196,9 @@ namespace Newtonsoft.Json.Tests address.Address = "array 2 address"; test.Addresses.Add(address); - Stopwatch timed = new Stopwatch(); - timed.Start(); - - string json = null; - for (int x = 0; x < 10000; x++) - { - json = this.SerializeJsonNet(test); - //json = this.SerializeWebExtensions(test); - } - timed.Stop(); - - Console.WriteLine("{0}", timed.ElapsedMilliseconds + " ms"); - Console.WriteLine("{0}", json); + BenchmarkSerializeMethod(SerializeMethod.JsonNet, test); + BenchmarkSerializeMethod(SerializeMethod.JavaScriptSerializer, test); + BenchmarkSerializeMethod(SerializeMethod.DataContractJsonSerializer, test); } [Test] @@ -134,19 +237,9 @@ namespace Newtonsoft.Json.Tests ] }"; - for (int x = 0; x < 10000; x++) - { - Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer(); - - serializer.NullValueHandling = NullValueHandling.Ignore; - - serializer.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace; - serializer.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore; - serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - - TestClass test = (TestClass)serializer.Deserialize(new StringReader(json), typeof(TestClass)); - } - + BenchmarkDeserializeMethod(SerializeMethod.JsonNet, json); + BenchmarkDeserializeMethod(SerializeMethod.JavaScriptSerializer, json); + BenchmarkDeserializeMethod(SerializeMethod.DataContractJsonSerializer, json); } } @@ -229,4 +322,5 @@ namespace Newtonsoft.Json.Tests private DateTime _Entered = DateTime.Parse("01/01/2007", CultureInfo.CurrentCulture.DateTimeFormat); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json/DefaultValueHandling.cs b/Src/Newtonsoft.Json/DefaultValueHandling.cs new file mode 100644 index 00000000..6e395d70 --- /dev/null +++ b/Src/Newtonsoft.Json/DefaultValueHandling.cs @@ -0,0 +1,47 @@ +#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 +{ + /// + /// Specifies default value handling options for the . + /// + public enum DefaultValueHandling + { + /// + /// Include null values when serializing and deserializing objects. + /// + Include = 0, + /// + /// Ignore null values when serializing and deserializing objects. + /// + Ignore = 1 + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JavaScriptConvert.cs b/Src/Newtonsoft.Json/JavaScriptConvert.cs index c5e384c6..b97a5097 100644 --- a/Src/Newtonsoft.Json/JavaScriptConvert.cs +++ b/Src/Newtonsoft.Json/JavaScriptConvert.cs @@ -430,7 +430,7 @@ namespace Newtonsoft.Json /// A JSON string representation of the object. public static string SerializeObject(object value) { - return SerializeObject(value, null); + return SerializeObject(value, (JsonSerializerSettings)null); } /// @@ -441,17 +441,27 @@ namespace Newtonsoft.Json /// A JSON string representation of the object. public static string SerializeObject(object value, params JsonConverter[] converters) { + JsonSerializerSettings settings = (converters != null && converters.Length > 0) + ? new JsonSerializerSettings { Converters = converters } + : null; + + return SerializeObject(value, settings); + } + + /// + /// Serializes the specified object to a JSON string using a collection of . + /// + /// The object to serialize. + /// + /// The used to serialize the object. + /// If this is null, default serialization settings will be is used. + /// + /// A JSON string representation of the object. + public static string SerializeObject(object value, JsonSerializerSettings settings) + { + JsonSerializer jsonSerializer = JsonSerializer.Create(settings); + StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); - JsonSerializer jsonSerializer = new JsonSerializer(); - - if (!CollectionUtils.IsNullOrEmpty(converters)) - { - for (int i = 0; i < converters.Length; i++) - { - jsonSerializer.Converters.Add(converters[i]); - } - } - using (JsonWriter jsonWriter = new JsonTextWriter(sw)) { //jsonWriter.Formatting = Formatting.Indented; @@ -468,7 +478,7 @@ namespace Newtonsoft.Json /// The deserialized object from the Json string. public static object DeserializeObject(string value) { - return DeserializeObject(value, null, null); + return DeserializeObject(value, null, (JsonSerializerSettings)null); } /// @@ -479,7 +489,7 @@ namespace Newtonsoft.Json /// The deserialized object from the Json string. public static object DeserializeObject(string value, Type type) { - return DeserializeObject(value, type, null); + return DeserializeObject(value, type, (JsonSerializerSettings)null); } /// @@ -530,16 +540,27 @@ namespace Newtonsoft.Json /// The deserialized object from the JSON string. public static object DeserializeObject(string value, Type type, params JsonConverter[] converters) { - StringReader sr = new StringReader(value); - JsonSerializer jsonSerializer = new JsonSerializer(); + JsonSerializerSettings settings = (converters != null && converters.Length > 0) + ? new JsonSerializerSettings { Converters = converters } + : null; - if (!CollectionUtils.IsNullOrEmpty(converters)) - { - for (int i = 0; i < converters.Length; i++) - { - jsonSerializer.Converters.Add(converters[i]); - } - } + return DeserializeObject(value, type, settings); + } + + /// + /// Deserializes the JSON string to the specified type. + /// + /// The object to deserialize. + /// The type of the object to deserialize. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be is used. + /// + /// The deserialized object from the JSON string. + public static object DeserializeObject(string value, Type type, JsonSerializerSettings settings) + { + StringReader sr = new StringReader(value); + JsonSerializer jsonSerializer = JsonSerializer.Create(settings); object deserializedValue; diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 4db23a49..580fde2c 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -45,12 +45,13 @@ namespace Newtonsoft.Json public class JsonSerializer { private static readonly Dictionary ConverterTypeCache = new Dictionary(); - private static Dictionary TypeMemberMappings = new Dictionary(); + private static readonly Dictionary TypeMemberMappingsCache = new Dictionary(); private ReferenceLoopHandling _referenceLoopHandling; private MissingMemberHandling _missingMemberHandling; private ObjectCreationHandling _objectCreationHandling; private NullValueHandling _nullValueHandling; + private DefaultValueHandling _defaultValueHandling; private int _level; private JsonConverterCollection _converters; @@ -99,6 +100,21 @@ namespace Newtonsoft.Json } } + /// + /// Get or set how null default are handled during serialization and deserialization. + /// + public DefaultValueHandling DefaultValueHandling + { + get { return _defaultValueHandling; } + set + { + if (value < DefaultValueHandling.Include || value > DefaultValueHandling.Ignore) + throw new ArgumentOutOfRangeException("value"); + + _defaultValueHandling = value; + } + } + /// /// Gets or sets how objects are created during deserialization. /// @@ -135,10 +151,31 @@ namespace Newtonsoft.Json /// public JsonSerializer() { - _referenceLoopHandling = ReferenceLoopHandling.Error; - _missingMemberHandling = MissingMemberHandling.Error; - _nullValueHandling = NullValueHandling.Include; - _objectCreationHandling = ObjectCreationHandling.Auto; + _referenceLoopHandling = JsonSerializerSettings.DefaultReferenceLoopHandling; + _missingMemberHandling = JsonSerializerSettings.DefaultMissingMemberHandling; + _nullValueHandling = JsonSerializerSettings.DefaultNullValueHandling; + _defaultValueHandling = JsonSerializerSettings.DefaultDefaultValueHandling; + _objectCreationHandling = JsonSerializerSettings.DefaultObjectCreationHandling; + } + + + public static JsonSerializer Create(JsonSerializerSettings settings) + { + JsonSerializer jsonSerializer = new JsonSerializer(); + + if (settings != null) + { + if (!CollectionUtils.IsNullOrEmpty(settings.Converters)) + jsonSerializer.Converters.AddRange(settings.Converters); + + jsonSerializer.ReferenceLoopHandling = settings.ReferenceLoopHandling; + jsonSerializer.MissingMemberHandling = settings.MissingMemberHandling; + jsonSerializer.ObjectCreationHandling = settings.ObjectCreationHandling; + jsonSerializer.NullValueHandling = settings.NullValueHandling; + jsonSerializer.DefaultValueHandling = settings.DefaultValueHandling; + } + + return jsonSerializer; } #region Deserialize @@ -236,7 +273,7 @@ namespace Newtonsoft.Json { value = CreateJToken(reader); } - else if (typeof(IDictionary).IsAssignableFrom(objectType) || ReflectionUtils.IsSubClass(objectType, typeof(IDictionary<,>))) + else if (typeof(IDictionary).IsAssignableFrom(objectType) || ReflectionUtils.ImplementsGenericInterfaceDefinition(objectType, typeof(IDictionary<,>))) { if (existingValue == null) value = CreateAndPopulateDictionary(reader, objectType); @@ -321,11 +358,11 @@ namespace Newtonsoft.Json { MemberMappingCollection memberMappings; - if (TypeMemberMappings.TryGetValue(objectType, out memberMappings)) + if (TypeMemberMappingsCache.TryGetValue(objectType, out memberMappings)) return memberMappings; memberMappings = CreateMemberMappings(objectType); - TypeMemberMappings[objectType] = memberMappings; + TypeMemberMappingsCache[objectType] = memberMappings; return memberMappings; } @@ -357,7 +394,10 @@ namespace Newtonsoft.Json JsonConverter memberConverter = GetConverter(member, ReflectionUtils.GetMemberUnderlyingType(member)); - MemberMapping memberMapping = new MemberMapping(mappedName, member, ignored, readable, writable, memberConverter); + DefaultValueAttribute defaultValueAttribute = ReflectionUtils.GetAttribute(member, true); + object defaultValue = (defaultValueAttribute != null) ? defaultValueAttribute.Value : null; + + MemberMapping memberMapping = new MemberMapping(mappedName, member, ignored, readable, writable, memberConverter, defaultValue); memberMappings.AddMapping(memberMapping); } @@ -435,12 +475,18 @@ namespace Newtonsoft.Json // get the member's underlying type memberType = ReflectionUtils.GetMemberUnderlyingType(memberMapping.Member); - object currentValue = ReflectionUtils.GetMemberValue(memberMapping.Member, target); - bool useExistingValue = (currentValue != null - && (_objectCreationHandling == ObjectCreationHandling.Auto || _objectCreationHandling == ObjectCreationHandling.Reuse) - && (reader.TokenType == JsonToken.StartArray || reader.TokenType == JsonToken.StartObject) - && (!memberType.IsArray && !ReflectionUtils.IsSubClass(memberType, typeof(ReadOnlyCollection<>)))); + 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.InheritsGenericClassDefinition(memberType, typeof(ReadOnlyCollection<>))); + } + if (!memberMapping.Writable && !useExistingValue) { @@ -453,6 +499,9 @@ namespace Newtonsoft.Json 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); } @@ -712,8 +761,13 @@ namespace Newtonsoft.Json return false; } - private void WriteMemberInfoProperty(JsonWriter writer, object value, MemberInfo member, string propertyName, JsonConverter memberConverter) + private void WriteMemberInfoProperty(JsonWriter writer, object value, MemberMapping memberMapping) { + MemberInfo member = memberMapping.Member; + string propertyName = memberMapping.MappingName; + JsonConverter memberConverter = memberMapping.MemberConverter; + object defaultValue = memberMapping.DefaultValue; + if (!ReflectionUtils.IsIndexedProperty(member)) { object memberValue = ReflectionUtils.GetMemberValue(member, value); @@ -721,6 +775,9 @@ namespace Newtonsoft.Json if (_nullValueHandling == NullValueHandling.Ignore && memberValue == null) return; + if (_defaultValueHandling == DefaultValueHandling.Ignore && object.Equals(memberValue, defaultValue)) + return; + if (writer.SerializeStack.IndexOf(memberValue) != -1) { switch (_referenceLoopHandling) @@ -776,7 +833,7 @@ namespace Newtonsoft.Json foreach (MemberMapping memberMapping in memberMappings) { if (!memberMapping.Ignored && memberMapping.Readable) - WriteMemberInfoProperty(writer, value, memberMapping.Member, memberMapping.MappingName, memberMapping.MemberConverter); + WriteMemberInfoProperty(writer, value, memberMapping); } writer.WriteEndObject(); diff --git a/Src/Newtonsoft.Json/JsonSerializerSettings.cs b/Src/Newtonsoft.Json/JsonSerializerSettings.cs new file mode 100644 index 00000000..4b4f7e54 --- /dev/null +++ b/Src/Newtonsoft.Json/JsonSerializerSettings.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json +{ + public class JsonSerializerSettings + { + internal const ReferenceLoopHandling DefaultReferenceLoopHandling = ReferenceLoopHandling.Error; + internal const MissingMemberHandling DefaultMissingMemberHandling = MissingMemberHandling.Error; + internal const NullValueHandling DefaultNullValueHandling = NullValueHandling.Include; + internal const DefaultValueHandling DefaultDefaultValueHandling = DefaultValueHandling.Include; + internal const ObjectCreationHandling DefaultObjectCreationHandling = ObjectCreationHandling.Auto; + + public ReferenceLoopHandling ReferenceLoopHandling { get; set; } + public MissingMemberHandling MissingMemberHandling { get; set; } + public ObjectCreationHandling ObjectCreationHandling { get; set; } + public NullValueHandling NullValueHandling { get; set; } + public DefaultValueHandling DefaultValueHandling { get; set; } + public IList Converters { get; set; } + + public JsonSerializerSettings() + { + ReferenceLoopHandling = DefaultReferenceLoopHandling; + MissingMemberHandling = DefaultMissingMemberHandling; + ObjectCreationHandling = DefaultObjectCreationHandling; + NullValueHandling = DefaultNullValueHandling; + DefaultValueHandling = DefaultDefaultValueHandling; + Converters = null; + } + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonTextWriter.cs b/Src/Newtonsoft.Json/JsonTextWriter.cs index 3a261169..040481d6 100644 --- a/Src/Newtonsoft.Json/JsonTextWriter.cs +++ b/Src/Newtonsoft.Json/JsonTextWriter.cs @@ -207,14 +207,13 @@ namespace Newtonsoft.Json if (Formatting == Formatting.Indented) { _writer.Write(Environment.NewLine); - // for each level of object... - for (int i = 0; i < Top; i++) + + // levels of indentation multiplied by the indent count + int currentIndentCount = Top * _indentation; + + for (int i = 0; i < currentIndentCount; i++) { - // ...write the indent char the specified number of times - for (int j = 0; j < _indentation; j++) - { - _writer.Write(_indentChar); - } + _writer.Write(_indentChar); } } } diff --git a/Src/Newtonsoft.Json/MemberMapping.cs b/Src/Newtonsoft.Json/MemberMapping.cs index 8f65601b..f8aa35e5 100644 --- a/Src/Newtonsoft.Json/MemberMapping.cs +++ b/Src/Newtonsoft.Json/MemberMapping.cs @@ -38,8 +38,9 @@ namespace Newtonsoft.Json private readonly bool _readable; private readonly bool _writable; private readonly JsonConverter _memberConverter; + private readonly object _defaultValue; - public MemberMapping(string mappingName, MemberInfo member, bool ignored, bool readable, bool writable, JsonConverter memberConverter) + public MemberMapping(string mappingName, MemberInfo member, bool ignored, bool readable, bool writable, JsonConverter memberConverter, object defaultValue) { _mappingName = mappingName; _member = member; @@ -47,6 +48,7 @@ namespace Newtonsoft.Json _readable = readable; _writable = writable; _memberConverter = memberConverter; + _defaultValue = defaultValue; } public string MappingName @@ -77,6 +79,11 @@ namespace Newtonsoft.Json public JsonConverter MemberConverter { get { return _memberConverter; } + } + + public object DefaultValue + { + get { return _defaultValue; } } } -} +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj index 51633598..20ec5856 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -218,6 +218,7 @@ + @@ -231,6 +232,7 @@ + @@ -260,6 +262,7 @@ + diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 2b9553c4..f6b4f774 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -74,8 +74,10 @@ + + diff --git a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs index ac07f550..85c8aaca 100644 --- a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs @@ -360,7 +360,7 @@ namespace Newtonsoft.Json.Utilities return true; else if (typeof(IList).IsAssignableFrom(type)) return true; - else if (ReflectionUtils.IsSubClass(type, typeof(IList<>))) + else if (ReflectionUtils.ImplementsGenericInterfaceDefinition(type, typeof(IList<>))) return true; else return false; @@ -371,7 +371,7 @@ namespace Newtonsoft.Json.Utilities ValidationUtils.ArgumentNotNull(list, "list"); Type collectionDefinition; - if (ReflectionUtils.IsSubClass(list.GetType(), typeof(ICollection<>), out collectionDefinition)) + if (ReflectionUtils.ImplementsGenericInterfaceDefinition(list.GetType(), typeof(ICollection<>), out collectionDefinition)) { Type collectionItemType = ReflectionUtils.GetCollectionItemType(collectionDefinition); @@ -398,7 +398,7 @@ namespace Newtonsoft.Json.Utilities ValidationUtils.ArgumentNotNull(list, "list"); Type listDefinition; - if (ReflectionUtils.IsSubClass(list.GetType(), typeof(IList<>), out listDefinition)) + if (ReflectionUtils.ImplementsGenericInterfaceDefinition(list.GetType(), typeof(IList<>), out listDefinition)) { Type collectionItemType = ReflectionUtils.GetCollectionItemType(listDefinition); @@ -426,7 +426,7 @@ namespace Newtonsoft.Json.Utilities ValidationUtils.ArgumentNotNull(dictionary, "dictionary"); Type dictionaryDefinition; - if (ReflectionUtils.IsSubClass(dictionary.GetType(), typeof(IDictionary<,>), out dictionaryDefinition)) + if (ReflectionUtils.ImplementsGenericInterfaceDefinition(dictionary.GetType(), typeof(IDictionary<,>), out dictionaryDefinition)) { Type dictionaryKeyType = ReflectionUtils.GetDictionaryKeyType(dictionaryDefinition); Type dictionaryValueType = ReflectionUtils.GetDictionaryValueType(dictionaryDefinition); @@ -466,7 +466,7 @@ namespace Newtonsoft.Json.Utilities list = new List(); isReadOnlyOrFixedSize = true; } - else if (ReflectionUtils.IsSubClass(listType, typeof(ReadOnlyCollection<>), out collectionType)) + else if (ReflectionUtils.InheritsGenericClassDefinition(listType, typeof(ReadOnlyCollection<>), out collectionType)) { Type readOnlyCollectionContentsType = collectionType.GetGenericArguments()[0]; Type genericEnumerable = ReflectionUtils.MakeGenericType(typeof(IEnumerable<>), readOnlyCollectionContentsType); @@ -522,7 +522,7 @@ namespace Newtonsoft.Json.Utilities { if (listType.IsArray) list = ToArray(((List)list).ToArray(), ReflectionUtils.GetCollectionItemType(listType)); - else if (ReflectionUtils.IsSubClass(listType, typeof(ReadOnlyCollection<>))) + else if (ReflectionUtils.InheritsGenericClassDefinition(listType, typeof(ReadOnlyCollection<>))) list = (IList)Activator.CreateInstance(listType, list); } diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index 6919e32d..452d6d75 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -124,47 +124,77 @@ namespace Newtonsoft.Json.Utilities return !CollectionUtils.IsNullOrEmpty(property.GetIndexParameters()); } - public static bool IsSubClass(Type type, Type check) + public static bool ImplementsGenericInterfaceDefinition(Type type, Type genericInterfaceDefinition) { Type implementingType; - return IsSubClass(type, check, out implementingType); + return ImplementsGenericInterfaceDefinition(type, genericInterfaceDefinition, out implementingType); } - public static bool IsSubClass(Type type, Type check, out Type implementingType) + public static bool ImplementsGenericInterfaceDefinition(Type type, Type genericInterfaceDefinition, out Type implementingType) { ValidationUtils.ArgumentNotNull(type, "type"); - ValidationUtils.ArgumentNotNull(check, "check"); + ValidationUtils.ArgumentNotNull(genericInterfaceDefinition, "genericInterfaceDefinition"); - return IsSubClassInternal(type, type, check, out implementingType); - } + if (!genericInterfaceDefinition.IsInterface || !genericInterfaceDefinition.IsGenericTypeDefinition) + throw new ArgumentNullException("'{0}' is not a generic interface definition.".FormatWith(CultureInfo.InvariantCulture, genericInterfaceDefinition)); - private static bool IsSubClassInternal(Type initialType, Type currentType, Type check, out Type implementingType) - { - if (currentType == check) + if (type.IsInterface) { - implementingType = currentType; - return true; - } - - // don't get interfaces for an interface unless the initial type is an interface - if (check.IsInterface && (initialType.IsInterface || currentType == initialType)) - { - foreach (Type t in currentType.GetInterfaces()) + if (type.IsGenericType) { - if (IsSubClassInternal(initialType, t, check, out implementingType)) - { - // don't return the interface itself, return it's implementor - if (check == implementingType) - implementingType = currentType; + Type interfaceDefinition = type.GetGenericTypeDefinition(); + if (genericInterfaceDefinition == interfaceDefinition) + { + implementingType = type; return true; } } } - if (currentType.IsGenericType && !currentType.IsGenericTypeDefinition) + foreach (Type i in type.GetInterfaces()) { - if (IsSubClassInternal(initialType, currentType.GetGenericTypeDefinition(), check, out implementingType)) + if (i.IsGenericType) + { + Type interfaceDefinition = i.GetGenericTypeDefinition(); + + if (genericInterfaceDefinition == interfaceDefinition) + { + implementingType = i; + return true; + } + } + } + + implementingType = null; + return false; + } + + + public static bool InheritsGenericClassDefinition(Type type, Type genericClassDefinition) + { + Type implementingType; + return InheritsGenericClassDefinition(type, genericClassDefinition, out implementingType); + } + + public static bool InheritsGenericClassDefinition(Type type, Type genericClassDefinition, out Type implementingType) + { + ValidationUtils.ArgumentNotNull(type, "type"); + ValidationUtils.ArgumentNotNull(genericClassDefinition, "genericClassDefinition"); + + if (!genericClassDefinition.IsClass || !genericClassDefinition.IsGenericTypeDefinition) + throw new ArgumentNullException("'{0}' is not a generic class definition.".FormatWith(CultureInfo.InvariantCulture, genericClassDefinition)); + + return InheritsGenericClassDefinitionInternal(type, type, genericClassDefinition, out implementingType); + } + + private static bool InheritsGenericClassDefinitionInternal(Type initialType, Type currentType, Type genericClassDefinition, out Type implementingType) + { + if (currentType.IsGenericType) + { + Type currentGenericClassDefinition = currentType.GetGenericTypeDefinition(); + + if (genericClassDefinition == currentGenericClassDefinition) { implementingType = currentType; return true; @@ -177,7 +207,7 @@ namespace Newtonsoft.Json.Utilities return false; } - return IsSubClassInternal(initialType, currentType.BaseType, check, out implementingType); + return InheritsGenericClassDefinitionInternal(initialType, currentType.BaseType, genericClassDefinition, out implementingType); } /// @@ -194,7 +224,7 @@ namespace Newtonsoft.Json.Utilities { return type.GetElementType(); } - else if (IsSubClass(type, typeof(ICollection<>), out genericListType)) + else if (ImplementsGenericInterfaceDefinition(type, typeof(ICollection<>), out genericListType)) { if (genericListType.IsGenericTypeDefinition) throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type)); @@ -216,7 +246,7 @@ namespace Newtonsoft.Json.Utilities ValidationUtils.ArgumentNotNull(dictionaryType, "type"); Type genericDictionaryType; - if (IsSubClass(dictionaryType, typeof(IDictionary<,>), out genericDictionaryType)) + if (ImplementsGenericInterfaceDefinition(dictionaryType, typeof(IDictionary<,>), out genericDictionaryType)) { if (genericDictionaryType.IsGenericTypeDefinition) throw new Exception("Type {0} is not a dictionary.".FormatWith(CultureInfo.InvariantCulture, dictionaryType));