-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
This commit is contained in:
James Newton-King
2012-09-01 13:34:07 +12:00
parent 028c287cff
commit d9c8d78d77
20 changed files with 312 additions and 39 deletions
@@ -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.",
@@ -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);
}
}
@@ -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<JToken> 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);
}
}
}
@@ -1846,5 +1846,24 @@ Parameter name: arrayIndex",
Assert.IsFalse(o1.DeepEquals(null));
}
[Test]
public void ToListOnEmptyObject()
{
JObject o = JObject.Parse(@"{}");
IList<JToken> l1 = o.ToList<JToken>();
Assert.AreEqual(0, l1.Count);
IList<KeyValuePair<string, JToken>> l2 = o.ToList<KeyValuePair<string, JToken>>();
Assert.AreEqual(0, l2.Count);
o = JObject.Parse(@"{'hi':null}");
l1 = o.ToList<JToken>();
Assert.AreEqual(1, l1.Count);
l2 = o.ToList<KeyValuePair<string, JToken>>();
Assert.AreEqual(1, l2.Count);
}
}
}
@@ -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<Rate>(new JTokenReader(jTokenWriter.Token));
Assert.AreEqual(rate.Compoundings, rate2.Compoundings);
}
}
}
@@ -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
@@ -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
}
+13 -2
View File
@@ -24,6 +24,7 @@
#endregion
using System;
using System.ComponentModel;
namespace Newtonsoft.Json
{
@@ -44,7 +45,9 @@ namespace Newtonsoft.Json
Include = 0,
/// <summary>
/// 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 <c>null</c> values for object and nullable types
/// unless the property explicitly has a <see cref="DefaultValueAttribute"/> specified. To ignore default values
/// for all types use the <c>IgnoreAll</c> option.
/// </summary>
Ignore = 1,
/// <summary>
@@ -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.
/// </summary>
IgnoreAndPopulate = Ignore | Populate
IgnoreAndPopulate = Ignore | Populate,
/// <summary>
/// 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. <c>null</c> for objects and nullable typesl; <c>0</c> for integers,
/// decimals and floating point numbers; and <c>false</c> for booleans). The default value ignored can be override by
/// the <see cref="DefaultValueAttribute"/>.
/// </summary>
IgnoreAll = 4
}
}
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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.");
+1 -1
View File
@@ -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.");
+6 -2
View File
@@ -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;
@@ -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)]
@@ -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;
@@ -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;
}
@@ -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);
}
}
}