public static JsonPropertyInfo AddProperty(Type propertyType, PropertyInfo propertyInfo, Type parentClassType, JsonSerializerOptions options)
{
- JsonIgnoreAttribute? ignoreAttribute = JsonPropertyInfo.GetAttribute<JsonIgnoreAttribute>(propertyInfo);
- if (ignoreAttribute?.Condition == JsonIgnoreCondition.Always)
+ JsonIgnoreCondition? ignoreCondition = JsonPropertyInfo.GetAttribute<JsonIgnoreAttribute>(propertyInfo)?.Condition;
+
+ if (ignoreCondition == JsonIgnoreCondition.Always)
{
return JsonPropertyInfo.CreateIgnoredPropertyPlaceholder(propertyInfo, options);
}
propertyInfo,
parentClassType,
converter,
- options);
+ options,
+ ignoreCondition);
}
internal static JsonPropertyInfo CreateProperty(
PropertyInfo? propertyInfo,
Type parentClassType,
JsonConverter converter,
- JsonSerializerOptions options)
+ JsonSerializerOptions options,
+ JsonIgnoreCondition? ignoreCondition = null)
{
// Create the JsonPropertyInfo instance.
JsonPropertyInfo jsonPropertyInfo = converter.CreateJsonPropertyInfo();
runtimeClassType: converter.ClassType,
propertyInfo,
converter,
+ ignoreCondition,
options);
return jsonPropertyInfo;
PropertyNameKey = key;
}
- private void DetermineSerializationCapabilities()
+ private void DetermineSerializationCapabilities(JsonIgnoreCondition? ignoreCondition)
{
if ((ClassType & (ClassType.Enumerable | ClassType.Dictionary)) == 0)
{
+ Debug.Assert(ignoreCondition != JsonIgnoreCondition.Always);
+
+ // Three possible values for ignoreCondition:
+ // null = JsonIgnore was not placed on this property, global IgnoreReadOnlyProperties wins
+ // WhenNull = only ignore when null, global IgnoreReadOnlyProperties loses
+ // Never = never ignore (always include), global IgnoreReadOnlyProperties loses
+ bool serializeReadOnlyProperty = ignoreCondition != null || !Options.IgnoreReadOnlyProperties;
+
// We serialize if there is a getter + not ignoring readonly properties.
- ShouldSerialize = HasGetter && (HasSetter || !Options.IgnoreReadOnlyProperties);
+ ShouldSerialize = HasGetter && (HasSetter || serializeReadOnlyProperty);
// We deserialize if there is a setter.
ShouldDeserialize = HasSetter;
}
}
- private void DetermineIgnoreCondition()
+ private void DetermineIgnoreCondition(JsonIgnoreCondition? ignoreCondition)
{
- JsonIgnoreAttribute? ignoreAttribute;
- if (PropertyInfo != null && (ignoreAttribute = GetAttribute<JsonIgnoreAttribute>(PropertyInfo)) != null)
+ if (ignoreCondition != null)
{
- JsonIgnoreCondition condition = ignoreAttribute.Condition;
-
- // We should have created a placeholder property for this upstream and shouldn't be down this code-path.
- Debug.Assert(condition != JsonIgnoreCondition.Always);
+ Debug.Assert(PropertyInfo != null);
+ Debug.Assert(ignoreCondition != JsonIgnoreCondition.Always);
- if (condition != JsonIgnoreCondition.Never)
+ if (ignoreCondition != JsonIgnoreCondition.Never)
{
- Debug.Assert(condition == JsonIgnoreCondition.WhenNull);
+ Debug.Assert(ignoreCondition == JsonIgnoreCondition.WhenNull);
IgnoreNullValues = true;
}
}
public abstract bool GetMemberAndWriteJson(object obj, ref WriteStack state, Utf8JsonWriter writer);
public abstract bool GetMemberAndWriteJsonExtensionData(object obj, ref WriteStack state, Utf8JsonWriter writer);
- public virtual void GetPolicies()
+ public virtual void GetPolicies(JsonIgnoreCondition? ignoreCondition)
{
- DetermineSerializationCapabilities();
+ DetermineSerializationCapabilities(ignoreCondition);
DeterminePropertyName();
- DetermineIgnoreCondition();
+ DetermineIgnoreCondition(ignoreCondition);
}
public abstract object? GetValueAsObject(object obj);
ClassType runtimeClassType,
PropertyInfo? propertyInfo,
JsonConverter converter,
+ JsonIgnoreCondition? ignoreCondition,
JsonSerializerOptions options)
{
Debug.Assert(converter != null);
ClassType runtimeClassType,
PropertyInfo? propertyInfo,
JsonConverter converter,
+ JsonIgnoreCondition? ignoreCondition,
JsonSerializerOptions options)
{
base.Initialize(
runtimeClassType,
propertyInfo,
converter,
+ ignoreCondition,
options);
if (propertyInfo != null)
HasSetter = true;
}
- GetPolicies();
+ GetPolicies(ignoreCondition);
}
public override JsonConverter ConverterBase
{
public int Int1 { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenNull)]
- public int MyInt { get; set; }
+ public int MyInt { get; set; }
public int Int2 { get; set; }
}
{
string json = @"{""MyString"":""Random"",""MYSTRING"":null}";
- var options = new JsonSerializerOptions {
+ var options = new JsonSerializerOptions
+ {
IgnoreNullValues = true,
PropertyNameCaseInsensitive = true
};
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public Dictionary<string, string> Dictionary { get; set; } = new Dictionary<string, string> { ["Key"] = "Value" };
}
+
+ [Fact]
+ public static void IgnoreConditionNever_WinsOver_IgnoreReadOnlyValues()
+ {
+ var options = new JsonSerializerOptions { IgnoreReadOnlyProperties = true };
+
+ // Baseline
+ string json = JsonSerializer.Serialize(new ClassWithReadOnlyString("Hello"), options);
+ Assert.Equal("{}", json);
+
+ // With condition to never ignore
+ json = JsonSerializer.Serialize(new ClassWithReadOnlyString_IgnoreNever("Hello"), options);
+ Assert.Equal(@"{""MyString"":""Hello""}", json);
+
+ json = JsonSerializer.Serialize(new ClassWithReadOnlyString_IgnoreNever(null), options);
+ Assert.Equal(@"{""MyString"":null}", json);
+ }
+
+ [Fact]
+ public static void IgnoreConditionWhenNull_WinsOver_IgnoreReadOnlyValues()
+ {
+ var options = new JsonSerializerOptions { IgnoreReadOnlyProperties = true };
+
+ // Baseline
+ string json = JsonSerializer.Serialize(new ClassWithReadOnlyString("Hello"), options);
+ Assert.Equal("{}", json);
+
+ // With condition to ignore when null
+ json = JsonSerializer.Serialize(new ClassWithReadOnlyString_IgnoreWhenNull("Hello"), options);
+ Assert.Equal(@"{""MyString"":""Hello""}", json);
+
+ json = JsonSerializer.Serialize(new ClassWithReadOnlyString_IgnoreWhenNull(null), options);
+ Assert.Equal(@"{}", json);
+ }
+
+ private class ClassWithReadOnlyString
+ {
+ public string MyString { get; }
+
+ public ClassWithReadOnlyString(string myString) => MyString = myString;
+ }
+
+ private class ClassWithReadOnlyString_IgnoreNever
+ {
+ [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
+ public string MyString { get; }
+
+ public ClassWithReadOnlyString_IgnoreNever(string myString) => MyString = myString;
+ }
+
+ private class ClassWithReadOnlyString_IgnoreWhenNull
+ {
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenNull)]
+ public string MyString { get; }
+
+ public ClassWithReadOnlyString_IgnoreWhenNull(string myString) => MyString = myString;
+ }
}
}