From 96fe6c59efbe009c162c6a8313d90fd5bc9cb583 Mon Sep 17 00:00:00 2001 From: stan-sz <37585349+stan-sz@users.noreply.github.com> Date: Fri, 20 Jan 2023 10:47:50 +0100 Subject: [PATCH] Follow up on duplicate key checking Follow up on #536 and extend the functionality to `DictionaryNodeDeserializer`. --- .../Serialization/DeserializerTest.cs | 14 +++++++++++ .../Serialization/DeserializerBuilder.cs | 4 +-- .../DictionaryNodeDeserializer.cs | 25 +++++++++++++------ .../ObjectNodeDeserializer.cs | 5 ++-- .../StaticDictionaryNodeDeserializer.cs | 4 +-- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/YamlDotNet.Test/Serialization/DeserializerTest.cs b/YamlDotNet.Test/Serialization/DeserializerTest.cs index a61d683b..26f0d4b4 100644 --- a/YamlDotNet.Test/Serialization/DeserializerTest.cs +++ b/YamlDotNet.Test/Serialization/DeserializerTest.cs @@ -299,6 +299,13 @@ public void DeserializeWithDuplicateKeyChecking_YamlWithDuplicateKeys_ThrowsYaml Action act = () => sut.Deserialize(yaml); act.ShouldThrow("Because there are duplicate name keys"); + act = () => sut.Deserialize>(yaml); + act.ShouldThrow("Because there are duplicate name keys"); + + var stream = Yaml.ReaderFrom("backreference.yaml"); + var parser = new MergingParser(new Parser(stream)); + act = () => sut.Deserialize>>(parser); + act.ShouldThrow("Because there are duplicate name keys"); } [Fact] @@ -316,6 +323,13 @@ public void DeserializeWithoutDuplicateKeyChecking_YamlWithDuplicateKeys_DoesNot Action act = () => sut.Deserialize(yaml); act.ShouldNotThrow("Because duplicate key checking is not enabled"); + act = () => sut.Deserialize>(yaml); + act.ShouldNotThrow("Because duplicate key checking is not enabled"); + + var stream = Yaml.ReaderFrom("backreference.yaml"); + var parser = new MergingParser(new Parser(stream)); + act = () => sut.Deserialize>>(parser); + act.ShouldNotThrow("Because duplicate key checking is not enabled"); } public class Test diff --git a/YamlDotNet/Serialization/DeserializerBuilder.cs b/YamlDotNet/Serialization/DeserializerBuilder.cs index 7982d41f..9591e7b7 100755 --- a/YamlDotNet/Serialization/DeserializerBuilder.cs +++ b/YamlDotNet/Serialization/DeserializerBuilder.cs @@ -91,7 +91,7 @@ public DeserializerBuilder() { typeof(NullNodeDeserializer), _ => new NullNodeDeserializer() }, { typeof(ScalarNodeDeserializer), _ => new ScalarNodeDeserializer(attemptUnknownTypeDeserialization) }, { typeof(ArrayNodeDeserializer), _ => new ArrayNodeDeserializer() }, - { typeof(DictionaryNodeDeserializer), _ => new DictionaryNodeDeserializer(objectFactory.Value) }, + { typeof(DictionaryNodeDeserializer), _ => new DictionaryNodeDeserializer(objectFactory.Value, duplicateKeyChecking) }, { typeof(CollectionNodeDeserializer), _ => new CollectionNodeDeserializer(objectFactory.Value) }, { typeof(EnumerableNodeDeserializer), _ => new EnumerableNodeDeserializer() }, { typeof(ObjectNodeDeserializer), _ => new ObjectNodeDeserializer(objectFactory.Value, BuildTypeInspector(), ignoreUnmatched, duplicateKeyChecking) } @@ -151,7 +151,7 @@ public DeserializerBuilder WithStaticContext(StaticContext context) _baseTypeInspector = context.GetTypeInspector(); WithoutNodeDeserializer(); - WithNodeDeserializer(new StaticDictionaryNodeDeserializer(context.GetFactory())); + WithNodeDeserializer(new StaticDictionaryNodeDeserializer(context.GetFactory(), duplicateKeyChecking)); return Self; } diff --git a/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs index ec57c5f7..659ce10c 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/DictionaryNodeDeserializer.cs @@ -32,10 +32,12 @@ namespace YamlDotNet.Serialization.NodeDeserializers public class DictionaryNodeDeserializer : INodeDeserializer { private readonly IObjectFactory objectFactory; + private readonly bool duplicateKeyChecking; - public DictionaryNodeDeserializer(IObjectFactory objectFactory) + public DictionaryNodeDeserializer(IObjectFactory objectFactory, bool duplicateKeyChecking) { this.objectFactory = objectFactory ?? throw new ArgumentNullException(nameof(objectFactory)); + this.duplicateKeyChecking = duplicateKeyChecking; } public virtual bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) @@ -77,9 +79,18 @@ public virtual bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, IDictionary result) { - parser.Consume(); + var property = parser.Consume(); while (!parser.TryConsume(out var _)) { var key = nestedObjectDeserializer(parser, tKey); @@ -91,7 +102,7 @@ protected void Deserialize(Type tKey, Type tValue, IParser parser, Func result[v!] = value!; + keyPromise.ValueAvailable += v => TryAssign(result, v!, value!, property); } else { @@ -102,7 +113,7 @@ protected void Deserialize(Type tKey, Type tValue, IParser parser, Func result[key!] = v!; + valuePromise.ValueAvailable += v => TryAssign(result, key!, v!, property); } } } diff --git a/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs index a81324a1..0e23b5c4 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs @@ -55,17 +55,16 @@ public bool Deserialize(IParser parser, Type expectedType, Func(); + var consumedProperties = new HashSet(StringComparer.Ordinal); while (!parser.TryConsume(out var _)) { var propertyName = parser.Consume(); - if (duplicateKeyChecking && consumedProperties.Contains(propertyName.Value)) + if (duplicateKeyChecking && !consumedProperties.Add(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) { diff --git a/YamlDotNet/Serialization/NodeDeserializers/StaticDictionaryNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/StaticDictionaryNodeDeserializer.cs index 523390fb..ce6ccc3e 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/StaticDictionaryNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/StaticDictionaryNodeDeserializer.cs @@ -29,8 +29,8 @@ public class StaticDictionaryNodeDeserializer : DictionaryNodeDeserializer { private readonly ObjectFactories.StaticObjectFactory _objectFactory; - public StaticDictionaryNodeDeserializer(ObjectFactories.StaticObjectFactory objectFactory) - : base(objectFactory) + public StaticDictionaryNodeDeserializer(ObjectFactories.StaticObjectFactory objectFactory, bool duplicateKeyChecking) + : base(objectFactory, duplicateKeyChecking) { _objectFactory = objectFactory ?? throw new ArgumentNullException(nameof(objectFactory)); }