-Added Populate and IgnoreAndPopulate options to DefaultValueHandling for automatically populating default values during deserialization

This commit is contained in:
JamesNK
2011-08-01 02:05:14 +00:00
parent ce2b5e02e0
commit bc726f1b6d
9 changed files with 123 additions and 24 deletions
@@ -631,6 +631,21 @@ namespace Newtonsoft.Json.Tests.Schema
],
""default"": ""Default!""
},
""DefaultValueHandlingPopulateProperty"": {
""required"": true,
""type"": [
""string"",
""null""
],
""default"": ""Default!""
},
""DefaultValueHandlingIgnoreAndPopulateProperty"": {
""type"": [
""string"",
""null""
],
""default"": ""Default!""
},
""NullValueHandlingIgnoreProperty"": {
""type"": [
""string"",
@@ -50,7 +50,7 @@ namespace Newtonsoft.Json.Tests.Serialization
}
[Test]
public void DefaultValueAttributeTest()
public void SerializeDefaultValueAttributeTest()
{
string json = JsonConvert.SerializeObject(new DefaultValueAttributeTestClass(),
Formatting.None, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore });
@@ -69,6 +69,24 @@ namespace Newtonsoft.Json.Tests.Serialization
Assert.AreEqual(@"{}", json);
}
[Test]
public void DeserializeDefaultValueAttributeTest()
{
string json = "{}";
DefaultValueAttributeTestClass c = JsonConvert.DeserializeObject<DefaultValueAttributeTestClass>(json, new JsonSerializerSettings
{
DefaultValueHandling = DefaultValueHandling.Populate
});
Assert.AreEqual("TestProperty1Value", c.TestProperty1);
c = JsonConvert.DeserializeObject<DefaultValueAttributeTestClass>(json, new JsonSerializerSettings
{
DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate
});
Assert.AreEqual("TestProperty1Value", c.TestProperty1);
}
[JsonObject]
public class NetworkUser
{
@@ -1300,16 +1300,19 @@ keyword such as type of business.""
}
[Test]
public void JsonPropertyWithHandlingValues()
public void SerializeJsonPropertyWithHandlingValues()
{
JsonPropertyWithHandlingValues o = new JsonPropertyWithHandlingValues();
o.DefaultValueHandlingIgnoreProperty = "Default!";
o.DefaultValueHandlingIncludeProperty = "Default!";
o.DefaultValueHandlingPopulateProperty = "Default!";
o.DefaultValueHandlingIgnoreAndPopulateProperty = "Default!";
string json = JsonConvert.SerializeObject(o, Formatting.Indented);
Assert.AreEqual(@"{
""DefaultValueHandlingIncludeProperty"": ""Default!"",
""DefaultValueHandlingPopulateProperty"": ""Default!"",
""NullValueHandlingIncludeProperty"": null,
""ReferenceLoopHandlingErrorProperty"": null,
""ReferenceLoopHandlingIgnoreProperty"": null,
@@ -1320,10 +1323,23 @@ keyword such as type of business.""
Assert.AreEqual(@"{
""DefaultValueHandlingIncludeProperty"": ""Default!"",
""DefaultValueHandlingPopulateProperty"": ""Default!"",
""NullValueHandlingIncludeProperty"": null
}", json);
}
[Test]
public void DeserializeJsonPropertyWithHandlingValues()
{
string json = "{}";
JsonPropertyWithHandlingValues o = JsonConvert.DeserializeObject<JsonPropertyWithHandlingValues>(json);
Assert.AreEqual("Default!", o.DefaultValueHandlingIgnoreAndPopulateProperty);
Assert.AreEqual("Default!", o.DefaultValueHandlingPopulateProperty);
Assert.AreEqual(null, o.DefaultValueHandlingIgnoreProperty);
Assert.AreEqual(null, o.DefaultValueHandlingIncludeProperty);
}
[Test]
[ExpectedException(typeof(JsonSerializationException))]
public void JsonPropertyWithHandlingValues_ReferenceLoopError()
@@ -37,6 +37,14 @@ namespace Newtonsoft.Json.Tests.TestObjects
[DefaultValue("Default!")]
public string DefaultValueHandlingIncludeProperty { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[DefaultValue("Default!")]
public string DefaultValueHandlingPopulateProperty { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)]
[DefaultValue("Default!")]
public string DefaultValueHandlingIgnoreAndPopulateProperty { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string NullValueHandlingIgnoreProperty { get; set; }
+15 -3
View File
@@ -33,15 +33,27 @@ namespace Newtonsoft.Json
/// <summary>
/// Specifies default value handling options for the <see cref="JsonSerializer"/>.
/// </summary>
[Flags]
public enum DefaultValueHandling
{
/// <summary>
/// Include default values when serializing and deserializing objects.
/// Include members where the member value is the same as the member's default value when serializing objects.
/// Included members are written to JSON. Has no effect when deserializing.
/// </summary>
Include = 0,
/// <summary>
/// Ignore default values when serializing and deserializing objects.
/// 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.
/// </summary>
Ignore = 1
Ignore = 1,
/// <summary>
/// Members with a default value but no JSON will be set to their default value when deserializing.
/// </summary>
Populate = 2,
/// <summary>
/// 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
}
}
+1 -1
View File
@@ -200,7 +200,7 @@ namespace Newtonsoft.Json
get { return _defaultValueHandling; }
set
{
if (value < DefaultValueHandling.Include || value > DefaultValueHandling.Ignore)
if (value < DefaultValueHandling.Include || value > DefaultValueHandling.IgnoreAndPopulate)
throw new ArgumentOutOfRangeException("value");
_defaultValueHandling = value;
@@ -352,6 +352,11 @@ namespace Newtonsoft.Json.Schema
return type;
}
private bool HasFlag(DefaultValueHandling value, DefaultValueHandling flag)
{
return ((value & flag) == flag);
}
private void GenerateObjectSchema(Type type, JsonObjectContract contract)
{
CurrentSchema.Properties = new Dictionary<string, JsonSchema>();
@@ -360,7 +365,7 @@ namespace Newtonsoft.Json.Schema
if (!property.Ignored)
{
bool optional = property.NullValueHandling == NullValueHandling.Ignore ||
property.DefaultValueHandling == DefaultValueHandling.Ignore ||
HasFlag(property.DefaultValueHandling.GetValueOrDefault(), DefaultValueHandling.Ignore) ||
property.ShouldSerialize != null ||
property.GetIsSpecified != null;
@@ -555,9 +555,9 @@ namespace Newtonsoft.Json.Serialization
}
// 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
if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Ignore)
&& JsonReader.IsPrimitiveToken(reader.TokenType)
&& Equals(reader.Value, property.DefaultValue))
&& MiscellaneousUtils.ValueEquals(reader.Value, property.DefaultValue))
{
reader.Skip();
return;
@@ -579,12 +579,18 @@ namespace Newtonsoft.Json.Serialization
}
}
private bool HasFlag(DefaultValueHandling value, DefaultValueHandling flag)
{
return ((value & flag) == flag);
}
private bool ShouldSetPropertyValue(JsonProperty property, object value)
{
if (property.NullValueHandling.GetValueOrDefault(Serializer.NullValueHandling) == NullValueHandling.Ignore && value == null)
return false;
if (property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling) == DefaultValueHandling.Ignore && MiscellaneousUtils.ValueEquals(value, property.DefaultValue))
if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Ignore)
&& MiscellaneousUtils.ValueEquals(value, property.DefaultValue))
return false;
if (!property.Writable)
@@ -1065,8 +1071,8 @@ namespace Newtonsoft.Json.Serialization
{
contract.InvokeOnDeserializing(newObject, Serializer.Context);
Dictionary<JsonProperty, RequiredValue> requiredProperties =
contract.Properties.Where(m => m.Required != Required.Default).ToDictionary(m => m, m => RequiredValue.None);
Dictionary<JsonProperty, PropertyPresence> propertiesPresence =
contract.Properties.ToDictionary(m => m, m => PropertyPresence.None);
if (id != null)
Serializer.ReferenceResolver.AddReference(this, id, newObject);
@@ -1098,7 +1104,7 @@ namespace Newtonsoft.Json.Serialization
if (!ReadForType(reader, property.PropertyType, property.Converter))
throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName));
SetRequiredProperty(reader, property, requiredProperties);
SetPropertyPresence(reader, property, propertiesPresence);
SetPropertyValue(property, reader, newObject);
}
@@ -1111,12 +1117,26 @@ namespace Newtonsoft.Json.Serialization
}
break;
case JsonToken.EndObject:
foreach (KeyValuePair<JsonProperty, RequiredValue> requiredProperty in requiredProperties)
foreach (KeyValuePair<JsonProperty, PropertyPresence> propertyPresence in propertiesPresence)
{
if (requiredProperty.Value == RequiredValue.None)
throw new JsonSerializationException("Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, requiredProperty.Key.PropertyName));
if (requiredProperty.Key.Required == Required.Always && requiredProperty.Value == RequiredValue.Null)
throw new JsonSerializationException("Required property '{0}' expects a value but got null.".FormatWith(CultureInfo.InvariantCulture, requiredProperty.Key.PropertyName));
JsonProperty property = propertyPresence.Key;
PropertyPresence presence = propertyPresence.Value;
switch (presence)
{
case PropertyPresence.None:
if (property.Required == Required.AllowNull || property.Required == Required.Always)
throw new JsonSerializationException("Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName));
if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Populate)
&& property.Writable)
property.ValueProvider.SetValue(newObject, EnsureType(property.DefaultValue, CultureInfo.InvariantCulture, property.PropertyType));
break;
case PropertyPresence.Null:
if (property.Required == Required.Always)
throw new JsonSerializationException("Required property '{0}' expects a value but got null.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName));
break;
}
}
contract.InvokeOnDeserialized(newObject, Serializer.Context);
@@ -1132,13 +1152,13 @@ namespace Newtonsoft.Json.Serialization
throw new JsonSerializationException("Unexpected end when deserializing object.");
}
private void SetRequiredProperty(JsonReader reader, JsonProperty property, Dictionary<JsonProperty, RequiredValue> requiredProperties)
private void SetPropertyPresence(JsonReader reader, JsonProperty property, Dictionary<JsonProperty, PropertyPresence> requiredProperties)
{
if (property != null)
{
requiredProperties[property] = (reader.TokenType == JsonToken.Null || reader.TokenType == JsonToken.Undefined)
? RequiredValue.Null
: RequiredValue.Value;
? PropertyPresence.Null
: PropertyPresence.Value;
}
}
@@ -1154,7 +1174,7 @@ namespace Newtonsoft.Json.Serialization
}
}
internal enum RequiredValue
internal enum PropertyPresence
{
None,
Null,
@@ -202,8 +202,8 @@ namespace Newtonsoft.Json.Serialization
memberValue == null)
return;
if (property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling) ==
DefaultValueHandling.Ignore && MiscellaneousUtils.ValueEquals(memberValue, defaultValue))
if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Ignore)
&& MiscellaneousUtils.ValueEquals(memberValue, defaultValue))
return;
if (ShouldWriteReference(memberValue, property, contract))
@@ -360,6 +360,11 @@ namespace Newtonsoft.Json.Serialization
writer.WriteValue(ReflectionUtils.GetTypeName(type, Serializer.TypeNameAssemblyFormat, Serializer.Binder));
}
private bool HasFlag(DefaultValueHandling value, DefaultValueHandling flag)
{
return ((value & flag) == flag);
}
private bool HasFlag(PreserveReferencesHandling value, PreserveReferencesHandling flag)
{
return ((value & flag) == flag);