From 811e8d950990cd7def0dee2b81a93e818167bb59 Mon Sep 17 00:00:00 2001 From: Premek Vysoky Date: Tue, 4 May 2021 19:06:12 +0200 Subject: [PATCH 1/9] Add DefaultValuesHandling.OmitDefaultsOrEmpty --- .../Serialization/EmitDefaultValuesTests.cs | 66 ++++++++++++++++++- .../Serialization/DefaultValuesHandling.cs | 5 ++ .../MappingNodeTypeResolver.cs | 5 +- .../DefaultValuesObjectGraphVisitor.cs | 15 +++++ 4 files changed, 87 insertions(+), 4 deletions(-) diff --git a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs index 72bf7a7af..05bf15b13 100644 --- a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs +++ b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs @@ -19,7 +19,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using Xunit; using YamlDotNet.Serialization; @@ -44,6 +46,20 @@ private class Model public int? ANullableNonZeroInteger => 1; [DefaultValue(2)] public int? ANullableNonZeroDefaultInteger => 2; [DefaultValue(2)] public int? ANullableNonZeroNonDefaultInteger => 1; + + // Enumerables + public IList ANullList => null; + + public int[] AnEmptyArray => new int[0]; + public IList AnEmptyList => new List(); + public Dictionary AnEmptyDictionary => new Dictionary(); + public IEnumerable AnEmptyEnumerable => Enumerable.Empty(); + + public string[] AnNonEmptyArray => new[] { "foo", "bar" }; + public IList AnNonEmptyList => new List { 6, 9, 42 }; + public IEnumerable ANonEmptyEnumerable => new[] { true, false }; + public Dictionary ANonEmptyDictionary => new Dictionary() { { "foo", "bar" } }; + } [Fact] @@ -103,12 +119,14 @@ public void Only_null_values_are_omitted_when_DefaultValuesHandling_is_OmitNull( Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); } - [Fact] - public void All_default_values_are_omitted_when_DefaultValuesHandling_is_OmitAll() + [Theory] + [InlineData(DefaultValuesHandling.OmitDefaults)] + [InlineData(DefaultValuesHandling.OmitDefaultsOrEmpty)] + public void All_default_values_are_omitted_when_DefaultValuesHandling_is_OmitDefaults(DefaultValuesHandling defaultValuesHandling) { // Arrange var sut = new SerializerBuilder() - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) + .ConfigureDefaultValuesHandling(defaultValuesHandling) .Build(); // Act @@ -132,6 +150,48 @@ public void All_default_values_are_omitted_when_DefaultValuesHandling_is_OmitAll Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); } + [Fact] + public void Empty_enumerables_are_omitted_when_DefaultValuesHandling_is_OmitDefaultsOrEmpty() + { + // Arrange + var sut = new SerializerBuilder() + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaultsOrEmpty) + .Build(); + + // Act + var yaml = sut.Serialize(new Model()); + + // Assert defaults + Assert.DoesNotContain(nameof(Model.ANullString) + ':', yaml); + Assert.DoesNotContain(nameof(Model.ADefaultString) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultString) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultNullString) + ':', yaml); + + Assert.DoesNotContain(nameof(Model.AZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANonZeroInteger) + ':', yaml); + Assert.DoesNotContain(nameof(Model.ADefaultInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultZeroInteger) + ':', yaml); + + Assert.DoesNotContain(nameof(Model.ANullInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroInteger) + ':', yaml); + Assert.DoesNotContain(nameof(Model.ANullableNonZeroDefaultInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); + + // Assert enumerables + Assert.DoesNotContain(nameof(Model.ANullList) + ':', yaml); + + Assert.DoesNotContain(nameof(Model.AnEmptyArray) + ':', yaml); + Assert.DoesNotContain(nameof(Model.AnEmptyList) + ':', yaml); + Assert.DoesNotContain(nameof(Model.AnEmptyDictionary) + ':', yaml); + Assert.DoesNotContain(nameof(Model.AnEmptyEnumerable) + ':', yaml); + + Assert.Contains(nameof(Model.AnNonEmptyArray) + ':', yaml); + Assert.Contains(nameof(Model.AnNonEmptyList) + ':', yaml); + Assert.Contains(nameof(Model.ANonEmptyEnumerable) + ':', yaml); + Assert.Contains(nameof(Model.ANonEmptyDictionary) + ':', yaml); + } + [Fact] public void YamlMember_overrides_default_value_handling() { diff --git a/YamlDotNet/Serialization/DefaultValuesHandling.cs b/YamlDotNet/Serialization/DefaultValuesHandling.cs index 6af1c1fe8..ca4309fd9 100644 --- a/YamlDotNet/Serialization/DefaultValuesHandling.cs +++ b/YamlDotNet/Serialization/DefaultValuesHandling.cs @@ -40,5 +40,10 @@ public enum DefaultValuesHandling /// Specifies that properties that that contain their default value, either default(T) or the value specified in DefaultValueAttribute are to be omitted. /// OmitDefaults, + + /// + /// More relaxed than OmitDefaults - omits default values and also collections/arrays/enumerations that are empty. + /// + OmitDefaultsOrEmpty, } } diff --git a/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs b/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs index 17960e872..5d974aa7c 100644 --- a/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs +++ b/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs @@ -31,7 +31,10 @@ public class MappingNodeTypeResolver : INodeTypeResolver public MappingNodeTypeResolver(IDictionary mappings) { - if (mappings == null) throw new ArgumentNullException(nameof(mappings)); + if (mappings == null) + { + throw new ArgumentNullException(nameof(mappings)); + } foreach (var pair in mappings) { diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs index b2c2628b1..451dd9fd4 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs @@ -20,6 +20,7 @@ // SOFTWARE. using System; +using System.Collections; using System.ComponentModel; using YamlDotNet.Core; @@ -58,6 +59,20 @@ public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor val } break; + case DefaultValuesHandling.OmitDefaultsOrEmpty: + if (value.Value is null) + { + // Null is default for enumerable so it falls into OmitDefaults + return false; + } + + if (value.Value is IEnumerable enumerable && !enumerable.GetEnumerator().MoveNext()) + { + return false; + } + + goto case DefaultValuesHandling.OmitDefaults; + case DefaultValuesHandling.OmitDefaults: var defaultValue = key.GetCustomAttribute()?.Value ?? GetDefault(key.Type); if (Equals(value.Value, defaultValue)) From f6e6bee549f57e1b95443e5974f8b0c175ec66b2 Mon Sep 17 00:00:00 2001 From: Premek Vysoky Date: Tue, 4 May 2021 19:29:25 +0200 Subject: [PATCH 2/9] Fall through into `OmitDefaults` for non-enumerables --- .../ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs index 451dd9fd4..9b742d51d 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs @@ -60,12 +60,6 @@ public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor val break; case DefaultValuesHandling.OmitDefaultsOrEmpty: - if (value.Value is null) - { - // Null is default for enumerable so it falls into OmitDefaults - return false; - } - if (value.Value is IEnumerable enumerable && !enumerable.GetEnumerator().MoveNext()) { return false; From 6b2465ec428798737e85296a3f318a147790daa3 Mon Sep 17 00:00:00 2001 From: Premek Vysoky Date: Tue, 4 May 2021 21:45:30 +0200 Subject: [PATCH 3/9] Add `OmitNullOrEmpty` --- .../Serialization/EmitDefaultValuesTests.cs | 31 ++++++------------- .../Serialization/DefaultValuesHandling.cs | 5 +++ .../DefaultValuesObjectGraphVisitor.cs | 12 +++++-- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs index 05bf15b13..04957580f 100644 --- a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs +++ b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs @@ -90,12 +90,14 @@ public void All_default_values_and_nulls_are_emitted_when_no_configuration_is_pe Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); } - [Fact] - public void Only_null_values_are_omitted_when_DefaultValuesHandling_is_OmitNull() + [Theory] + [InlineData(DefaultValuesHandling.OmitNull)] + [InlineData(DefaultValuesHandling.OmitNullOrEmpty)] + public void Only_null_values_are_omitted_when_DefaultValuesHandling_is_OmitNull(DefaultValuesHandling defaultValuesHandling) { // Arrange var sut = new SerializerBuilder() - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) + .ConfigureDefaultValuesHandling(defaultValuesHandling) .Build(); // Act @@ -150,8 +152,10 @@ public void All_default_values_are_omitted_when_DefaultValuesHandling_is_OmitDef Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); } - [Fact] - public void Empty_enumerables_are_omitted_when_DefaultValuesHandling_is_OmitDefaultsOrEmpty() + [Theory] + [InlineData(DefaultValuesHandling.OmitNullOrEmpty)] + [InlineData(DefaultValuesHandling.OmitDefaultsOrEmpty)] + public void Empty_enumerables_are_omitted_when_DefaultValuesHandling_is_OmitDefaultsOrEmpty(DefaultValuesHandling defaultValuesHandling) { // Arrange var sut = new SerializerBuilder() @@ -161,23 +165,6 @@ public void Empty_enumerables_are_omitted_when_DefaultValuesHandling_is_OmitDefa // Act var yaml = sut.Serialize(new Model()); - // Assert defaults - Assert.DoesNotContain(nameof(Model.ANullString) + ':', yaml); - Assert.DoesNotContain(nameof(Model.ADefaultString) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultString) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultNullString) + ':', yaml); - - Assert.DoesNotContain(nameof(Model.AZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANonZeroInteger) + ':', yaml); - Assert.DoesNotContain(nameof(Model.ADefaultInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultZeroInteger) + ':', yaml); - - Assert.DoesNotContain(nameof(Model.ANullInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroInteger) + ':', yaml); - Assert.DoesNotContain(nameof(Model.ANullableNonZeroDefaultInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); - // Assert enumerables Assert.DoesNotContain(nameof(Model.ANullList) + ':', yaml); diff --git a/YamlDotNet/Serialization/DefaultValuesHandling.cs b/YamlDotNet/Serialization/DefaultValuesHandling.cs index ca4309fd9..10b1fba04 100644 --- a/YamlDotNet/Serialization/DefaultValuesHandling.cs +++ b/YamlDotNet/Serialization/DefaultValuesHandling.cs @@ -36,6 +36,11 @@ public enum DefaultValuesHandling /// OmitNull, + /// + /// More relaxed than OmitNull - omits null values and also collections/arrays/enumerations that are empty. + /// + OmitNullOrEmpty, + /// /// Specifies that properties that that contain their default value, either default(T) or the value specified in DefaultValueAttribute are to be omitted. /// diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs index 9b742d51d..a063e7e87 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs @@ -59,13 +59,13 @@ public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor val } break; - case DefaultValuesHandling.OmitDefaultsOrEmpty: + case DefaultValuesHandling.OmitNullOrEmpty: if (value.Value is IEnumerable enumerable && !enumerable.GetEnumerator().MoveNext()) { return false; } - goto case DefaultValuesHandling.OmitDefaults; + goto case DefaultValuesHandling.OmitNull; case DefaultValuesHandling.OmitDefaults: var defaultValue = key.GetCustomAttribute()?.Value ?? GetDefault(key.Type); @@ -74,6 +74,14 @@ public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor val return false; } break; + + case DefaultValuesHandling.OmitDefaultsOrEmpty: + if (value.Value is IEnumerable ienumerable && !ienumerable.GetEnumerator().MoveNext()) + { + return false; + } + + goto case DefaultValuesHandling.OmitDefaults; } return base.EnterMapping(key, value, context); From 39a946f358d5de7ee3478ef537ec581208faff87 Mon Sep 17 00:00:00 2001 From: Premek Vysoky Date: Sun, 30 May 2021 21:13:00 +0200 Subject: [PATCH 4/9] Revert unnecessary change --- .../NodeTypeResolvers/MappingNodeTypeResolver.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs b/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs index 5d974aa7c..17960e872 100644 --- a/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs +++ b/YamlDotNet/Serialization/NodeTypeResolvers/MappingNodeTypeResolver.cs @@ -31,10 +31,7 @@ public class MappingNodeTypeResolver : INodeTypeResolver public MappingNodeTypeResolver(IDictionary mappings) { - if (mappings == null) - { - throw new ArgumentNullException(nameof(mappings)); - } + if (mappings == null) throw new ArgumentNullException(nameof(mappings)); foreach (var pair in mappings) { From 53b3fecdf08827bbf0eb5e5558021918fcb8f29e Mon Sep 17 00:00:00 2001 From: Premek Vysoky Date: Sun, 30 May 2021 21:21:09 +0200 Subject: [PATCH 5/9] Make DefaultValuesHandling flags --- .../Serialization/EmitDefaultValuesTests.cs | 58 +++---------------- .../Serialization/DefaultValuesHandling.cs | 18 +++--- .../DefaultValuesObjectGraphVisitor.cs | 49 ++++++++-------- 3 files changed, 41 insertions(+), 84 deletions(-) diff --git a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs index 04957580f..28be2d42f 100644 --- a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs +++ b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs @@ -59,45 +59,14 @@ private class Model public IList AnNonEmptyList => new List { 6, 9, 42 }; public IEnumerable ANonEmptyEnumerable => new[] { true, false }; public Dictionary ANonEmptyDictionary => new Dictionary() { { "foo", "bar" } }; - } [Fact] - public void All_default_values_and_nulls_are_emitted_when_no_configuration_is_performed() + public void Only_null_values_are_omitted_when_DefaultValuesHandling_is_OmitNull() { // Arrange var sut = new SerializerBuilder() - .Build(); - - // Act - var yaml = sut.Serialize(new Model()); - - // Assert - Assert.Contains(nameof(Model.ANullString) + ':', yaml); - Assert.Contains(nameof(Model.ADefaultString) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultString) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultNullString) + ':', yaml); - - Assert.Contains(nameof(Model.AZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANonZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ADefaultInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANonDefaultZeroInteger) + ':', yaml); - - Assert.Contains(nameof(Model.ANullInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroDefaultInteger) + ':', yaml); - Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); - } - - [Theory] - [InlineData(DefaultValuesHandling.OmitNull)] - [InlineData(DefaultValuesHandling.OmitNullOrEmpty)] - public void Only_null_values_are_omitted_when_DefaultValuesHandling_is_OmitNull(DefaultValuesHandling defaultValuesHandling) - { - // Arrange - var sut = new SerializerBuilder() - .ConfigureDefaultValuesHandling(defaultValuesHandling) + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) .Build(); // Act @@ -121,14 +90,12 @@ public void Only_null_values_are_omitted_when_DefaultValuesHandling_is_OmitNull( Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); } - [Theory] - [InlineData(DefaultValuesHandling.OmitDefaults)] - [InlineData(DefaultValuesHandling.OmitDefaultsOrEmpty)] - public void All_default_values_are_omitted_when_DefaultValuesHandling_is_OmitDefaults(DefaultValuesHandling defaultValuesHandling) + [Fact] + public void All_default_values_are_omitted_when_DefaultValuesHandling_is_OmitAll() { // Arrange var sut = new SerializerBuilder() - .ConfigureDefaultValuesHandling(defaultValuesHandling) + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) .Build(); // Act @@ -152,31 +119,22 @@ public void All_default_values_are_omitted_when_DefaultValuesHandling_is_OmitDef Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); } - [Theory] - [InlineData(DefaultValuesHandling.OmitNullOrEmpty)] - [InlineData(DefaultValuesHandling.OmitDefaultsOrEmpty)] - public void Empty_enumerables_are_omitted_when_DefaultValuesHandling_is_OmitDefaultsOrEmpty(DefaultValuesHandling defaultValuesHandling) + [Fact] + public void Empty_enumerables_are_omitted_when_DefaultValuesHandling_is_OmitEmpty() { // Arrange var sut = new SerializerBuilder() - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaultsOrEmpty) + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitEmpty) .Build(); // Act var yaml = sut.Serialize(new Model()); // Assert enumerables - Assert.DoesNotContain(nameof(Model.ANullList) + ':', yaml); - Assert.DoesNotContain(nameof(Model.AnEmptyArray) + ':', yaml); Assert.DoesNotContain(nameof(Model.AnEmptyList) + ':', yaml); Assert.DoesNotContain(nameof(Model.AnEmptyDictionary) + ':', yaml); Assert.DoesNotContain(nameof(Model.AnEmptyEnumerable) + ':', yaml); - - Assert.Contains(nameof(Model.AnNonEmptyArray) + ':', yaml); - Assert.Contains(nameof(Model.AnNonEmptyList) + ':', yaml); - Assert.Contains(nameof(Model.ANonEmptyEnumerable) + ':', yaml); - Assert.Contains(nameof(Model.ANonEmptyDictionary) + ':', yaml); } [Fact] diff --git a/YamlDotNet/Serialization/DefaultValuesHandling.cs b/YamlDotNet/Serialization/DefaultValuesHandling.cs index 10b1fba04..7d49123b2 100644 --- a/YamlDotNet/Serialization/DefaultValuesHandling.cs +++ b/YamlDotNet/Serialization/DefaultValuesHandling.cs @@ -19,36 +19,34 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +using System; + namespace YamlDotNet.Serialization { /// /// Specifies the strategy to handle default and null values during serialization of properties. /// + [Flags] public enum DefaultValuesHandling { /// /// Specifies that all properties are to be emitted regardless of their value. This is the default behavior. /// - Preserve, + Preserve = 0, /// /// Specifies that properties that contain null references or a null Nullable<T> are to be omitted. /// - OmitNull, + OmitNull = 1, /// - /// More relaxed than OmitNull - omits null values and also collections/arrays/enumerations that are empty. + /// Omits collections/arrays/enumerations that are empty. /// - OmitNullOrEmpty, + OmitEmpty = 2, /// /// Specifies that properties that that contain their default value, either default(T) or the value specified in DefaultValueAttribute are to be omitted. /// - OmitDefaults, - - /// - /// More relaxed than OmitDefaults - omits default values and also collections/arrays/enumerations that are empty. - /// - OmitDefaultsOrEmpty, + OmitDefaults = 4, } } diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs index a063e7e87..12f0dde2e 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs @@ -43,45 +43,46 @@ public DefaultValuesObjectGraphVisitor(DefaultValuesHandling handling, IObjectGr public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor value, IEmitter context) { - var configuration = this.handling; + var configuration = handling; var yamlMember = key.GetCustomAttribute(); if (yamlMember != null && yamlMember.IsDefaultValuesHandlingSpecified) { configuration = yamlMember.DefaultValuesHandling; } - switch (configuration) + if ((configuration & DefaultValuesHandling.OmitNull) != 0) { - case DefaultValuesHandling.OmitNull: - if (value.Value is null) - { - return false; - } - break; - - case DefaultValuesHandling.OmitNullOrEmpty: - if (value.Value is IEnumerable enumerable && !enumerable.GetEnumerator().MoveNext()) - { - return false; - } - - goto case DefaultValuesHandling.OmitNull; + if (value.Value is null) + { + return false; + } + } - case DefaultValuesHandling.OmitDefaults: - var defaultValue = key.GetCustomAttribute()?.Value ?? GetDefault(key.Type); - if (Equals(value.Value, defaultValue)) + if ((configuration & DefaultValuesHandling.OmitEmpty) != 0) + { + if (value.Value is IEnumerable enumerable) + { + var enumerator = enumerable.GetEnumerator(); + var canMoveNext = enumerator.MoveNext(); + if (enumerator is IDisposable disposable) { - return false; + disposable.Dispose(); } - break; - case DefaultValuesHandling.OmitDefaultsOrEmpty: - if (value.Value is IEnumerable ienumerable && !ienumerable.GetEnumerator().MoveNext()) + if (!canMoveNext) { return false; } + } + } - goto case DefaultValuesHandling.OmitDefaults; + if ((configuration & DefaultValuesHandling.OmitDefaults) != 0) + { + var defaultValue = key.GetCustomAttribute()?.Value ?? GetDefault(key.Type); + if (Equals(value.Value, defaultValue)) + { + return false; + } } return base.EnterMapping(key, value, context); From ca109ec82aaee20d2ae0024a6c1a212e4e0db326 Mon Sep 17 00:00:00 2001 From: Premek Vysoky Date: Sun, 30 May 2021 21:24:25 +0200 Subject: [PATCH 6/9] Fix naming and docs --- .../Serialization/EmitDefaultValuesTests.cs | 2 +- YamlDotNet/Serialization/DefaultValuesHandling.cs | 10 +++++----- .../DefaultValuesObjectGraphVisitor.cs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs index 28be2d42f..518172b23 100644 --- a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs +++ b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs @@ -124,7 +124,7 @@ public void Empty_enumerables_are_omitted_when_DefaultValuesHandling_is_OmitEmpt { // Arrange var sut = new SerializerBuilder() - .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitEmpty) + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitEmptyCollections) .Build(); // Act diff --git a/YamlDotNet/Serialization/DefaultValuesHandling.cs b/YamlDotNet/Serialization/DefaultValuesHandling.cs index 7d49123b2..176a45bd0 100644 --- a/YamlDotNet/Serialization/DefaultValuesHandling.cs +++ b/YamlDotNet/Serialization/DefaultValuesHandling.cs @@ -35,18 +35,18 @@ public enum DefaultValuesHandling Preserve = 0, /// - /// Specifies that properties that contain null references or a null Nullable<T> are to be omitted. + /// Specifies that properties that contain null references or a null Nullable<T> are to be omitted. /// OmitNull = 1, /// - /// Omits collections/arrays/enumerations that are empty. + /// Specifies that properties that that contain their default value, either default(T) or the value specified in DefaultValueAttribute are to be omitted. /// - OmitEmpty = 2, + OmitDefaults = 2, /// - /// Specifies that properties that that contain their default value, either default(T) or the value specified in DefaultValueAttribute are to be omitted. + /// Specifies that properties that that contain collections/arrays/enumerations that are empty are to be omitted. /// - OmitDefaults = 4, + OmitEmptyCollections = 4, } } diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs index 12f0dde2e..7e478fa2a 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/DefaultValuesObjectGraphVisitor.cs @@ -58,7 +58,7 @@ public override bool EnterMapping(IPropertyDescriptor key, IObjectDescriptor val } } - if ((configuration & DefaultValuesHandling.OmitEmpty) != 0) + if ((configuration & DefaultValuesHandling.OmitEmptyCollections) != 0) { if (value.Value is IEnumerable enumerable) { From b71fbf21df6b04823bd2dd44e36c9c22a4f5ea90 Mon Sep 17 00:00:00 2001 From: Premek Vysoky Date: Sun, 30 May 2021 21:25:48 +0200 Subject: [PATCH 7/9] Add removed test --- .../Serialization/EmitDefaultValuesTests.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs index 518172b23..86e12e00a 100644 --- a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs +++ b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs @@ -61,6 +61,34 @@ private class Model public Dictionary ANonEmptyDictionary => new Dictionary() { { "foo", "bar" } }; } + [Fact] + public void All_default_values_and_nulls_are_emitted_when_no_configuration_is_performed() + { + // Arrange + var sut = new SerializerBuilder() + .Build(); + + // Act + var yaml = sut.Serialize(new Model()); + + // Assert + Assert.Contains(nameof(Model.ANullString) + ':', yaml); + Assert.Contains(nameof(Model.ADefaultString) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultString) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultNullString) + ':', yaml); + + Assert.Contains(nameof(Model.AZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANonZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ADefaultInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANonDefaultZeroInteger) + ':', yaml); + + Assert.Contains(nameof(Model.ANullInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroDefaultInteger) + ':', yaml); + Assert.Contains(nameof(Model.ANullableNonZeroNonDefaultInteger) + ':', yaml); + } + [Fact] public void Only_null_values_are_omitted_when_DefaultValuesHandling_is_OmitNull() { From 95bca095b03c1258d0f27dd5bab9aa24b6ba22eb Mon Sep 17 00:00:00 2001 From: Premek Vysoky Date: Sun, 30 May 2021 22:56:43 +0200 Subject: [PATCH 8/9] Test presence of non empty collections --- YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs index 86e12e00a..26079eff8 100644 --- a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs +++ b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs @@ -55,8 +55,8 @@ private class Model public Dictionary AnEmptyDictionary => new Dictionary(); public IEnumerable AnEmptyEnumerable => Enumerable.Empty(); - public string[] AnNonEmptyArray => new[] { "foo", "bar" }; - public IList AnNonEmptyList => new List { 6, 9, 42 }; + public string[] ANonEmptyArray => new[] { "foo", "bar" }; + public IList ANonEmptyList => new List { 6, 9, 42 }; public IEnumerable ANonEmptyEnumerable => new[] { true, false }; public Dictionary ANonEmptyDictionary => new Dictionary() { { "foo", "bar" } }; } @@ -163,6 +163,11 @@ public void Empty_enumerables_are_omitted_when_DefaultValuesHandling_is_OmitEmpt Assert.DoesNotContain(nameof(Model.AnEmptyList) + ':', yaml); Assert.DoesNotContain(nameof(Model.AnEmptyDictionary) + ':', yaml); Assert.DoesNotContain(nameof(Model.AnEmptyEnumerable) + ':', yaml); + + Assert.Contains(nameof(Model.ANonEmptyArray) + ':', yaml); + Assert.Contains(nameof(Model.ANonEmptyList) + ':', yaml); + Assert.Contains(nameof(Model.ANonEmptyDictionary) + ':', yaml); + Assert.Contains(nameof(Model.ANonEmptyEnumerable) + ':', yaml); } [Fact] From 173fe4422472455508d000a92b74362e3bf43a60 Mon Sep 17 00:00:00 2001 From: Premek Vysoky Date: Sun, 30 May 2021 22:57:43 +0200 Subject: [PATCH 9/9] Remove null list --- YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs index 26079eff8..a74cad3d2 100644 --- a/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs +++ b/YamlDotNet.Test/Serialization/EmitDefaultValuesTests.cs @@ -48,8 +48,6 @@ private class Model [DefaultValue(2)] public int? ANullableNonZeroNonDefaultInteger => 1; // Enumerables - public IList ANullList => null; - public int[] AnEmptyArray => new int[0]; public IList AnEmptyList => new List(); public Dictionary AnEmptyDictionary => new Dictionary();