Skip to content

Commit 2e317a5

Browse files
authored
Merge pull request #960 from julian-burger-ttd/ttd-fix-property-override-handling
Fix property handling when there are hidden properties
2 parents ae070f1 + d99ee83 commit 2e317a5

File tree

2 files changed

+111
-1
lines changed

2 files changed

+111
-1
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// This file is part of YamlDotNet - A .NET library for YAML.
2+
// Copyright (c) Antoine Aubry and contributors
3+
//
4+
// Permission is hereby granted, free of charge, to any person obtaining a copy of
5+
// this software and associated documentation files (the "Software"), to deal in
6+
// the Software without restriction, including without limitation the rights to
7+
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8+
// of the Software, and to permit persons to whom the Software is furnished to do
9+
// so, subject to the following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included in all
12+
// copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
// SOFTWARE.
21+
22+
using System;
23+
using FluentAssertions;
24+
using Xunit;
25+
using YamlDotNet.Core;
26+
using YamlDotNet.Serialization;
27+
using YamlDotNet.Serialization.NamingConventions;
28+
29+
namespace YamlDotNet.Test.Serialization
30+
{
31+
public class HiddenPropertyTests
32+
{
33+
public class HiddenPropertyBase
34+
{
35+
protected object value;
36+
37+
public object Value => this.value;
38+
39+
[YamlIgnore]
40+
public object SetOnly { set => this.value = value; }
41+
42+
[YamlIgnore]
43+
public object GetAndSet { get; set; }
44+
}
45+
46+
public class HiddenPropertyDerived<T> : HiddenPropertyBase
47+
{
48+
public new T Value { get => (T)this.value; }
49+
public new T SetOnly { set => this.value = value; }
50+
public new T GetAndSet { get; set; }
51+
}
52+
53+
public class DuplicatePropertyBase
54+
{
55+
protected object value;
56+
57+
public object Value => this.value;
58+
public object SetOnly { set => this.value = value; }
59+
public object GetAndSet { get; set; }
60+
}
61+
62+
public class DuplicatePropertyDerived<T> : DuplicatePropertyBase
63+
{
64+
public new T Value { get => (T)this.value; }
65+
public new T SetOnly { set => this.value = value; }
66+
public new T GetAndSet { get; set; }
67+
}
68+
69+
[Fact]
70+
public void TestHidden()
71+
{
72+
var yaml = @"
73+
setOnly: set
74+
getAndSet: getAndSet
75+
";
76+
var d = new DeserializerBuilder()
77+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
78+
.Build();
79+
80+
var o = d.Deserialize<HiddenPropertyDerived<string>>(yaml);
81+
Assert.Equal("set", o.Value);
82+
Assert.Equal("getAndSet", o.GetAndSet);
83+
}
84+
85+
[Fact]
86+
public void TestDuplicate()
87+
{
88+
var yaml = @"
89+
setOnly: set
90+
getAndSet: getAndSet
91+
";
92+
var d = new DeserializerBuilder()
93+
.WithNamingConvention(CamelCaseNamingConvention.Instance)
94+
.Build();
95+
96+
Action action = () => { d.Deserialize<DuplicatePropertyDerived<string>>(yaml); };
97+
action.ShouldThrow<YamlException>();
98+
}
99+
}
100+
}

YamlDotNet/ReflectionExtensions.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,10 @@ public static Type[] GetGenericArguments(this Type type)
185185

186186
public static PropertyInfo? GetPublicProperty(this Type type, string name)
187187
{
188-
return type.GetRuntimeProperty(name);
188+
// In the case of property hiding, get the most-derived implementation.
189+
return type
190+
.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)
191+
.FirstOrDefault(p => p.Name == name);
189192
}
190193

191194
public static FieldInfo? GetPublicStaticField(this Type type, string name)
@@ -290,6 +293,13 @@ public static Attribute[] GetAllCustomAttributes<TAttribute>(this PropertyInfo m
290293
if (property != null)
291294
{
292295
result.AddRange(property.GetCustomAttributes(typeof(TAttribute)));
296+
297+
if ((property.GetGetMethod()?.IsHideBySig == true)
298+
|| (property.GetSetMethod()?.IsHideBySig == true))
299+
{
300+
// Don't continue up the hierarchy.
301+
break;
302+
}
293303
}
294304

295305
type = type.BaseType();

0 commit comments

Comments
 (0)