Skip to content

Commit

Permalink
Fix issue when serializing/deserializing from/to a non-standard dicti…
Browse files Browse the repository at this point in the history
…onary
  • Loading branch information
xoofx committed Feb 1, 2022
1 parent 3fba27d commit 72f2515
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 6 deletions.
39 changes: 39 additions & 0 deletions src/Tomlyn.Tests/ModelTests/ReflectionModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,34 @@ public void TestCommentRoundtripWithModel()
Assert.AreEqual(input, toml2);
}

[Test]
public void TestModelWithSpecialDictionary()
{
var input = @"[values.test]
a = 1
b = true
".ReplaceLineEndings().Trim();
StandardTests.DisplayHeader("input");
Console.WriteLine(input);
var model = Toml.ToModel<ModelWithSpecialDictionary>(input);

Assert.True(model.Values.ContainsKey("test"));

var test = model.Values["test"];

Assert.True(test.ContainsKey("a"));
Assert.True(test.ContainsKey("b"));

Assert.AreEqual(1L, test["a"]);
Assert.AreEqual(true, test["b"]);

var output = Toml.FromModel(model).ReplaceLineEndings().Trim();
StandardTests.DisplayHeader("output");
Console.WriteLine(output);

Assert.AreEqual(output, input);
}

public class SimpleModel
{
public SimpleModel()
Expand Down Expand Up @@ -232,6 +260,17 @@ public class SimpleSubModel
public int? Value { get; set; }
}


public class ModelWithSpecialDictionary
{
public ModelWithSpecialDictionary()
{
Values = new Dictionary<string, Dictionary<string, object>>();
}

public Dictionary<string, Dictionary<string, object>> Values { get; }
}

public class PrimitiveModel : IEquatable<PrimitiveModel>
{
public sbyte Int8Value { get; set; }
Expand Down
12 changes: 10 additions & 2 deletions src/Tomlyn/Model/Accessors/DictionaryDynamicAccessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,11 @@ public SlowDictionaryAccessor(DynamicModelReadContext context, Type dictionaryTy
_propSetter = propSetter!;

MethodInfo? methodTryGetValue = null;
var valueByRefType = valueType.MakeByRefType();
foreach (var method in dictionaryType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy))
{
var parameters = method.GetParameters();
if (method.Name == "TryGetValue" && method.ReturnType == typeof(bool) && parameters.Length == 2 && parameters[0].ParameterType == KeyType && parameters[1].IsOut && parameters[1].ParameterType == valueType)
if (method.Name == "TryGetValue" && method.ReturnType == typeof(bool) && parameters.Length == 2 && parameters[0].ParameterType == KeyType && parameters[1].IsOut && parameters[1].ParameterType == valueByRefType)
{
methodTryGetValue = method;
break;
Expand All @@ -225,7 +226,14 @@ public SlowDictionaryAccessor(DynamicModelReadContext context, Type dictionaryTy
var enumerator = (IDictionaryEnumerator)it.GetEnumerator();
while (enumerator.MoveNext())
{
yield return new KeyValuePair<string, object?>((string)_context.ConvertTo!(enumerator.Key, typeof(string)), enumerator.Value);

if (!_context.TryConvertValue(new SourceSpan(), enumerator.Key, typeof(string), out var newKey) || newKey is not string text)
{
_context.Diagnostics.Error(new SourceSpan(), $"Unable to convert key {enumerator.Key} to a string");
yield break;
}

yield return new KeyValuePair<string, object?>(text, enumerator.Value);
}
}

Expand Down
10 changes: 7 additions & 3 deletions src/Tomlyn/Model/Accessors/DynamicModelReadContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public DynamicModelReadContext(TomlModelOptions options)

public Func<Type, ObjectKind, object> CreateInstance { get; set; }

public Func<object, Type, object>? ConvertTo { get; set; }
public Func<object, Type, object?>? ConvertTo { get; set; }

public DiagnosticsBag Diagnostics { get; }

Expand Down Expand Up @@ -154,8 +154,12 @@ public bool TryConvertValue(SourceSpan span, object? value, Type changeType, out

if (ConvertTo is not null)
{
outputValue = ConvertTo(value, changeType);
return true;
var convertedValue = ConvertTo(value, changeType);
outputValue = convertedValue;
if (convertedValue is not null)
{
return true;
}
}

errorMessage = $"Unsupported type to convert {value.GetType().FullName} to type {changeType.FullName}.";
Expand Down
2 changes: 1 addition & 1 deletion src/Tomlyn/Model/SyntaxToModelTransform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ private bool GetOrCreateSubObject(SourceSpan span, string key, ObjectKind kind)
var accessor = _currentObjectAccessor as ObjectDynamicAccessor;
if (accessor is null)
{
_context.Diagnostics.Error(span, $"Unable to set a key on an object accessor {_currentObjectAccessor!.TargetType.FullName}");
_context.Diagnostics.Error(span, $"Unable to set a key {key} on an object accessor {_currentObjectAccessor!.TargetType.FullName}");
return false;
}

Expand Down

0 comments on commit 72f2515

Please sign in to comment.