-Added Populate and IgnoreAndPopulate options to DefaultValueHandling for automatically populating default values during deserialization
This commit is contained in:
@@ -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; }
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user