Configuration Json provider: adds empty objects to configuration (#40829)
authorAlessio Franceschelli <alessio@franceschelli.me>
Wed, 19 Aug 2020 16:51:12 +0000 (17:51 +0100)
committerGitHub <noreply@github.com>
Wed, 19 Aug 2020 16:51:12 +0000 (09:51 -0700)
src/libraries/Microsoft.Extensions.Configuration.Json/src/JsonConfigurationFileParser.cs
src/libraries/Microsoft.Extensions.Configuration.Json/tests/EmptyObjectTest.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Configuration.Json/tests/IntegrationTest.cs [new file with mode: 0644]
src/libraries/Microsoft.Extensions.Configuration/tests/ConfigurationTest.cs

index 354b94c..4029c92 100644 (file)
@@ -43,13 +43,22 @@ namespace Microsoft.Extensions.Configuration.Json
             return _data;
         }
 
-        private void VisitElement(JsonElement element) {
+        private void VisitElement(JsonElement element)
+        {
+            var isEmpty = true;
+
             foreach (JsonProperty property in element.EnumerateObject())
             {
+                isEmpty = false;
                 EnterContext(property.Name);
                 VisitValue(property.Value);
                 ExitContext();
             }
+
+            if (isEmpty)
+            {
+                _data[_currentPath] = null;
+            }
         }
 
         private void VisitValue(JsonElement value)
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Json/tests/EmptyObjectTest.cs b/src/libraries/Microsoft.Extensions.Configuration.Json/tests/EmptyObjectTest.cs
new file mode 100644 (file)
index 0000000..5f2f085
--- /dev/null
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.Configuration.Test;
+using Xunit;
+
+namespace Microsoft.Extensions.Configuration.Json.Test
+{
+    public class EmptyObjectTest
+    {
+        [Fact]
+        public void EmptyObject_AddsAsNull()
+        {
+            var json = @"{
+                ""key"": { },
+            }";
+
+            var jsonConfigSource = new JsonConfigurationProvider(new JsonConfigurationSource());
+            jsonConfigSource.Load(TestStreamHelpers.StringToStream(json));
+
+            Assert.Null(jsonConfigSource.Get("key"));
+        }
+
+        [Fact]
+        public void NullObject_AddsEmptyString()
+        {
+            var json = @"{
+                ""key"": null,
+            }";
+
+            var jsonConfigSource = new JsonConfigurationProvider(new JsonConfigurationSource());
+            jsonConfigSource.Load(TestStreamHelpers.StringToStream(json));
+
+            Assert.Equal("", jsonConfigSource.Get("key"));
+        }
+
+        [Fact]
+        public void NestedObject_DoesNotAddParent()
+        {
+            var json = @"{
+                ""key"": {
+                    ""nested"": ""value""
+                },
+            }";
+
+            var jsonConfigSource = new JsonConfigurationProvider(new JsonConfigurationSource());
+            jsonConfigSource.Load(TestStreamHelpers.StringToStream(json));
+
+            Assert.False(jsonConfigSource.TryGet("key", out _));
+            Assert.Equal("value", jsonConfigSource.Get("key:nested"));
+        }
+    }
+}
diff --git a/src/libraries/Microsoft.Extensions.Configuration.Json/tests/IntegrationTest.cs b/src/libraries/Microsoft.Extensions.Configuration.Json/tests/IntegrationTest.cs
new file mode 100644 (file)
index 0000000..b206e31
--- /dev/null
@@ -0,0 +1,67 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Extensions.Configuration.Test;
+using Xunit;
+using Xunit.Sdk;
+
+namespace Microsoft.Extensions.Configuration.Json.Test
+{
+    public class IntegrationTest
+    {
+        [Fact]
+        public void LoadJsonConfiguration()
+        {
+            var json = @"{
+                ""a"": ""b"",
+                ""c"": {
+                    ""d"": ""e""
+                },
+                ""f"": """",
+                ""g"": null,
+                ""h"": {},
+                ""i"": {
+                    ""k"": {}
+                } 
+            }";
+
+            var configurationBuilder = new ConfigurationBuilder();
+            configurationBuilder.AddJsonStream(TestStreamHelpers.StringToStream(json));
+            var configuration = configurationBuilder.Build();
+
+            Assert.Collection(configuration.GetChildren(),
+                new Action<IConfigurationSection>[] {
+                    x => AssertSection(x, "a", "b"),
+                    x => AssertSection(x, "c", null, new Action<IConfigurationSection>[] {
+                        x => AssertSection(x, "d", "e"),
+                    }),
+                    x => AssertSection(x, "f", ""),
+                    x => AssertSection(x, "g", ""),
+                    x => AssertSection(x, "h", null),
+                    x => AssertSection(x, "i", null, new Action<IConfigurationSection>[] {
+                        x => AssertSection(x, "k", null),
+                    }),
+            });
+        }
+
+        private static void AssertSection(IConfigurationSection configurationSection, string key, string value)
+            => AssertSection(configurationSection, key, value, new Action<IConfigurationSection>[0]);
+
+        private static void AssertSection(IConfigurationSection configurationSection, string key, string value, Action<IConfigurationSection>[] childrenInspectors)
+        {
+            if (key != configurationSection.Key || value != configurationSection.Value)
+            {
+                throw new EqualException(
+                    expected: GetString(key, value),
+                    actual: GetString(configurationSection));
+            }
+
+            Assert.Collection(configurationSection.GetChildren(), childrenInspectors);
+        }
+
+        private static string GetString(IConfigurationSection configurationSection) => GetString(configurationSection.Key, configurationSection.Value);
+        private static string GetString(string key, string value) => $"\"{key}\":" + (value is null ? "null" : $"\"{value}\"");
+    }
+}
index 45bafb4..40f2b23 100644 (file)
@@ -769,6 +769,69 @@ namespace Microsoft.Extensions.Configuration.Test
             Assert.False(sectionNotExists);
         }
 
+        [Theory]
+        [InlineData("Value1")]
+        [InlineData("")]
+        public void KeyWithValueAndWithoutChildrenExistsAsSection(string value)
+        {
+            // Arrange
+            var dict = new Dictionary<string, string>()
+            {
+                {"Mem1", value}
+            };
+            var configurationBuilder = new ConfigurationBuilder();
+            configurationBuilder.AddInMemoryCollection(dict);
+            var config = configurationBuilder.Build();
+
+            // Act
+            var sectionExists = config.GetSection("Mem1").Exists();
+
+            // Assert
+            Assert.True(sectionExists);
+        }
+
+        [Fact]
+        public void KeyWithNullValueAndWithoutChildrenIsASectionButNotExists()
+        {
+            // Arrange
+            var dict = new Dictionary<string, string>()
+            {
+                {"Mem1", null}
+            };
+            var configurationBuilder = new ConfigurationBuilder();
+            configurationBuilder.AddInMemoryCollection(dict);
+            var config = configurationBuilder.Build();
+
+            // Act
+            var sections = config.GetChildren();
+            var sectionExists = config.GetSection("Mem1").Exists();
+            var sectionChildren = config.GetSection("Mem1").GetChildren();
+
+            // Assert
+            Assert.Single(sections, section => section.Key == "Mem1");
+            Assert.False(sectionExists);
+            Assert.Empty(sectionChildren);
+        }
+
+        [Fact]
+        public void SectionWithChildrenHasNullValue()
+        {
+            // Arrange
+            var dict = new Dictionary<string, string>()
+            {
+                {"Mem1:KeyInMem1", "ValueInMem1"},
+            };
+            var configurationBuilder = new ConfigurationBuilder();
+            configurationBuilder.AddInMemoryCollection(dict);
+            var config = configurationBuilder.Build();
+
+            // Act
+            var sectionValue = config.GetSection("Mem1").Value;
+
+            // Assert
+            Assert.Null(sectionValue);
+        }
+
         [Fact]
         public void NullSectionDoesNotExist()
         {