From f5ed7be283f38c6e175b23d20b951cea1fa75df8 Mon Sep 17 00:00:00 2001 From: JamesNK Date: Thu, 27 May 2010 08:37:34 +0000 Subject: [PATCH] -Improved default null and value handing when JSON value is not compatible with property value -Added interfaces, structs and enums as valid targets of JsonConverterAttribute --- .../Serialization/NullValueHandlingTests.cs | 14 ++++++++++++++ .../TestObjects/DateTimeTestClass.cs | 3 +++ Src/Newtonsoft.Json/Bson/BsonReader.cs | 2 ++ Src/Newtonsoft.Json/DefaultValueHandling.cs | 4 ++-- Src/Newtonsoft.Json/JsonReader.cs | 18 ++++++++++++++++++ .../Serialization/DefaultContractResolver.cs | 6 ++++-- .../JsonSerializerInternalReader.cs | 16 ++++++++++++++++ 7 files changed, 59 insertions(+), 4 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Serialization/NullValueHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/NullValueHandlingTests.cs index cf55e795..2c87e714 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/NullValueHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/NullValueHandlingTests.cs @@ -35,6 +35,20 @@ namespace Newtonsoft.Json.Tests.Serialization { public class NullValueHandlingTests : TestFixtureBase { + [Test] + public void DeserializeNullIntoDateTime() + { + DateTimeTestClass c = JsonConvert.DeserializeObject(@"{DateTimeField:null}", new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + Assert.AreEqual(c.DateTimeField, default(DateTime)); + } + + [Test] + public void DeserializeEmptyStringIntoDateTimeWithEmptyStringDefaultValue() + { + DateTimeTestClass c = JsonConvert.DeserializeObject(@"{DateTimeField:""""}", new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + Assert.AreEqual(c.DateTimeField, default(DateTime)); + } + [Test] public void NullValueHandlingSerialization() { diff --git a/Src/Newtonsoft.Json.Tests/TestObjects/DateTimeTestClass.cs b/Src/Newtonsoft.Json.Tests/TestObjects/DateTimeTestClass.cs index c51d55ad..233879f4 100644 --- a/Src/Newtonsoft.Json.Tests/TestObjects/DateTimeTestClass.cs +++ b/Src/Newtonsoft.Json.Tests/TestObjects/DateTimeTestClass.cs @@ -26,6 +26,7 @@ #if !PocketPC && !NET20 using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; @@ -34,6 +35,8 @@ namespace Newtonsoft.Json.Tests.TestObjects public class DateTimeTestClass { public string PreField { get; set; } + [DefaultValue("")] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] public DateTime DateTimeField { get; set; } public DateTimeOffset DateTimeOffsetField { get; set; } public string PostField { get; set; } diff --git a/Src/Newtonsoft.Json/Bson/BsonReader.cs b/Src/Newtonsoft.Json/Bson/BsonReader.cs index 852fb20e..9f25b71d 100644 --- a/Src/Newtonsoft.Json/Bson/BsonReader.cs +++ b/Src/Newtonsoft.Json/Bson/BsonReader.cs @@ -575,6 +575,8 @@ namespace Newtonsoft.Json.Bson int offset = 0; do { + // read up to the maximum size of the buffer or what is remaining, minus the offset + // of left over multibyte chars int count = ((length - totalBytesRead) > MaxCharBytesSize - offset) ? MaxCharBytesSize - offset : length - totalBytesRead - offset; diff --git a/Src/Newtonsoft.Json/DefaultValueHandling.cs b/Src/Newtonsoft.Json/DefaultValueHandling.cs index 6e395d70..570d6843 100644 --- a/Src/Newtonsoft.Json/DefaultValueHandling.cs +++ b/Src/Newtonsoft.Json/DefaultValueHandling.cs @@ -36,11 +36,11 @@ namespace Newtonsoft.Json public enum DefaultValueHandling { /// - /// Include null values when serializing and deserializing objects. + /// Include default values when serializing and deserializing objects. /// Include = 0, /// - /// Ignore null values when serializing and deserializing objects. + /// Ignore default values when serializing and deserializing objects. /// Ignore = 1 } diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index 560e194d..d5165785 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -336,6 +336,24 @@ namespace Newtonsoft.Json } } + internal static bool IsPrimitiveToken(JsonToken token) + { + switch (token) + { + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.String: + case JsonToken.Boolean: + case JsonToken.Undefined: + case JsonToken.Null: + case JsonToken.Date: + case JsonToken.Bytes: + return true; + default: + return false; + } + } + internal static bool IsStartToken(JsonToken token) { switch (token) diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index ccf41511..d44940a6 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -125,8 +125,10 @@ namespace Newtonsoft.Json.Serialization /// Initializes a new instance of the class. /// /// - /// if set to true the will use a cached shared with other resolvers of the same type. - /// This will improve significantly performance for unshared resolvers but cause unexpected behavior if instances of the resolver could produce different results. + /// If set to true the will use a cached shared with other resolvers of the same type. + /// Sharing the cache will significantly performance because expensive reflection will only happen once but could cause unexpected + /// behavior if different instances of the resolver are suppose to produce different results. When set to false it is highly + /// recommended to reuse instances with the . /// public DefaultContractResolver(bool shareCache) { diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index b2003e7d..57410b28 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -510,6 +510,22 @@ namespace Newtonsoft.Json.Serialization return; } + // test tokentype here because null might not be convertable to some types, e.g. ignoring null when applied to DateTime + if (property.NullValueHandling.GetValueOrDefault(Serializer.NullValueHandling) == NullValueHandling.Ignore && reader.TokenType == JsonToken.Null) + { + reader.Skip(); + return; + } + + // test tokentype here because default value might not be convertable to actual type, e.g. default of "" for DateTime + if (property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling) == DefaultValueHandling.Ignore + && JsonReader.IsPrimitiveToken(reader.TokenType) + && Equals(reader.Value, property.DefaultValue)) + { + reader.Skip(); + return; + } + object existingValue = (useExistingValue) ? currentValue : null; object value = CreateValueProperty(reader, property, target, gottenCurrentValue, existingValue);