Skip to content

Commit

Permalink
Merge branch 'master' of github.com:aaubry/YamlDotNet into ec-aot
Browse files Browse the repository at this point in the history
  • Loading branch information
EdwardCooke committed Dec 19, 2022
2 parents 467204b + 40e05a2 commit fcc703a
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 9 deletions.
29 changes: 29 additions & 0 deletions YamlDotNet.Test/Core/ScannerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,35 @@ public void Issue_553_562()
}
}

[Fact]
public void Plain_Scalar_outside_of_flow_allows_braces()
{
AssertSequenceOfTokensFrom(Yaml.ScannerForText(@"value: -[123]"),
StreamStart,
BlockMappingStart,
Key,
PlainScalar("value"),
Value,
PlainScalar("-[123]"),
BlockEnd,
StreamEnd);
}

[Fact]
public void Plain_Scalar_inside_flow_does_not_allow_braces()
{
AssertPartialSequenceOfTokensFrom(Yaml.ScannerForText(@"
[
value: -[123]
]"),
StreamStart,
FlowSequenceStart,
Key,
PlainScalar("value"),
Value,
Error("Invalid key indicator format."));
}

private void AssertPartialSequenceOfTokensFrom(Scanner scanner, params Token[] tokens)
{
var tokenNumber = 1;
Expand Down
5 changes: 5 additions & 0 deletions YamlDotNet.Test/Core/TokenHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ protected static AnchorAlias AnchorAlias(string alias)
return new AnchorAlias(new AnchorName(alias));
}

protected static Error Error(string value)
{
return new Error(value, new Mark(), new Mark());
}

protected static Comment StandaloneComment(string text)
{
return new Comment(text, false);
Expand Down
37 changes: 36 additions & 1 deletion YamlDotNet.Test/Serialization/DeserializerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using System.Linq;
using FluentAssertions;
using Xunit;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

Expand Down Expand Up @@ -216,7 +217,6 @@ public void NewLinesInKeys()
Assert.Equal($"value\na\nb", dictionary.First().Value);
}


[Theory]
[InlineData(".nan", System.Single.NaN)]
[InlineData(".NaN", System.Single.NaN)]
Expand Down Expand Up @@ -283,6 +283,41 @@ public void DeserializeScalarEdgeCases(IConvertible value, Type type)
result.Should().Be(value);
}

[Fact]
public void DeserializeWithDuplicateKeyChecking_YamlWithDuplicateKeys_ThrowsYamlException()
{
var yaml = @"
name: Jack
momentOfBirth: 1983-04-21T20:21:03.0041599Z
name: Jake
";

var sut = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.WithDuplicateKeyChecking()
.Build();

Action act = () => sut.Deserialize<Person>(yaml);
act.ShouldThrow<YamlException>("Because there are duplicate name keys");
}

[Fact]
public void DeserializeWithoutDuplicateKeyChecking_YamlWithDuplicateKeys_DoesNotThrowYamlException()
{
var yaml = @"
name: Jack
momentOfBirth: 1983-04-21T20:21:03.0041599Z
name: Jake
";

var sut = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.Build();

Action act = () => sut.Deserialize<Person>(yaml);
act.ShouldNotThrow<YamlException>("Because duplicate key checking is not enabled");
}

