diff --git a/Tomlet.Tests/SerializationWithDictionaryTests.cs b/Tomlet.Tests/SerializationWithDictionaryTests.cs new file mode 100644 index 0000000..8bc4c40 --- /dev/null +++ b/Tomlet.Tests/SerializationWithDictionaryTests.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using Tomlet.Tests.TestModelClasses; +using Xunit; + +namespace Tomlet.Tests +{ + public class IssueReproductionTests + { + [Fact] + public void DictionaryKeyWithPeriodShouldSerializeAndDeserializeCorrectly() + { + var model = new ClassWithDictionary + { + GenericDictionary = new Dictionary + { + { "VsPerMonitorDpiAwarenessEnabled.35204", "some value" }, + { "key.with.dots", "another value" }, + { "Key3", "value 3" }, + { "Key4", "value 4" } + } + }; + + var toml = TomletMain.TomlStringFrom(model); + // Keys with periods should be quoted in output to prevent being interpreted as dotted keys + Assert.Contains("\"VsPerMonitorDpiAwarenessEnabled.35204\"", toml); + Assert.Contains("\"key.with.dots\"", toml); + + // Round-trip: deserialize and verify data integrity + var modelOut = TomletMain.To(toml); + Assert.True(modelOut.GenericDictionary.ContainsKey("VsPerMonitorDpiAwarenessEnabled.35204")); + Assert.Equal("some value", modelOut.GenericDictionary["VsPerMonitorDpiAwarenessEnabled.35204"]); + Assert.True(modelOut.GenericDictionary.ContainsKey("key.with.dots")); + Assert.Equal("another value", modelOut.GenericDictionary["key.with.dots"]); + } + + [Fact] + public void DottedKeysAndDictionaryKeysWithPeriodsBothWork() + { + // This test verifies both features work together: + // 1. Dotted keys in TOML create nested tables (parsing) + // 2. Dictionary keys containing periods are properly quoted (serialization) + + var tomlInput = @" +[GenericDictionary] +""config.setting.name"" = ""value1"" +normal_key = ""value2"" +"; + var model = TomletMain.To(tomlInput); + + Assert.Equal(2, model.GenericDictionary.Count); + Assert.True(model.GenericDictionary.ContainsKey("config.setting.name")); + Assert.Equal("value1", model.GenericDictionary["config.setting.name"]); + Assert.Equal("value2", model.GenericDictionary["normal_key"]); + + // Round-trip should preserve the keys + var modelOut = TomletMain.To(TomletMain.TomlStringFrom(model)); + Assert.True(modelOut.GenericDictionary.ContainsKey("config.setting.name")); + Assert.Equal("value1", modelOut.GenericDictionary["config.setting.name"]); + } + } +} diff --git a/Tomlet/Models/TomlTable.cs b/Tomlet/Models/TomlTable.cs index ad7c716..ae4c70b 100644 --- a/Tomlet/Models/TomlTable.cs +++ b/Tomlet/Models/TomlTable.cs @@ -66,7 +66,7 @@ public string SerializeNonInlineTable(string? keyName, bool includeHeader = true if (value is TomlTable { ShouldBeSerializedInline: false } or TomlArray { CanBeSerializedInline: false }) continue; - WriteValueToStringBuilder(keyName, subKey, builder); + WriteValueToStringBuilder(keyName, subKey, value, builder); } foreach (var (subKey, value) in Entries) @@ -74,7 +74,7 @@ public string SerializeNonInlineTable(string? keyName, bool includeHeader = true if (value is not TomlTable { ShouldBeSerializedInline: false }) continue; - WriteValueToStringBuilder(keyName, subKey, builder); + WriteValueToStringBuilder(keyName, subKey, value, builder); } foreach (var (subKey, value) in Entries) @@ -82,21 +82,16 @@ public string SerializeNonInlineTable(string? keyName, bool includeHeader = true if (value is not TomlArray { CanBeSerializedInline: false }) continue; - WriteValueToStringBuilder(keyName, subKey, builder); + WriteValueToStringBuilder(keyName, subKey, value, builder); } return builder.ToString(); } - private void WriteValueToStringBuilder(string? keyName, string subKey, StringBuilder builder) + private void WriteValueToStringBuilder(string? keyName, string subKey, TomlValue value, StringBuilder builder) { - var value = GetValue(subKey); - subKey = EscapeKeyIfNeeded(subKey); - if (keyName != null) - keyName = EscapeKeyIfNeeded(keyName); - var fullSubKey = keyName == null ? subKey : $"{keyName}.{subKey}"; var hadBlankLine = builder.Length < 2 || builder[builder.Length - 2] == '\n'; @@ -159,8 +154,7 @@ private static bool IsValidKey(string key) { foreach (var c in key) { - //TODO Future: This check for period is perhaps not super valid but it was way more broken without it so I'm leaving it in for now. - if (!char.IsLetterOrDigit(c) && c != '_' && c != '-' && c != '.') + if (!char.IsLetterOrDigit(c) && c != '_' && c != '-') { return false; }