Skip to content

Commit 1e4bf40

Browse files
committed
Added on onserialized/onserializing and ondeserialized/ondeserializing
1 parent e856ff3 commit 1e4bf40

File tree

17 files changed

+427
-12
lines changed

17 files changed

+427
-12
lines changed

YamlDotNet.Analyzers.StaticGenerator/ClassObject.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,32 @@ public class ClassObject
3030
public List<IFieldSymbol> FieldSymbols { get; }
3131
public string FullName { get; }
3232
public string GuidSuffix { get; }
33-
public ITypeSymbol ModuleSymbol { get; }
34-
public List<IPropertySymbol> PropertySymbols { get; }
35-
public string SanitizedClassName { get; }
3633
public bool IsArray { get; }
3734
public bool IsDictionary { get; }
3835
public bool IsList { get; }
36+
public ITypeSymbol ModuleSymbol { get; }
37+
public List<IMethodSymbol> OnDeserializedMethods { get; }
38+
public List<IMethodSymbol> OnDeserializingMethods { get; }
39+
public List<IMethodSymbol> OnSerializedMethods { get; }
40+
public List<IMethodSymbol> OnSerializingMethods { get; }
41+
public List<IPropertySymbol> PropertySymbols { get; }
42+
public string SanitizedClassName { get; }
3943

4044
public ClassObject(string sanitizedClassName, ITypeSymbol moduleSymbol, bool isDictionary = false, bool isList = false, bool isArray = false)
4145
{
4246
FieldSymbols = new List<IFieldSymbol>();
4347
PropertySymbols = new List<IPropertySymbol>();
4448
FullName = moduleSymbol.GetFullName() ?? string.Empty;
4549
GuidSuffix = Guid.NewGuid().ToString("N");
46-
ModuleSymbol = moduleSymbol;
47-
SanitizedClassName = sanitizedClassName;
4850
IsDictionary = isDictionary;
4951
IsList = isList;
5052
IsArray = isArray;
53+
ModuleSymbol = moduleSymbol;
54+
OnDeserializedMethods = new List<IMethodSymbol>();
55+
OnDeserializingMethods = new List<IMethodSymbol>();
56+
OnSerializedMethods = new List<IMethodSymbol>();
57+
OnSerializingMethods = new List<IMethodSymbol>();
58+
SanitizedClassName = sanitizedClassName;
5159
}
5260
}
5361
}

YamlDotNet.Analyzers.StaticGenerator/ClassSyntaxReceiver.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
6565
foreach (var member in members)
6666
{
6767
if (member.IsStatic ||
68-
member.DeclaredAccessibility != Accessibility.Public ||
68+
(member.DeclaredAccessibility != Accessibility.Public &&
69+
member.DeclaredAccessibility != Accessibility.Internal) ||
6970
member.GetAttributes().Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.YamlIgnoreAttribute"))
7071
{
7172
continue;
@@ -81,6 +82,26 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
8182
classObject.FieldSymbols.Add(fieldSymbol);
8283
CheckForSupportedGeneric(fieldSymbol.Type);
8384
}
85+
else if (member is IMethodSymbol methodSymbol)
86+
{
87+
var methodAttributes = methodSymbol.GetAttributes();
88+
if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnDeserializedAttribute"))
89+
{
90+
classObject.OnDeserializedMethods.Add(methodSymbol);
91+
}
92+
if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnDeserializingAttribute"))
93+
{
94+
classObject.OnDeserializingMethods.Add(methodSymbol);
95+
}
96+
if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnSerializedAttribute"))
97+
{
98+
classObject.OnSerializedMethods.Add(methodSymbol);
99+
}
100+
if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnSerializingAttribute"))
101+
{
102+
classObject.OnSerializingMethods.Add(methodSymbol);
103+
}
104+
}
84105
}
85106
classSymbol = classSymbol.BaseType;
86107
}

YamlDotNet.Analyzers.StaticGenerator/StaticObjectFactoryFile.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
// SOFTWARE.
2121

2222
using System;
23+
using System.Collections.Generic;
2324
using System.Linq;
2425
using Microsoft.CodeAnalysis;
2526

@@ -168,7 +169,36 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver)
168169

