diff --git a/src/Serilog/Settings/KeyValuePairs/KeyValuePairSettings.cs b/src/Serilog/Settings/KeyValuePairs/KeyValuePairSettings.cs index 6ee49adce..f00d3698f 100644 --- a/src/Serilog/Settings/KeyValuePairs/KeyValuePairSettings.cs +++ b/src/Serilog/Settings/KeyValuePairs/KeyValuePairSettings.cs @@ -14,9 +14,11 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; using Serilog.Configuration; using Serilog.Events; @@ -155,6 +157,12 @@ internal static IEnumerable LoadConfigurationAssemblies(Dictionary> ExtendedTypeConversions = new Dictionary> + { + { typeof(Uri), s => new Uri(s) }, + { typeof(TimeSpan), s => TimeSpan.Parse(s) } + }; + internal static object ConvertToType(string value, Type toType) { var toTypeInfo = toType.GetTypeInfo(); @@ -171,18 +179,34 @@ internal static object ConvertToType(string value, Type toType) if (toTypeInfo.IsEnum) return Enum.Parse(toType, value); - var extendedTypeConversions = new Dictionary> - { - { typeof(Uri), s => new Uri(s) }, - { typeof(TimeSpan), s => TimeSpan.Parse(s) } - }; - - var convertor = extendedTypeConversions + var convertor = ExtendedTypeConversions .Where(t => t.Key.GetTypeInfo().IsAssignableFrom(toTypeInfo)) .Select(t => t.Value) .FirstOrDefault(); - return convertor == null ? Convert.ChangeType(value, toType) : convertor(value); + if (convertor != null) + return convertor(value); + + if (toTypeInfo.IsInterface && !string.IsNullOrWhiteSpace(value)) + { + var type = Type.GetType(value.Trim(), throwOnError: false).GetTypeInfo(); + if (type != null) + { + var ctor = type.DeclaredConstructors.FirstOrDefault(ci => + { + var parameters = ci.GetParameters(); + return parameters.Length == 0 || parameters.All(pi => pi.HasDefaultValue); + }); + + if (ctor == null) + throw new InvalidOperationException($"A default constructor was not found on {type.FullName}."); + + var call = ctor.GetParameters().Select(pi => pi.DefaultValue).ToArray(); + return ctor.Invoke(call); + } + } + + return Convert.ChangeType(value, toType); } internal static IList FindSinkConfigurationMethods(IEnumerable configurationAssemblies) diff --git a/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs b/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs index e81b69146..12d5d6ea3 100644 --- a/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs +++ b/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs @@ -7,6 +7,9 @@ using Serilog.Tests.Support; using Serilog.Enrichers; using TestDummies; +using Serilog.Formatting; +using Serilog.Formatting.Json; +using Serilog.Tests.Formatting.Json; namespace Serilog.Tests.AppSettings.Tests { @@ -91,5 +94,13 @@ public void PropertyEnrichmentIsApplied() Assert.NotNull(evt); Assert.Equal("Test", evt.Properties["App"].LiteralValue()); } + + + [Fact] + public void StringValuesConvertToDefaultInstancesIfTargetIsInterface() + { + var result = (object)KeyValuePairSettings.ConvertToType("Serilog.Formatting.Json.JsonFormatter", typeof(ITextFormatter)); + Assert.IsType(result); + } } } \ No newline at end of file