Skip to content

Commit

Permalink
Include start / end in exceptions thrown by type inspector
Browse files Browse the repository at this point in the history
- Add two new exceptions `PropertyNotFoundException` and  `MultiplePropertiesFoundException` than inherits from `YamlException`
- Adapt the code to pass the current property as a `Scalar` which includes `Start` / `End`
  • Loading branch information
jairbubbles committed Jul 15, 2021
1 parent 665b182 commit 573515f
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 14 deletions.
9 changes: 8 additions & 1 deletion YamlDotNet.Test/Serialization/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1203,7 +1203,14 @@ public void DontIgnoreExtraPropertiesIfWanted()
{
var text = Lines("aaa: hello", "bbb: world");
var actual = Record.Exception(() => Deserializer.Deserialize<Simple>(UsingReaderFor(text)));
Assert.IsType<YamlException>(actual);
Assert.IsType<PropertyNotFoundException>(actual);
((PropertyNotFoundException)actual).Start.Column.Should().Be(1);
((PropertyNotFoundException)actual).Start.Line.Should().Be(2);
((PropertyNotFoundException)actual).Start.Index.Should().Be(12);
((PropertyNotFoundException)actual).End.Column.Should().Be(4);
((PropertyNotFoundException)actual).End.Line.Should().Be(2);
((PropertyNotFoundException)actual).End.Index.Should().Be(15);
((PropertyNotFoundException)actual).Message.Should().EndWith("Property 'bbb' not found on type 'YamlDotNet.Test.Serialization.Simple'.");
}

[Fact]
Expand Down
40 changes: 40 additions & 0 deletions YamlDotNet/Core/MultiplePropertiesFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// This file is part of YamlDotNet - A .NET library for YAML.
// Copyright (c) Antoine Aubry and contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;

namespace YamlDotNet.Core
{
/// <summary>
/// Exception that is thrown when multiple properties are found in the given type
/// </summary>
public sealed class MultiplePropertiesFoundException : YamlException
{
public MultiplePropertiesFoundException(Scalar propertyName, Type type, IEnumerable<IPropertyDescriptor> candidates) : base(propertyName.Start, propertyName.End,
$"Multiple properties with the name/alias '{propertyName.Value}' already exists on type '{type.FullName}', maybe you're misusing YamlAlias or maybe you are using the wrong naming convention? The matching properties are: {string.Join(", ", candidates.Select(p => p.Name).ToArray())}")
{
}
}
}
36 changes: 36 additions & 0 deletions YamlDotNet/Core/PropertyNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// This file is part of YamlDotNet - A .NET library for YAML.
// Copyright (c) Antoine Aubry and contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

using System;
using YamlDotNet.Core.Events;

namespace YamlDotNet.Core
{
/// <summary>
/// Exception that is thrown when a property is not found in the given type
/// </summary>
public sealed class PropertyNotFoundException : YamlException
{
public PropertyNotFoundException(Scalar propertyName, Type type) : base(propertyName.Start, propertyName.End, $"Property '{propertyName.Value}' not found on type '{type.FullName}'.")
{
}
}
}
9 changes: 5 additions & 4 deletions YamlDotNet/Serialization/ITypeInspector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using YamlDotNet.Core.Events;

namespace YamlDotNet.Serialization
{
Expand All @@ -43,12 +44,12 @@ public interface ITypeInspector
/// </summary>
/// <param name="type">The type whose properties are to be searched.</param>
/// <param name="container">The actual object of type <paramref name="type"/> whose properties are to be searched. Can be null.</param>
/// <param name="name">The name of the property.</param>
/// <param name="propertyName">The name of the property.</param>
/// <param name="ignoreUnmatched">
/// Determines if an exception or null should be returned if <paramref name="name"/> can't be
/// found in <paramref name="type"/>
/// Determines if an exception or null should be returned if <paramref name="propertyName"/> can't be
/// found in <paramref name="type"/>
/// </param>
/// <returns></returns>
IPropertyDescriptor GetProperty(Type type, object? container, string name, [MaybeNullWhen(true)] bool ignoreUnmatched);
IPropertyDescriptor GetProperty(Type type, object? container, Scalar propertyName, [MaybeNullWhen(true)] bool ignoreUnmatched);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ bool INodeDeserializer.Deserialize(IParser parser, Type expectedType, Func<IPars
while (!parser.TryConsume<MappingEnd>(out var _))
{
var propertyName = parser.Consume<Scalar>();
var property = typeDescriptor.GetProperty(implementationType, null, propertyName.Value, ignoreUnmatched);
var property = typeDescriptor.GetProperty(implementationType, null, propertyName, ignoreUnmatched);
if (property == null)
{
parser.SkipThisAndNestedEvents();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ public class MappingNodeTypeResolver : INodeTypeResolver

public MappingNodeTypeResolver(IDictionary<Type, Type> mappings)
{
if (mappings == null) throw new ArgumentNullException(nameof(mappings));
if (mappings == null)
{
throw new ArgumentNullException(nameof(mappings));
}

foreach (var pair in mappings)
{
Expand Down
13 changes: 6 additions & 7 deletions YamlDotNet/Serialization/TypeInspectors/TypeInspectorSkeleton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;

namespace YamlDotNet.Serialization.TypeInspectors
{
public abstract class TypeInspectorSkeleton : ITypeInspector
{
public abstract IEnumerable<IPropertyDescriptor> GetProperties(Type type, object? container);

public IPropertyDescriptor GetProperty(Type type, object? container, string name, [MaybeNullWhen(true)] bool ignoreUnmatched)
public IPropertyDescriptor GetProperty(Type type, object? container, Scalar propertyName, [MaybeNullWhen(true)] bool ignoreUnmatched)
{
var candidates = GetProperties(type, container)
.Where(p => p.Name == name);
var candidates = GetProperties(type, container).Where(p => p.Name == propertyName.Value);

using var enumerator = candidates.GetEnumerator();
if (!enumerator.MoveNext())
Expand All @@ -44,16 +45,14 @@ public IPropertyDescriptor GetProperty(Type type, object? container, string name
return null!;
}

throw new SerializationException($"Property '{name}' not found on type '{type.FullName}'.");
throw new PropertyNotFoundException(propertyName, type);
}

var property = enumerator.Current;

if (enumerator.MoveNext())
{
throw new SerializationException(
$"Multiple properties with the name/alias '{name}' already exists on type '{type.FullName}', maybe you're misusing YamlAlias or maybe you are using the wrong naming convention? The matching properties are: {string.Join(", ", candidates.Select(p => p.Name).ToArray())}"
);
throw new MultiplePropertiesFoundException(propertyName, type, candidates);
}

return property;
Expand Down

0 comments on commit 573515f

Please sign in to comment.