Skip to content

Commit

Permalink
fix: Serializing dictionary with ', ", . in key.
Browse files Browse the repository at this point in the history
Fixes issue where GetTopLevelAndSubKeys splits a key when serializing.
Adds test to handle new cases.
Does not test cases with mixed " and ' due to issue explained in #57
  • Loading branch information
ITR13 committed Jan 14, 2024
1 parent acbaa75 commit bdbb5a0
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 0 deletions.
197 changes: 197 additions & 0 deletions Tomlet.Tests/SerializeSpecialKeysTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
using System.Collections.Generic;
using Tomlet.Tests.TestModelClasses;
using Xunit;

namespace Tomlet.Tests
{
public class SerializeSpecialKeysTests
{
void AssertEqual(Dictionary<string, string> dictionary, Dictionary<string, string> other)
{
Assert.Equal(dictionary.Count, other.Count);
foreach (var (key, value) in dictionary)
{
var otherValue = Assert.Contains(key, (IDictionary<string, string>)other);
Assert.Equal(value, otherValue);
}
}


[Fact]
public void NoSpecialKeys()
{
var dict = new Dictionary<string, string>
{
{ "SomeKey", "SomeValue" },
};
var tomlString = TomletMain.TomlStringFrom(dict);
var otherDict = TomletMain.To<Dictionary<string, string>>(tomlString);
AssertEqual(dict, otherDict);
}

[Fact]
public void DottedKey()
{
var dict = new Dictionary<string, string>
{
{ "Some.Key", "Some.Value" },
{ "Some.", "Some." },
{ ".Key", ".Value" },
{ ".", "." },
};
var tomlString = TomletMain.TomlStringFrom(dict);
var otherDict = TomletMain.To<Dictionary<string, string>>(tomlString);
AssertEqual(dict, otherDict);
}

[Fact]
public void QuotedKey()
{
var dict = new Dictionary<string, string>
{
{ "'SomeKey'", "'SomeValue'" },
{ "\"SomeKey\"", "\"SomeValue\"" },
{ "\"", "\"" },
{ "'", "'" },
};
var tomlString = TomletMain.TomlStringFrom(dict);
var otherDict = TomletMain.To<Dictionary<string, string>>(tomlString);
AssertEqual(dict, otherDict);
}

[Fact]
public void QuotedDottedKey()
{
var dict = new Dictionary<string, string>
{
{ "'Some.Key'", "'Some.Value'" },
{ "\"Some.Key\"", "\"Some.Value\"" },
{ "'Some'.Key", "'Some'.Value" },
{ "\"Some\".Key", "\"Some\".Value" },
{ "Some.'Key'", "Some.'Value'" },
{ "Some.\"Key\"", "Some.\"Value\"" },
};
var tomlString = TomletMain.TomlStringFrom(dict);
var otherDict = TomletMain.To<Dictionary<string, string>>(tomlString);
AssertEqual(dict, otherDict);
}

[Fact]
public void Brackets()
{
var dict = new Dictionary<string, string>
{
{ "[SomeKey]", "[SomeValue]" },
{ "[SomeKey\"", "[SomeValue\"" },
{ "[SomeKey", "[SomeValue" },
{ "SomeKey]", "SomeValue]" },
{ "[", "]" },
{ "]", "]" },
};
var tomlString = TomletMain.TomlStringFrom(dict);
var otherDict = TomletMain.To<Dictionary<string, string>>(tomlString);
AssertEqual(dict, otherDict);
}

[Fact]
public void NoSpecialKeysWithClass()
{
var dict = new Dictionary<string, string>
{
{ "SomeKey", "SomeValue" },
};
var tomlString = TomletMain.TomlStringFrom(
new ClassWithDictionary
{
GenericDictionary = dict,
}
);
var otherClass = TomletMain.To<ClassWithDictionary>(tomlString);
AssertEqual(dict, otherClass.GenericDictionary);
}

[Fact]
public void DottedKeyWithClass()
{
var dict = new Dictionary<string, string>
{
{ "Some.Key", "Some.Value" },
{ "Some.", "Some." },
{ ".Key", ".Value" },
{ ".", "." },
};
var tomlString = TomletMain.TomlStringFrom(
new ClassWithDictionary
{
GenericDictionary = dict,
}
);
var otherClass = TomletMain.To<ClassWithDictionary>(tomlString);
AssertEqual(dict, otherClass.GenericDictionary);
}

[Fact]
public void QuotedKeyWithClass()
{
var dict = new Dictionary<string, string>
{
{ "'SomeKey'", "'SomeValue'" },
{ "\"SomeKey\"", "\"SomeValue\"" },
{ "\"", "\"" },
{ "'", "'" },
};
var tomlString = TomletMain.TomlStringFrom(
new ClassWithDictionary
{
GenericDictionary = dict,
}
);
var otherClass = TomletMain.To<ClassWithDictionary>(tomlString);
AssertEqual(dict, otherClass.GenericDictionary);
}

[Fact]
public void QuotedDottedKeyWithClass()
{
var dict = new Dictionary<string, string>
{
{ "'Some.Key'", "'Some.Value'" },
{ "\"Some.Key\"", "\"Some.Value\"" },
{ "'Some'.Key", "'Some'.Value" },
{ "\"Some\".Key", "\"Some\".Value" },
{ "Some.'Key'", "Some.'Value'" },
{ "Some.\"Key\"", "Some.\"Value\"" },
};
var tomlString = TomletMain.TomlStringFrom(
new ClassWithDictionary
{
GenericDictionary = dict,
}
);
var otherClass = TomletMain.To<ClassWithDictionary>(tomlString);
AssertEqual(dict, otherClass.GenericDictionary);
}

[Fact]
public void BracketsWithClass()
{
var dict = new Dictionary<string, string>
{
{ "[SomeKey]", "[SomeValue]" },
{ "[SomeKey\"", "[SomeValue\"" },
{ "[SomeKey", "[SomeValue" },
{ "SomeKey]", "SomeValue]" },
{ "[", "]" },
{ "]", "]" },
};
var tomlString = TomletMain.TomlStringFrom(
new ClassWithDictionary
{
GenericDictionary = dict,
}
);
var otherClass = TomletMain.To<ClassWithDictionary>(tomlString);
AssertEqual(dict, otherClass.GenericDictionary);
}
}
}
1 change: 1 addition & 0 deletions Tomlet/Models/TomlTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public string SerializeNonInlineTable(string? keyName, bool includeHeader = true

private void WriteValueToStringBuilder(string? keyName, string subKey, StringBuilder builder)
{
subKey = TomlKeyUtils.FullStringToProperKey(subKey);
var value = GetValue(subKey);

subKey = EscapeKeyIfNeeded(subKey);
Expand Down
14 changes: 14 additions & 0 deletions Tomlet/TomlKeyUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,19 @@ internal static void GetTopLevelAndSubKeys(string key, out string ourKeyName, ou

ourKeyName = ourKeyName.Trim();
}

public static string FullStringToProperKey(string key)
{
GetTopLevelAndSubKeys(key, out var a, out var b);
var keyLooksQuoted = key.StartsWith("\"") || key.StartsWith("'");
var keyLooksDotted = key.Contains(".");

if (keyLooksQuoted || keyLooksDotted || !string.IsNullOrEmpty(b))
{
return TomlUtils.AddCorrectQuotes(key);
}

return key;
}
}
}

0 comments on commit bdbb5a0

Please sign in to comment.