From d9c8d78d770e1df5cd39f39bd7864aef410ddd53 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Sat, 1 Sep 2012 13:34:07 +1200 Subject: [PATCH] -Added IgnoreAll to DefaultValueHandling which ignores all default class/struct/privative values -Added support for DataMemberAttribute's EmitDefaultValue -Fixed losing decimal precision when writing JValues -Fixed error when calling ToList on empty JObjects and JArrays --- Doc/Introduction.aml | 17 +- Doc/ReducingSerializedJSONSize.aml | 4 +- Doc/SerializationSettings.aml | 11 +- Doc/doc.shfbproj | 1 - .../Documentation/SerializationTests.cs | 2 +- .../Linq/DynamicTests.cs | 4 +- Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs | 16 ++ .../Linq/JObjectTests.cs | 19 ++ Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs | 28 +++ .../Properties/AssemblyInfo.cs | 2 +- .../DefaultValueHandlingTests.cs | 162 ++++++++++++++++-- Src/Newtonsoft.Json/DefaultValueHandling.cs | 15 +- Src/Newtonsoft.Json/JsonSerializer.cs | 2 +- Src/Newtonsoft.Json/Linq/JContainer.cs | 2 +- Src/Newtonsoft.Json/Linq/JObject.cs | 2 +- Src/Newtonsoft.Json/Linq/JValue.cs | 8 +- .../Properties/AssemblyInfo.cs | 2 +- .../Serialization/DefaultContractResolver.cs | 3 +- .../JsonSerializerInternalWriter.cs | 8 +- .../Utilities/ReflectionUtils.cs | 43 +++++ 20 files changed, 312 insertions(+), 39 deletions(-) diff --git a/Doc/Introduction.aml b/Doc/Introduction.aml index 4da88112..e4bbfe42 100644 --- a/Doc/Introduction.aml +++ b/Doc/Introduction.aml @@ -31,11 +31,11 @@ Convert JSON to and from XML - Supports .NET 2, .NET 3.5, .NET 4, Silverlight, Windows Phone and Windows 8 + Supports .NET 2, .NET 3.5, .NET 4, .NET 4.5, Silverlight, Windows Phone and Windows 8 RT -The JSON serializer is a good choice when the JSON you are reading or writing maps closely to a .NET class. +The JSON serializer in Json.NET is a good choice when the JSON you are reading or writing maps closely to a .NET class. LINQ to JSON is good for situations where you are only interested in getting values from JSON, you don't have a class to serialize or deserialize to, or the JSON is radically different from your class and you need to manually read @@ -72,19 +72,24 @@ and write from your objects. was required and Json.NET will split into the three major classes it still uses today, JsonReader, JsonWriter and JsonSerializer. Json.NET was first released in June 2006. Since then Json.NET has been downloaded - hundreds of thousands of times by developers and is used in a number of major open + hundreds of thousands of times by developers around the world. It is used in many major open source projects including Mono http://www.mono-project.com/ _blank , an open source implementation - of the .NET framework, + of the .NET framework; RavenDB http://ravendb.net/ _blank , - a JSON based documentat database, and + a JSON based documentat database; + SignalR + http://signalr.net/ + _blank + , + an async library for building real-time, multi-user interactive web applications; and ASP.NET Web API http://www.asp.net/web-api _blank @@ -101,7 +106,7 @@ and write from your objects.
Donate Json.NET is a free open source project developed in my own time. - I really appreciate your feedback and support for Json.NET and its future development. + I really appreciate your feedback and donations help support Json.NET and its future development.
diff --git a/Doc/ReducingSerializedJSONSize.aml b/Doc/ReducingSerializedJSONSize.aml index 9cbe0698..a99bedea 100644 --- a/Doc/ReducingSerializedJSONSize.aml +++ b/Doc/ReducingSerializedJSONSize.aml @@ -84,10 +84,10 @@ T:Newtonsoft.Json.DefaultValueHandling is an option on the JsonSerializer and controls how the serializer handles - properties with a default value. Setting a value of DefaultValueHandling.Ignore + properties with a default value. Setting a value of DefaultValueHandling.IgnoreAll will make the JsonSerializer skip writing any properties that have a default value to the JSON result. For object references this will be null. For value - types like int and DateTime the serializer will skip the default unitialized + types like int and DateTime the serializer will skip the default uninitialized value for that value type. Json.NET also allows you to customize what the default value of an individual diff --git a/Doc/SerializationSettings.aml b/Doc/SerializationSettings.aml index c7512f86..e5a89c69 100644 --- a/Doc/SerializationSettings.aml +++ b/Doc/SerializationSettings.aml @@ -125,11 +125,18 @@ Include - By default Json.NET will write a field/property value to JSON when serializing if the value is the same as the field/property's default value. The Json.NET deserializer will continue setting a field/property if the JSON value is the same as the default value. + By default Json.NET will write a field/property value to JSON when serializing if the value is the same as the field/property's default value. + The Json.NET deserializer will continue setting a field/property if the JSON value is the same as the default value. Ignore - Json.NET will skip writing a field/property value to JSON if the value is the same as the field/property's default value. The Json.NET deserializer will skip setting a .NET object's field/property if the JSON value is the same as the default value. + Json.NET will skip writing a field/property value to JSON if the value is null or is the same as the value specified in field/property's T:System.ComponentModel.DefaultValueAttribute. The Json.NET deserializer will skip setting a .NET object's field/property if the JSON value is the same as the default value. + + + IgnoreAll + Json.NET will skip writing a field/property value to JSON if the value is the same as the field/property's default value, or the custom + value specified in T:System.ComponentModel.DefaultValueAttribute if it is present. The Json.NET deserializer + will skip setting a .NET object's field/property if the JSON value is the same as the default value. DefaultValueHandling can also be customized on individual properties with JsonPropertyAttribute. diff --git a/Doc/doc.shfbproj b/Doc/doc.shfbproj index 9be93ec3..af9f3ede 100644 --- a/Doc/doc.shfbproj +++ b/Doc/doc.shfbproj @@ -141,7 +141,6 @@ - diff --git a/Src/Newtonsoft.Json.Tests/Documentation/SerializationTests.cs b/Src/Newtonsoft.Json.Tests/Documentation/SerializationTests.cs index 2b82d61f..b524bd38 100644 --- a/Src/Newtonsoft.Json.Tests/Documentation/SerializationTests.cs +++ b/Src/Newtonsoft.Json.Tests/Documentation/SerializationTests.cs @@ -725,7 +725,7 @@ namespace Newtonsoft.Json.Tests.Documentation string ignored = JsonConvert.SerializeObject(invoice, Formatting.Indented, - new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); + new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.IgnoreAll }); // { // "Company": "Acme Ltd.", diff --git a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs index 80a16955..69be6719 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs @@ -701,7 +701,7 @@ namespace Newtonsoft.Json.Tests.Linq // "Enabled": true, // "Price": 2.45, // "StockCount": 9000, - // "StockValue": 22050.0 + // "StockValue": 22050.00 // } Assert.AreEqual(@"{ @@ -709,7 +709,7 @@ namespace Newtonsoft.Json.Tests.Linq ""Enabled"": true, ""Price"": 2.45, ""StockCount"": 9000, - ""StockValue"": 22050.0 + ""StockValue"": 22050.00 }", json); } } diff --git a/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs index 059eb6d0..bce45527 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs @@ -561,5 +561,21 @@ Parameter name: index", JArray.Parse(json); }); } + + [Test] + public void ToListOnEmptyArray() + { + string json = @"{""decks"":[]}"; + + JArray decks = (JArray)JObject.Parse(json)["decks"]; + IList l = decks.ToList(); + Assert.AreEqual(0, l.Count); + + json = @"{""decks"":[1]}"; + + decks = (JArray)JObject.Parse(json)["decks"]; + l = decks.ToList(); + Assert.AreEqual(1, l.Count); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs index 7ff6f612..2f3ce3b9 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -1846,5 +1846,24 @@ Parameter name: arrayIndex", Assert.IsFalse(o1.DeepEquals(null)); } + + [Test] + public void ToListOnEmptyObject() + { + JObject o = JObject.Parse(@"{}"); + IList l1 = o.ToList(); + Assert.AreEqual(0, l1.Count); + + IList> l2 = o.ToList>(); + Assert.AreEqual(0, l2.Count); + + o = JObject.Parse(@"{'hi':null}"); + + l1 = o.ToList(); + Assert.AreEqual(1, l1.Count); + + l2 = o.ToList>(); + Assert.AreEqual(1, l2.Count); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs index 859361bf..6bc6c6eb 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs @@ -314,5 +314,33 @@ namespace Newtonsoft.Json.Tests.Linq Assert.AreEqual("5.2", json); } + + public class Rate + { + public decimal Compoundings { get; set; } + } + + private readonly Rate rate = new Rate { Compoundings = 12.166666666666666666666666667m }; + + [Test] + public void WriteFullDecimalPrecision() + { + var jTokenWriter = new JTokenWriter(); + new JsonSerializer().Serialize(jTokenWriter, rate); + string json = jTokenWriter.Token.ToString(); + Assert.AreEqual(@"{ + ""Compoundings"": 12.166666666666666666666666667 +}", json); + } + + [Test] + public void RoundTripDecimal() + { + var jTokenWriter = new JTokenWriter(); + new JsonSerializer().Serialize(jTokenWriter, rate); + var rate2 = new JsonSerializer().Deserialize(new JTokenReader(jTokenWriter.Token)); + + Assert.AreEqual(rate.Compoundings, rate2.Compoundings); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index af18a064..82c70d36 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -76,5 +76,5 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.8.15215")] +[assembly: AssemblyFileVersion("4.5.8.15301")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs index d7e11efe..7f4db1f0 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs @@ -25,6 +25,12 @@ using System; using System.ComponentModel; +using System.IO; +using System.Runtime.Serialization; +#if !(SILVERLIGHT || PocketPC || NET20 || NET35 || NETFX_CORE || PORTABLE) +using System.Runtime.Serialization.Json; +#endif +using System.Text; using Newtonsoft.Json.Tests.TestObjects; #if !NETFX_CORE using NUnit.Framework; @@ -57,26 +63,33 @@ namespace Newtonsoft.Json.Tests.Serialization Formatting.Indented, new JsonSerializerSettings { }); - // { - // "Company": "Acme Ltd.", - // "Amount": 50.0, - // "Paid": false, - // "PaidDate": null, - // "FollowUpDays": 30, - // "FollowUpEmailAddress": "" - // } + Assert.AreEqual(@"{ + ""Company"": ""Acme Ltd."", + ""Amount"": 50.0, + ""Paid"": false, + ""PaidDate"": null, + ""FollowUpDays"": 30, + ""FollowUpEmailAddress"": """" +}", included); string ignored = JsonConvert.SerializeObject(invoice, Formatting.Indented, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); - // { - // "Company": "Acme Ltd.", - // "Amount": 50.0 - // } + Assert.AreEqual(@"{ + ""Company"": ""Acme Ltd."", + ""Amount"": 50.0, + ""Paid"": false +}", ignored); - Console.WriteLine(included); - Console.WriteLine(ignored); + string ignoredAll = JsonConvert.SerializeObject(invoice, + Formatting.Indented, + new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.IgnoreAll }); + + Assert.AreEqual(@"{ + ""Company"": ""Acme Ltd."", + ""Amount"": 50.0 +}", ignoredAll); } [Test] @@ -172,5 +185,126 @@ namespace Newtonsoft.Json.Tests.Serialization Assert.IsFalse(MathUtils.ApproxEquals(1000.0, 1000.000000000001)); Assert.IsFalse(MathUtils.ApproxEquals(0.0, 0.00001)); } + +#if !NET20 + [Test] + public void EmitDefaultValueTest() + { + EmitDefaultValueClass c = new EmitDefaultValueClass(); + +#if !(SILVERLIGHT || PocketPC || NET20 || NET35 || NETFX_CORE || PORTABLE) + DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(EmitDefaultValueClass)); + + MemoryStream ms = new MemoryStream(); + jsonSerializer.WriteObject(ms, c); + + Assert.AreEqual("{}", Encoding.UTF8.GetString(ms.ToArray())); +#endif + + string json = JsonConvert.SerializeObject(c); + + Assert.AreEqual("{}", json); + } +#endif + + [Test] + public void DefaultValueHandlingPropertyTest() + { + DefaultValueHandlingPropertyClass c = new DefaultValueHandlingPropertyClass(); + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"{ + ""IntInclude"": 0, + ""IntDefault"": 0 +}", json); + + json = JsonConvert.SerializeObject(c, Formatting.Indented, new JsonSerializerSettings + { + DefaultValueHandling = DefaultValueHandling.IgnoreAll + }); + + Assert.AreEqual(@"{ + ""IntInclude"": 0 +}", json); + + json = JsonConvert.SerializeObject(c, Formatting.Indented, new JsonSerializerSettings + { + DefaultValueHandling = DefaultValueHandling.Include + }); + + Assert.AreEqual(@"{ + ""IntInclude"": 0, + ""IntDefault"": 0 +}", json); + } } + + public struct DefaultStruct + { + public string Default { get; set; } + } + + public class DefaultValueHandlingPropertyClass + { + [JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAll)] + public int IntIgnore { get; set; } + + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)] + public int IntInclude { get; set; } + + [JsonProperty] + public int IntDefault { get; set; } + } + +#if !NET20 + [DataContract] + public class EmitDefaultValueClass + { + [DataMember(EmitDefaultValue = false)] + public Guid Guid { get; set; } + [DataMember(EmitDefaultValue = false)] + public TimeSpan TimeSpan { get; set; } + [DataMember(EmitDefaultValue = false)] + public DateTime DateTime { get; set; } + [DataMember(EmitDefaultValue = false)] + public DateTimeOffset DateTimeOffset { get; set; } + [DataMember(EmitDefaultValue = false)] + public decimal Decimal { get; set; } + [DataMember(EmitDefaultValue = false)] + public int Integer { get; set; } + [DataMember(EmitDefaultValue = false)] + public double Double { get; set; } + [DataMember(EmitDefaultValue = false)] + public bool Boolean { get; set; } + [DataMember(EmitDefaultValue = false)] + public DefaultStruct Struct { get; set; } + [DataMember(EmitDefaultValue = false)] + public StringComparison Enum { get; set; } + + [DataMember(EmitDefaultValue = false)] + public Guid? NullableGuid { get; set; } + [DataMember(EmitDefaultValue = false)] + public TimeSpan? NullableTimeSpan { get; set; } + [DataMember(EmitDefaultValue = false)] + public DateTime? NullableDateTime { get; set; } + [DataMember(EmitDefaultValue = false)] + public DateTimeOffset? NullableDateTimeOffset { get; set; } + [DataMember(EmitDefaultValue = false)] + public decimal? NullableDecimal { get; set; } + [DataMember(EmitDefaultValue = false)] + public int? NullableInteger { get; set; } + [DataMember(EmitDefaultValue = false)] + public double? NullableDouble { get; set; } + [DataMember(EmitDefaultValue = false)] + public bool? NullableBoolean { get; set; } + [DataMember(EmitDefaultValue = false)] + public DefaultStruct? NullableStruct { get; set; } + [DataMember(EmitDefaultValue = false)] + public StringComparison? NullableEnum { get; set; } + + [DataMember(EmitDefaultValue = false)] + public object Object { get; set; } + } +#endif } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/DefaultValueHandling.cs b/Src/Newtonsoft.Json/DefaultValueHandling.cs index 91d37381..908fd722 100644 --- a/Src/Newtonsoft.Json/DefaultValueHandling.cs +++ b/Src/Newtonsoft.Json/DefaultValueHandling.cs @@ -24,6 +24,7 @@ #endregion using System; +using System.ComponentModel; namespace Newtonsoft.Json { @@ -44,7 +45,9 @@ namespace Newtonsoft.Json Include = 0, /// /// Ignore members where the member value is the same as the member's default value when serializing objects - /// so that is is not written to JSON, and ignores setting members when the JSON value equals the member's default value. + /// so that is is not written to JSON. This option will only ignore null values for object and nullable types + /// unless the property explicitly has a specified. To ignore default values + /// for all types use the IgnoreAll option. /// Ignore = 1, /// @@ -55,6 +58,14 @@ namespace Newtonsoft.Json /// Ignore members where the member value is the same as the member's default value when serializing objects /// and sets members to their default value when deserializing. /// - IgnoreAndPopulate = Ignore | Populate + IgnoreAndPopulate = Ignore | Populate, + /// + /// Ignore members where the member value is the same as the member's default value when serializing objects + /// so that is is not written to JSON, and ignores setting members when the JSON value equals the member's default value. + /// This option will ignore all default values (e.g. null for objects and nullable typesl; 0 for integers, + /// decimals and floating point numbers; and false for booleans). The default value ignored can be override by + /// the . + /// + IgnoreAll = 4 } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index f76e96b3..8a1b1044 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -209,7 +209,7 @@ namespace Newtonsoft.Json get { return _defaultValueHandling; } set { - if (value < DefaultValueHandling.Include || value > DefaultValueHandling.IgnoreAndPopulate) + if (value < DefaultValueHandling.Include || value > DefaultValueHandling.IgnoreAll) throw new ArgumentOutOfRangeException("value"); _defaultValueHandling = value; diff --git a/Src/Newtonsoft.Json/Linq/JContainer.cs b/Src/Newtonsoft.Json/Linq/JContainer.cs index c577628e..e8a52e92 100644 --- a/Src/Newtonsoft.Json/Linq/JContainer.cs +++ b/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -503,7 +503,7 @@ namespace Newtonsoft.Json.Linq throw new ArgumentNullException("array"); if (arrayIndex < 0) throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0."); - if (arrayIndex >= array.Length) + if (arrayIndex >= array.Length && arrayIndex != 0) throw new ArgumentException("arrayIndex is equal to or greater than the length of array."); if (Count > array.Length - arrayIndex) throw new ArgumentException("The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array."); diff --git a/Src/Newtonsoft.Json/Linq/JObject.cs b/Src/Newtonsoft.Json/Linq/JObject.cs index b2bf7f9b..8719aadf 100644 --- a/Src/Newtonsoft.Json/Linq/JObject.cs +++ b/Src/Newtonsoft.Json/Linq/JObject.cs @@ -472,7 +472,7 @@ namespace Newtonsoft.Json.Linq throw new ArgumentNullException("array"); if (arrayIndex < 0) throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0."); - if (arrayIndex >= array.Length) + if (arrayIndex >= array.Length && arrayIndex != 0) throw new ArgumentException("arrayIndex is equal to or greater than the length of array."); if (Count > array.Length - arrayIndex) throw new ArgumentException("The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array."); diff --git a/Src/Newtonsoft.Json/Linq/JValue.cs b/Src/Newtonsoft.Json/Linq/JValue.cs index b4b37b32..5fd95037 100644 --- a/Src/Newtonsoft.Json/Linq/JValue.cs +++ b/Src/Newtonsoft.Json/Linq/JValue.cs @@ -539,8 +539,12 @@ namespace Newtonsoft.Json.Linq writer.WriteValue(Convert.ToInt64(_value, CultureInfo.InvariantCulture)); return; case JTokenType.Float: - if (_value is float) - writer.WriteValue(_value); + if (_value is decimal) + writer.WriteValue((decimal)_value); + else if (_value is double) + writer.WriteValue((double)_value); + else if (_value is float) + writer.WriteValue((float)_value); else writer.WriteValue(Convert.ToDouble(_value, CultureInfo.InvariantCulture)); return; diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 338439a1..523fda5e 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -90,7 +90,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.5.8.15215")] +[assembly: AssemblyFileVersion("4.5.8.15301")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 2f6fa643..c7ac4a1a 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -968,6 +968,7 @@ namespace Newtonsoft.Json.Serialization { property._required = propertyAttribute._required; property.Order = propertyAttribute._order; + property.DefaultValueHandling = propertyAttribute._defaultValueHandling; hasMemberAttribute = true; } #if !PocketPC && !NET20 @@ -975,6 +976,7 @@ namespace Newtonsoft.Json.Serialization { property._required = (dataMemberAttribute.IsRequired) ? Required.AllowNull : Required.Default; property.Order = (dataMemberAttribute.Order != -1) ? (int?) dataMemberAttribute.Order : null; + property.DefaultValueHandling = (!dataMemberAttribute.EmitDefaultValue) ? (DefaultValueHandling?) DefaultValueHandling.IgnoreAll : null; hasMemberAttribute = true; } #endif @@ -1012,7 +1014,6 @@ namespace Newtonsoft.Json.Serialization property.DefaultValue = (defaultValueAttribute != null) ? defaultValueAttribute.Value : null; property.NullValueHandling = (propertyAttribute != null) ? propertyAttribute._nullValueHandling : null; - property.DefaultValueHandling = (propertyAttribute != null) ? propertyAttribute._defaultValueHandling : null; property.ReferenceLoopHandling = (propertyAttribute != null) ? propertyAttribute._referenceLoopHandling : null; property.ObjectCreationHandling = (propertyAttribute != null) ? propertyAttribute._objectCreationHandling : null; property.TypeNameHandling = (propertyAttribute != null) ? propertyAttribute._typeNameHandling : null; diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index e502cb61..73f3ba7b 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -204,10 +204,16 @@ namespace Newtonsoft.Json.Serialization memberValue == null) return false; - if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Ignore) + DefaultValueHandling defaultValueHandling = property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling); + + if (HasFlag(defaultValueHandling, DefaultValueHandling.Ignore) && MiscellaneousUtils.ValueEquals(memberValue, property.DefaultValue)) return false; + if (defaultValueHandling == DefaultValueHandling.IgnoreAll + && MiscellaneousUtils.ValueEquals(memberValue, property.DefaultValue ?? ReflectionUtils.GetDefaultValue(property.PropertyType))) + return false; + return true; } diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index 950e7336..8b6e4541 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -1015,5 +1015,48 @@ namespace Newtonsoft.Json.Utilities return isMethodOverriden; } + + public static object GetDefaultValue(Type type) + { + if (!type.IsValueType) + return null; + + switch (Type.GetTypeCode(type)) + { + case TypeCode.Boolean: + return false; + case TypeCode.Char: + case TypeCode.SByte: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + return 0; + case TypeCode.Int64: + case TypeCode.UInt64: + return 0L; + case TypeCode.Single: + return 0f; + case TypeCode.Double: + return 0.0; + case TypeCode.Decimal: + return 0m; + case TypeCode.DateTime: + return new DateTime(); + } + + if (type == typeof(Guid)) + return new Guid(); + + if (type == typeof(DateTimeOffset)) + return new DateTimeOffset(); + + if (IsNullable(type)) + return null; + + // possibly use IL initobj for perf here? + return Activator.CreateInstance(type); + } } } \ No newline at end of file