From 811e8d950990cd7def0dee2b81a93e818167bb59 Mon Sep 17 00:00:00 2001
From: Premek Vysoky <premek.vysoky@microsoft.com>
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<int> ANullList => null;
+
+            public int[] AnEmptyArray => new int[0];
+            public IList<int> AnEmptyList => new List<int>();
+            public Dictionary<string, string> AnEmptyDictionary => new Dictionary<string, string>();
+            public IEnumerable<int> AnEmptyEnumerable => Enumerable.Empty<int>();
+
+            public string[] AnNonEmptyArray => new[] { "foo", "bar" };
+            public IList<int> AnNonEmptyList => new List<int> { 6, 9, 42 };
+            public IEnumerable<bool> ANonEmptyEnumerable => new[] { true, false };
+            public Dictionary<string, string> ANonEmptyDictionary => new Dictionary<string, string>() { { "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. 
         /// </summary>
         OmitDefaults,
+
+        /// <summary>
+        /// More relaxed than OmitDefaults - omits default values and also collections/arrays/enumerations that are empty.
+        /// </summary>
+        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<Type, Type> 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<DefaultValueAttribute>()?.Value ?? GetDefault(key.Type);
                     if (Equals(value.Value, defaultValue))

From f6e6bee549f57e1b95443e5974f8b0c175ec66b2 Mon Sep 17 00:00:00 2001
From: Premek Vysoky <premek.vysoky@microsoft.com>
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 <premek.vysoky@microsoft.com>
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
         /// </summary>
         OmitNull,
 
+        /// <summary>
+        /// More relaxed than OmitNull - omits null values and also collections/arrays/enumerations that are empty.
+        /// </summary>
+        OmitNullOrEmpty,
+
         /// <summary>
         /// Specifies that properties that that contain their default value, either default(T) or the value specified in DefaultValueAttribute are to be omitted. 
         /// </summary>
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<DefaultValueAttribute>()?.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 <premek.vysoky@microsoft.com>
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<Type, Type> 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 <premek.vysoky@microsoft.com>
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<int> AnNonEmptyList => new List<int> { 6, 9, 42 };
             public IEnumerable<bool> ANonEmptyEnumerable => new[] { true, false };
             public Dictionary<string, string> ANonEmptyDictionary => new Dictionary<string, string>() { { "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
 {
     /// <summary>
     /// Specifies the strategy to handle default and null values during serialization of properties.
     /// </summary>
+    [Flags]
     public enum DefaultValuesHandling
     {
         /// <summary>
         /// Specifies that all properties are to be emitted regardless of their value. This is the default behavior.
         /// </summary>
-        Preserve,
+        Preserve = 0,
 
         /// <summary>
         /// Specifies that properties that contain null references or a null Nullable&lt;T&gt; are to be omitted. 
         /// </summary>
-        OmitNull,
+        OmitNull = 1,
 
         /// <summary>
-        /// More relaxed than OmitNull - omits null values and also collections/arrays/enumerations that are empty.
+        /// Omits collections/arrays/enumerations that are empty.
         /// </summary>
-        OmitNullOrEmpty,
+        OmitEmpty = 2,
 
         /// <summary>
         /// Specifies that properties that that contain their default value, either default(T) or the value specified in DefaultValueAttribute are to be omitted. 
         /// </summary>
-        OmitDefaults,
-
-        /// <summary>
-        /// More relaxed than OmitDefaults - omits default values and also collections/arrays/enumerations that are empty.
-        /// </summary>
-        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<YamlMemberAttribute>();
             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<DefaultValueAttribute>()?.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<DefaultValueAttribute>()?.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 <premek.vysoky@microsoft.com>
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,
 
         /// <summary>
-        /// Specifies that properties that contain null references or a null Nullable&lt;T&gt; are to be omitted. 
+        /// Specifies that properties that contain null references or a null Nullable&lt;T&gt; are to be omitted.
         /// </summary>
         OmitNull = 1,
 
         /// <summary>
-        /// 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.
         /// </summary>
-        OmitEmpty = 2,
+        OmitDefaults = 2,
 
         /// <summary>
-        /// 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.
         /// </summary>
-        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 <premek.vysoky@microsoft.com>
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<string, string> ANonEmptyDictionary => new Dictionary<string, string>() { { "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 <premek.vysoky@microsoft.com>
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<string, string> AnEmptyDictionary => new Dictionary<string, string>();
             public IEnumerable<int> AnEmptyEnumerable => Enumerable.Empty<int>();
 
-            public string[] AnNonEmptyArray => new[] { "foo", "bar" };
-            public IList<int> AnNonEmptyList => new List<int> { 6, 9, 42 };
+            public string[] ANonEmptyArray => new[] { "foo", "bar" };
+            public IList<int> ANonEmptyList => new List<int> { 6, 9, 42 };
             public IEnumerable<bool> ANonEmptyEnumerable => new[] { true, false };
             public Dictionary<string, string> ANonEmptyDictionary => new Dictionary<string, string>() { { "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 <premek.vysoky@microsoft.com>
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<int> ANullList => null;
-
             public int[] AnEmptyArray => new int[0];
             public IList<int> AnEmptyList => new List<int>();
             public Dictionary<string, string> AnEmptyDictionary => new Dictionary<string, string>();