169170
Write("throw new ArgumentOutOfRangeException(\"Unknown type: \" + type.ToString());");
170171
UnIndent(); Write("}");
172+
WriteExecuteMethod(classSyntaxReceiver, "ExecuteOnDeserializing", (c) => c.OnDeserializingMethods);
173+
WriteExecuteMethod(classSyntaxReceiver, "ExecuteOnDeserialized", (c) => c.OnDeserializedMethods);
174+
WriteExecuteMethod(classSyntaxReceiver, "ExecuteOnSerializing", (c) => c.OnSerializingMethods);
175+
WriteExecuteMethod(classSyntaxReceiver, "ExecuteOnSerialized", (c) => c.OnSerializedMethods);
176+
UnIndent(); Write("}");
177+
}
171178

179+
private void WriteExecuteMethod(ClassSyntaxReceiver classSyntaxReceiver, string methodName, Func<ClassObject, IEnumerable<IMethodSymbol>> selector)
180+
{
181+
Write($"public override void {methodName}(object value)");
182+
Write("{"); Indent();
183+
Write("if (value == null) return;");
184+
Write("var type = value.GetType();");
185+
foreach (var o in classSyntaxReceiver.Classes)
186+
{
187+
var classObject = o.Value;
188+
var methods = selector(classObject);
189+
if (methods.Any())
190+
{
191+
var className = classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty);
192+
Write($"if (type == typeof({className}))");
193+
Write("{"); Indent();
194+
foreach (var m in methods)
195+
{
196+
Write($"(({className})value).{m.Name}();");
197+
}
198+
Write("return;");
199+
UnIndent(); Write("}");
200+
}
201+
}
172202
UnIndent(); Write("}");
173203
}
174204
}

YamlDotNet.Core7AoTCompileTest/Program.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,11 @@
2323
#pragma warning disable CS8602 // Possible null reference argument.
2424

2525
using System;
26-
using System.Collections;
2726
using System.Collections.Generic;
2827
using System.IO;
29-
using System.Reflection;
30-
using System.Security.Cryptography.X509Certificates;
3128
using YamlDotNet.Core;
32-
using YamlDotNet.Core.Events;
3329
using YamlDotNet.Serialization;
34-
using YamlDotNet.Serialization.NodeDeserializers;
30+
using YamlDotNet.Serialization.Callbacks;
3531

3632
string yaml = $@"MyBool: true
3733
hi: 1
@@ -203,6 +199,32 @@ public class InheritedBase
203199
public class Inherited : InheritedBase
204200
{
205201
public string NotInherited { get; set; }
202+
203+
204+
[OnSerializing]
205+
public void Serializing()
206+
{
207+
Console.WriteLine("Serializing");
208+
}
209+
210+
[OnSerialized]
211+
public void Serialized()
212+
{
213+
Console.WriteLine("Serialized");
214+
}
215+
216+
[OnDeserialized]
217+
public void Deserialized()
218+
{
219+
Console.WriteLine("Deserialized");
220+
}
221+
222+
[OnDeserializing]
223+
public void Deserializing()
224+
{
225+
Console.WriteLine("Deserializing");
226+
}
227+
206228
}
207229

208230
public enum MyTestEnum

YamlDotNet.Test/Analyzers/StaticGenerator/ObjectTests.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
// SOFTWARE.
2121
using Xunit;
2222
using YamlDotNet.Serialization;
23+
using YamlDotNet.Serialization.Callbacks;
2324