public class Test
{
public string Value { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion YamlDotNet/Core/Scanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ private void FetchNextToken()
FetchBlockEntry();
return;
}
else if (analyzer.Check(",[]{}", 1))
else if (flowLevel > 0 && analyzer.Check(",[]{}", 1))
{
tokens.Enqueue(new Error("Invalid key indicator format.", cursor.Mark(), cursor.Mark()));
}
Expand Down
10 changes: 5 additions & 5 deletions YamlDotNet/Core/Tokens/Error.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@
namespace YamlDotNet.Core.Tokens
{
/// <summary>
/// Base class for YAML tokens.
/// Error tokens.
/// </summary>
internal class Error : Token
public class Error : Token
{
/// <summary>
/// Gets the value of the comment
/// Gets the value of the error
/// </summary>
internal string Value { get; }
public string Value { get; }

internal Error(string value, Mark start, Mark end)
public Error(string value, Mark start, Mark end)
: base(start, end)
{
Value = value;
Expand Down
13 changes: 12 additions & 1 deletion YamlDotNet/Serialization/DeserializerBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public sealed class DeserializerBuilder : BuilderSkeleton<DeserializerBuilder>
private readonly Dictionary<TagName, Type> tagMappings;
private readonly Dictionary<Type, Type> typeMappings;
private bool ignoreUnmatched;
private bool duplicateKeyChecking;
private bool attemptUnknownTypeDeserialization;
private ITypeInspector? _baseTypeInspector = null;

Expand Down Expand Up @@ -93,7 +94,7 @@ public DeserializerBuilder()
{ typeof(DictionaryNodeDeserializer), _ => new DictionaryNodeDeserializer(objectFactory.Value) },
{ typeof(CollectionNodeDeserializer), _ => new CollectionNodeDeserializer(objectFactory.Value) },
{ typeof(EnumerableNodeDeserializer), _ => new EnumerableNodeDeserializer() },
{ typeof(ObjectNodeDeserializer), _ => new ObjectNodeDeserializer(objectFactory.Value, BuildTypeInspector(), ignoreUnmatched) }
{ typeof(ObjectNodeDeserializer), _ => new ObjectNodeDeserializer(objectFactory.Value, BuildTypeInspector(), ignoreUnmatched, duplicateKeyChecking) }
};

nodeTypeResolverFactories = new LazyComponentRegistrationList<Nothing, INodeTypeResolver>
Expand Down Expand Up @@ -428,6 +429,16 @@ public DeserializerBuilder IgnoreUnmatchedProperties()
return this;
}

/// <summary>
/// Instructs the deserializer to check for duplicate keys and throw an exception if duplicate keys are found.
/// </summary>
/// <returns></returns>
public DeserializerBuilder WithDuplicateKeyChecking()
{
duplicateKeyChecking = true;
return this;
}

/// <summary>
/// Creates a new <see cref="Deserializer" /> according to the current configuration.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
// SOFTWARE.

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
Expand All @@ -32,12 +33,14 @@ public sealed class ObjectNodeDeserializer : INodeDeserializer
private readonly IObjectFactory objectFactory;
private readonly ITypeInspector typeDescriptor;
private readonly bool ignoreUnmatched;
private readonly bool duplicateKeyChecking;

public ObjectNodeDeserializer(IObjectFactory objectFactory, ITypeInspector typeDescriptor, bool ignoreUnmatched)
public ObjectNodeDeserializer(IObjectFactory objectFactory, ITypeInspector typeDescriptor, bool ignoreUnmatched, bool duplicateKeyChecking)
{
this.objectFactory = objectFactory ?? throw new ArgumentNullException(nameof(objectFactory));
this.typeDescriptor = typeDescriptor ?? throw new ArgumentNullException(nameof(typeDescriptor));
this.ignoreUnmatched = ignoreUnmatched;
this.duplicateKeyChecking = duplicateKeyChecking;
}

public bool Deserialize(IParser parser, Type expectedType, Func<IParser, Type, object?> nestedObjectDeserializer, out object? value)
Expand All @@ -52,11 +55,17 @@ public bool Deserialize(IParser parser, Type expectedType, Func<IParser, Type, o
var implementationType = Nullable.GetUnderlyingType(expectedType) ?? expectedType;

value = objectFactory.Create(implementationType);
var consumedProperties = new List<string>();
while (!parser.TryConsume<MappingEnd>(out var _))
{
var propertyName = parser.Consume<Scalar>();
if (duplicateKeyChecking && consumedProperties.Contains(propertyName.Value))
{
throw new YamlException(propertyName.Start, propertyName.End, $"Encountered duplicate key {propertyName.Value}");
}
try
{
consumedProperties.Add(propertyName.Value);
var property = typeDescriptor.GetProperty(implementationType, null, propertyName.Value, ignoreUnmatched);
if (property == null)
{
Expand Down

0 comments on commit fcc703a

Please sign in to comment.