From 642fabfddefdf8fddb56a1c343e0bda462b77709 Mon Sep 17 00:00:00 2001 From: Rian Drake Date: Tue, 19 Aug 2025 17:16:43 +1200 Subject: [PATCH] Improve behaviour for invalid enum keys in Dictionary fields Previously, invalid enum keys would often crash the program when trying to add the same key twice. However, it may not be ideal for us to even add invalid keys to a Dictionary; so I've provided an option that allows the user to skip these fields from being added entirely. In the case of Ignore, it will still set it to the first enum value as it did before - but it will no longer crash when trying to add the same key multiple times (in the case where multiple invalid keys were found, or a valid key at the first enumerator also exists) --- ...beratelyIncorrectTestResources.Designer.cs | 6 ++++ .../DeliberatelyIncorrectTestResources.resx | 9 ++++++ Tomlet.Tests/EnumTests.cs | 11 ++++++- Tomlet.Tests/ExceptionTests.cs | 4 +++ Tomlet/TomlSerializationMethods.cs | 30 +++++++++++-------- Tomlet/TomlSerializerOptions.cs | 3 ++ 6 files changed, 49 insertions(+), 14 deletions(-) diff --git a/Tomlet.Tests/DeliberatelyIncorrectTestResources.Designer.cs b/Tomlet.Tests/DeliberatelyIncorrectTestResources.Designer.cs index 8052348..d4b98e3 100644 --- a/Tomlet.Tests/DeliberatelyIncorrectTestResources.Designer.cs +++ b/Tomlet.Tests/DeliberatelyIncorrectTestResources.Designer.cs @@ -141,6 +141,12 @@ internal static string KeyRedefinitionViaTableTestInput { } } + internal static string EnumKeyRedefinitionViaTableTestInput { + get { + return ResourceManager.GetString("EnumKeyRedefinitionViaTableTestInput", resourceCulture); + } + } + internal static string ReDefiningSubTableAsSubTableArrayTestInput { get { return ResourceManager.GetString("ReDefiningSubTableAsSubTableArrayTestInput", resourceCulture); diff --git a/Tomlet.Tests/DeliberatelyIncorrectTestResources.resx b/Tomlet.Tests/DeliberatelyIncorrectTestResources.resx index 14993b7..6ae1090 100644 --- a/Tomlet.Tests/DeliberatelyIncorrectTestResources.resx +++ b/Tomlet.Tests/DeliberatelyIncorrectTestResources.resx @@ -81,6 +81,15 @@ apple = "red" [fruit.apple] texture = "smooth" + + + [Subnames] +[Subnames.Value1] +a = 'A' +b = 'B' +[Subnames.Value1] +a = 'A' +b = 'B' # INVALID TOML DOC diff --git a/Tomlet.Tests/EnumTests.cs b/Tomlet.Tests/EnumTests.cs index c8166f7..8cca0cc 100644 --- a/Tomlet.Tests/EnumTests.cs +++ b/Tomlet.Tests/EnumTests.cs @@ -64,7 +64,16 @@ public void CanDeserializeEnumDictionary() var expected = new Dictionary {{TestEnum.Value1, 1}, {TestEnum.Value2, 2}, {TestEnum.Value3, 3}}; Assert.Equal(expected, result); } - + + [Fact] + public void CanIgnoreInvalidEnumDictionaryKeys() + { + var toml = "Value1 = 1\nValue2 = 2\nValue3 = 3\nValue4 = 4\nValue5 = 5\nValue6 = 6\n"; + var result = TomletMain.To>(toml, new TomlSerializerOptions { IgnoreInvalidEnumValues = true }); + var expected = new Dictionary {{TestEnum.Value1, 1}, {TestEnum.Value2, 2}, {TestEnum.Value3, 3}}; + Assert.Equal(expected, result); + } + [Fact] public void CanDeserializeEnumDictionaryWithFields() { diff --git a/Tomlet.Tests/ExceptionTests.cs b/Tomlet.Tests/ExceptionTests.cs index 1865244..e334d2a 100644 --- a/Tomlet.Tests/ExceptionTests.cs +++ b/Tomlet.Tests/ExceptionTests.cs @@ -109,6 +109,10 @@ public void ReDefiningASubTableAsASubTableArrayThrowsAnException() => public void RedefiningAKeyAsATableNameThrowsAnException() => AssertThrows(() => GetDocument(DeliberatelyIncorrectTestResources.KeyRedefinitionViaTableTestInput)); + [Fact] + public void RedefiningAnEnumKeyAsATableEntryThrowsAnException() => + AssertThrows(() => GetDocument(DeliberatelyIncorrectTestResources.EnumKeyRedefinitionViaTableTestInput)); + [Fact] public void DefiningATableArrayWithTheSameNameAsATableThrowsAnException() => AssertThrows(() => GetDocument(DeliberatelyIncorrectTestResources.DefiningAsArrayWhenAlreadyTableTestInput)); diff --git a/Tomlet/TomlSerializationMethods.cs b/Tomlet/TomlSerializationMethods.cs index d3ac220..cee4b8e 100644 --- a/Tomlet/TomlSerializationMethods.cs +++ b/Tomlet/TomlSerializationMethods.cs @@ -388,28 +388,32 @@ private static Deserialize> PrimitiveKeyedDictionaryDes if (value is not TomlTable table) throw new TomlTypeMismatchException(typeof(TomlTable), value.GetType(), typeof(Dictionary)); - return table.Entries.ToDictionary( - entry => + return table.Entries + .Where(entry => { - if (!type.IsEnum) - { - return (TKey)(entry.Key as IConvertible).ToType(typeof(TKey), CultureInfo.InvariantCulture); - } + if (!type.IsEnum || !options.IgnoreInvalidEnumValues) + return true; try { - return (TKey)Enum.Parse(type, entry.Key, true); + _ = Enum.Parse(type, entry.Key, true); + return true; } catch (ArgumentException) { - if (options.IgnoreInvalidEnumValues) - return (TKey)Enum.GetValues(type).GetValue(0)!; - - throw new TomlEnumParseException(entry.Key, typeof(TKey)); + return false; } + }) + .ToDictionary(entry => + { + if (!type.IsEnum) + { + return (TKey)(entry.Key as IConvertible).ToType(typeof(TKey), CultureInfo.InvariantCulture); + } + + return (TKey)Enum.Parse(type, entry.Key, true); }, - entry => (TValue)valueDeserializer(entry.Value) - ); + entry => (TValue)valueDeserializer(entry.Value)); }; } diff --git a/Tomlet/TomlSerializerOptions.cs b/Tomlet/TomlSerializerOptions.cs index 37daa9a..0cdcf87 100644 --- a/Tomlet/TomlSerializerOptions.cs +++ b/Tomlet/TomlSerializerOptions.cs @@ -17,5 +17,8 @@ public class TomlSerializerOptions /// /// When set to true, the deserializer will ignore invalid enum values (and they will be implicitly left at their default value). When set to false, an exception will be thrown if the enum value is not found. /// + /// + /// When deserializing dictionaries with enums as keys, invalid enum values will be skipped. + /// public bool IgnoreInvalidEnumValues { get; set; } = false; } \ No newline at end of file