2425
namespace YamlDotNet.Test.Analyzers.StaticGenerator
2526
{
@@ -78,6 +79,46 @@ public void RegularObjectWorks()
7879
";
7980
Assert.Equal(yaml.NormalizeNewLines().TrimNewLines(), actualYaml.NormalizeNewLines().TrimNewLines());
8081
}
82+
83+
[Fact]
84+
public void CallbacksAreExecuted()
85+
{
86+
var yaml = "Test: Hi";
87+
var deserializer = new StaticDeserializerBuilder(new StaticContext()).Build();
88+
var test = deserializer.Deserialize<TestState>(yaml);
89+
90+
Assert.Equal(1, test.OnDeserializedCallCount);
91+
Assert.Equal(1, test.OnDeserializingCallCount);
92+
93+
var serializer = new StaticSerializerBuilder(new StaticContext()).Build();
94+
yaml = serializer.Serialize(test);
95+
Assert.Equal(1, test.OnSerializedCallCount);
96+
Assert.Equal(1, test.OnSerializingCallCount);
97+
}
98+
99+
[YamlSerializable]
100+
public class TestState
101+
{
102+
public int OnDeserializedCallCount { get; set; }
103+
public int OnDeserializingCallCount { get; set; }
104+
public int OnSerializedCallCount { get; set; }
105+
public int OnSerializingCallCount { get; set; }
106+
107+
public string Test { get; set; } = string.Empty;
108+
109+
[OnDeserialized]
110+
public void Deserialized() => OnDeserializedCallCount++;
111+
112+
[OnDeserializing]
113+
public void Deserializing() => OnDeserializingCallCount++;
114+
115+
[OnSerialized]
116+
public void Serialized() => OnSerializedCallCount++;
117+
118+
[OnSerializing]
119+
public void Serializing() => OnSerializingCallCount++;
120+
}
121+
81122
}
82123
public class InheritedClass
83124
{

YamlDotNet.Test/Serialization/DeserializerTest.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
using Xunit;
2727
using YamlDotNet.Core;
2828
using YamlDotNet.Serialization;
29+
using YamlDotNet.Serialization.Callbacks;
2930
using YamlDotNet.Serialization.NamingConventions;
3031

3132
namespace YamlDotNet.Test.Serialization
@@ -331,7 +332,32 @@ public void DeserializeWithoutDuplicateKeyChecking_YamlWithDuplicateKeys_DoesNot
331332
act = () => sut.Deserialize<Dictionary<string, Dictionary<string, string>>>(parser);
332333
act.ShouldNotThrow<YamlException>("Because duplicate key checking is not enabled");
333334
}
334-
335+
336+
[Fact]
337+
public void SerializeStateMethodsGetCalledOnce()
338+
{
339+
var yaml = "Test: Hi";
340+
var deserializer = new DeserializerBuilder().Build();
341+
var test = deserializer.Deserialize<TestState>(yaml);
342+
343+
Assert.Equal(1, test.OnDeserializedCallCount);
344+
Assert.Equal(1, test.OnDeserializingCallCount);
345+
}
346+
347+
public class TestState
348+
{
349+
public int OnDeserializedCallCount { get; set; }
350+
public int OnDeserializingCallCount { get; set; }
351+
352+
public string Test { get; set; } = string.Empty;
353+
354+
[OnDeserialized]
355+
public void Deserialized() => OnDeserializedCallCount++;
356+
357+
[OnDeserializing]
358+
public void Deserializing() => OnDeserializingCallCount++;
359+
}
360+
335361
public class Test
336362
{
337363
public string Value { get; set; }

YamlDotNet.Test/Serialization/SerializationTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
using YamlDotNet.Core;
3838
using YamlDotNet.Core.Events;
3939
using YamlDotNet.Serialization;
40+
using YamlDotNet.Serialization.Callbacks;
4041
using YamlDotNet.Serialization.NamingConventions;
4142
using YamlDotNet.Serialization.ObjectFactories;
4243

@@ -2395,6 +2396,31 @@ public void KeysOnConcreteClassDontGetQuoted_TypeBoolDoesNotGetQuoted()
23952396
result.Should().Be($"True: {Environment.NewLine}False: hello{Environment.NewLine}Null: true{Environment.NewLine}");
23962397
}
23972398

2399+
[Fact]
2400+
public void SerializeStateMethodsGetCalledOnce()
2401+
{
2402+
var serializer = new SerializerBuilder().Build();
2403+
var test = new TestState();
2404+
serializer.Serialize(test);
2405+
2406+
Assert.Equal(1, test.OnSerializedCallCount);
2407+
Assert.Equal(1, test.OnSerializingCallCount);
2408+
}
2409+
2410+
public class TestState
2411+
{
2412+
public int OnSerializedCallCount { get; set; }
2413+
public int OnSerializingCallCount { get; set; }
2414+
2415+
public string Test { get; set; } = string.Empty;
2416+
2417+
[OnSerialized]
2418+
public void Serialized() => OnSerializedCallCount++;
2419+
2420+
[OnSerializing]
2421+
public void Serializing() => OnSerializingCallCount++;
2422+
}
2423+
23982424
public class ReservedWordsTestClass<TNullType>
23992425
{
24002426
public string True { get; set; }
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
24+
namespace YamlDotNet.Serialization.Callbacks
25+
{
26+
[AttributeUsage(AttributeTargets.Method)]
27+
public sealed class OnDeserializedAttribute : Attribute
28+
{
29+
}
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
24+
namespace YamlDotNet.Serialization.Callbacks
25+
{
26+
[AttributeUsage(AttributeTargets.Method)]
27+
public sealed class OnDeserializingAttribute : Attribute
28+
{
29+
}
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
24+
namespace YamlDotNet.Serialization.Callbacks
25+
{
26+
[AttributeUsage(AttributeTargets.Method)]
27+
public sealed class OnSerializedAttribute : Attribute
28+
{
29+
}
30+
}

0 commit comments

Comments
 (0)