Skip to content

Commit

Permalink
Merge pull request #843 from EdwardCooke/ec-785-callbacks
Browse files Browse the repository at this point in the history
Added on onserialized/onserializing and ondeserialized/ondeserializing

+semver:feature
  • Loading branch information
EdwardCooke committed Sep 2, 2023
2 parents e856ff3 + 1e4bf40 commit 1a73db7
Show file tree
Hide file tree
Showing 17 changed files with 427 additions and 12 deletions.
18 changes: 13 additions & 5 deletions YamlDotNet.Analyzers.StaticGenerator/ClassObject.cs
Expand Up @@ -30,24 +30,32 @@ public class ClassObject
public List<IFieldSymbol> FieldSymbols { get; }
public string FullName { get; }
public string GuidSuffix { get; }
public ITypeSymbol ModuleSymbol { get; }
public List<IPropertySymbol> PropertySymbols { get; }
public string SanitizedClassName { get; }
public bool IsArray { get; }
public bool IsDictionary { get; }
public bool IsList { get; }
public ITypeSymbol ModuleSymbol { get; }
public List<IMethodSymbol> OnDeserializedMethods { get; }
public List<IMethodSymbol> OnDeserializingMethods { get; }
public List<IMethodSymbol> OnSerializedMethods { get; }
public List<IMethodSymbol> OnSerializingMethods { get; }
public List<IPropertySymbol> PropertySymbols { get; }
public string SanitizedClassName { get; }

public ClassObject(string sanitizedClassName, ITypeSymbol moduleSymbol, bool isDictionary = false, bool isList = false, bool isArray = false)
{
FieldSymbols = new List<IFieldSymbol>();
PropertySymbols = new List<IPropertySymbol>();
FullName = moduleSymbol.GetFullName() ?? string.Empty;
GuidSuffix = Guid.NewGuid().ToString("N");
ModuleSymbol = moduleSymbol;
SanitizedClassName = sanitizedClassName;
IsDictionary = isDictionary;
IsList = isList;
IsArray = isArray;
ModuleSymbol = moduleSymbol;
OnDeserializedMethods = new List<IMethodSymbol>();
OnDeserializingMethods = new List<IMethodSymbol>();
OnSerializedMethods = new List<IMethodSymbol>();
OnSerializingMethods = new List<IMethodSymbol>();
SanitizedClassName = sanitizedClassName;
}
}
}
23 changes: 22 additions & 1 deletion YamlDotNet.Analyzers.StaticGenerator/ClassSyntaxReceiver.cs
Expand Up @@ -65,7 +65,8 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
foreach (var member in members)
{
if (member.IsStatic ||
member.DeclaredAccessibility != Accessibility.Public ||
(member.DeclaredAccessibility != Accessibility.Public &&
member.DeclaredAccessibility != Accessibility.Internal) ||
member.GetAttributes().Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.YamlIgnoreAttribute"))
{
continue;
Expand All @@ -81,6 +82,26 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
classObject.FieldSymbols.Add(fieldSymbol);
CheckForSupportedGeneric(fieldSymbol.Type);
}
else if (member is IMethodSymbol methodSymbol)
{
var methodAttributes = methodSymbol.GetAttributes();
if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnDeserializedAttribute"))
{
classObject.OnDeserializedMethods.Add(methodSymbol);
}
if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnDeserializingAttribute"))
{
classObject.OnDeserializingMethods.Add(methodSymbol);
}
if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnSerializedAttribute"))
{
classObject.OnSerializedMethods.Add(methodSymbol);
}
if (methodAttributes.Any(x => x.AttributeClass!.ToDisplayString() == "YamlDotNet.Serialization.Callbacks.OnSerializingAttribute"))
{
classObject.OnSerializingMethods.Add(methodSymbol);
}
}
}
classSymbol = classSymbol.BaseType;
}
Expand Down
30 changes: 30 additions & 0 deletions YamlDotNet.Analyzers.StaticGenerator/StaticObjectFactoryFile.cs
Expand Up @@ -20,6 +20,7 @@
// SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;

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

Write("throw new ArgumentOutOfRangeException(\"Unknown type: \" + type.ToString());");
UnIndent(); Write("}");
WriteExecuteMethod(classSyntaxReceiver, "ExecuteOnDeserializing", (c) => c.OnDeserializingMethods);
WriteExecuteMethod(classSyntaxReceiver, "ExecuteOnDeserialized", (c) => c.OnDeserializedMethods);
WriteExecuteMethod(classSyntaxReceiver, "ExecuteOnSerializing", (c) => c.OnSerializingMethods);
WriteExecuteMethod(classSyntaxReceiver, "ExecuteOnSerialized", (c) => c.OnSerializedMethods);
UnIndent(); Write("}");
}

private void WriteExecuteMethod(ClassSyntaxReceiver classSyntaxReceiver, string methodName, Func<ClassObject, IEnumerable<IMethodSymbol>> selector)
{
Write($"public override void {methodName}(object value)");
Write("{"); Indent();
Write("if (value == null) return;");
Write("var type = value.GetType();");
foreach (var o in classSyntaxReceiver.Classes)
{
var classObject = o.Value;
var methods = selector(classObject);
if (methods.Any())
{
var className = classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty);
Write($"if (type == typeof({className}))");
Write("{"); Indent();
foreach (var m in methods)
{
Write($"(({className})value).{m.Name}();");
}
Write("return;");
UnIndent(); Write("}");
}
}
UnIndent(); Write("}");
}
}
Expand Down
32 changes: 27 additions & 5 deletions YamlDotNet.Core7AoTCompileTest/Program.cs
Expand Up @@ -23,15 +23,11 @@
#pragma warning disable CS8602 // Possible null reference argument.

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.Callbacks;

string yaml = $@"MyBool: true
hi: 1
Expand Down Expand Up @@ -203,6 +199,32 @@ public class InheritedBase
public class Inherited : InheritedBase
{
public string NotInherited { get; set; }


[OnSerializing]
public void Serializing()
{
Console.WriteLine("Serializing");
}

[OnSerialized]
public void Serialized()
{
Console.WriteLine("Serialized");
}

[OnDeserialized]
public void Deserialized()
{
Console.WriteLine("Deserialized");
}

[OnDeserializing]
public void Deserializing()
{
Console.WriteLine("Deserializing");
}

}

public enum MyTestEnum
Expand Down
41 changes: 41 additions & 0 deletions YamlDotNet.Test/Analyzers/StaticGenerator/ObjectTests.cs
Expand Up @@ -20,6 +20,7 @@
// SOFTWARE.
using Xunit;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.Callbacks;

namespace YamlDotNet.Test.Analyzers.StaticGenerator
{
Expand Down Expand Up @@ -78,6 +79,46 @@ public void RegularObjectWorks()
";
Assert.Equal(yaml.NormalizeNewLines().TrimNewLines(), actualYaml.NormalizeNewLines().TrimNewLines());
}

[Fact]
public void CallbacksAreExecuted()
{
var yaml = "Test: Hi";
var deserializer = new StaticDeserializerBuilder(new StaticContext()).Build();
var test = deserializer.Deserialize<TestState>(yaml);

Assert.Equal(1, test.OnDeserializedCallCount);
Assert.Equal(1, test.OnDeserializingCallCount);

var serializer = new StaticSerializerBuilder(new StaticContext()).Build();
yaml = serializer.Serialize(test);
Assert.Equal(1, test.OnSerializedCallCount);
Assert.Equal(1, test.OnSerializingCallCount);
}

[YamlSerializable]
public class TestState
{
public int OnDeserializedCallCount { get; set; }
public int OnDeserializingCallCount { get; set; }
public int OnSerializedCallCount { get; set; }
public int OnSerializingCallCount { get; set; }

public string Test { get; set; } = string.Empty;

[OnDeserialized]
public void Deserialized() => OnDeserializedCallCount++;

[OnDeserializing]
public void Deserializing() => OnDeserializingCallCount++;

[OnSerialized]
public void Serialized() => OnSerializedCallCount++;

[OnSerializing]
public void Serializing() => OnSerializingCallCount++;
}

}
public class InheritedClass
{
Expand Down
28 changes: 27 additions & 1 deletion YamlDotNet.Test/Serialization/DeserializerTest.cs
Expand Up @@ -26,6 +26,7 @@
using Xunit;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.NamingConventions;

namespace YamlDotNet.Test.Serialization
Expand Down Expand Up @@ -331,7 +332,32 @@ public void DeserializeWithoutDuplicateKeyChecking_YamlWithDuplicateKeys_DoesNot
act = () => sut.Deserialize<Dictionary<string, Dictionary<string, string>>>(parser);
act.ShouldNotThrow<YamlException>("Because duplicate key checking is not enabled");
}


[Fact]
public void SerializeStateMethodsGetCalledOnce()
{
var yaml = "Test: Hi";
var deserializer = new DeserializerBuilder().Build();
var test = deserializer.Deserialize<TestState>(yaml);

Assert.Equal(1, test.OnDeserializedCallCount);
Assert.Equal(1, test.OnDeserializingCallCount);
}

public class TestState
{
public int OnDeserializedCallCount { get; set; }
public int OnDeserializingCallCount { get; set; }

public string Test { get; set; } = string.Empty;

[OnDeserialized]
public void Deserialized() => OnDeserializedCallCount++;

[OnDeserializing]
public void Deserializing() => OnDeserializingCallCount++;
}

public class Test
{
public string Value { get; set; }
Expand Down
26 changes: 26 additions & 0 deletions YamlDotNet.Test/Serialization/SerializationTests.cs
Expand Up @@ -37,6 +37,7 @@
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.ObjectFactories;

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

[Fact]
public void SerializeStateMethodsGetCalledOnce()
{
var serializer = new SerializerBuilder().Build();
var test = new TestState();
serializer.Serialize(test);

Assert.Equal(1, test.OnSerializedCallCount);
Assert.Equal(1, test.OnSerializingCallCount);
}

public class TestState
{
public int OnSerializedCallCount { get; set; }
public int OnSerializingCallCount { get; set; }

public string Test { get; set; } = string.Empty;

[OnSerialized]
public void Serialized() => OnSerializedCallCount++;

[OnSerializing]
public void Serializing() => OnSerializingCallCount++;
}

public class ReservedWordsTestClass<TNullType>
{
public string True { get; set; }
Expand Down
30 changes: 30 additions & 0 deletions YamlDotNet/Serialization/Callbacks/OnDeserializedAttribute.cs
@@ -0,0 +1,30 @@
// 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;

namespace YamlDotNet.Serialization.Callbacks
{
[AttributeUsage(AttributeTargets.Method)]
public sealed class OnDeserializedAttribute : Attribute
{
}
}
30 changes: 30 additions & 0 deletions YamlDotNet/Serialization/Callbacks/OnDeserializingAttribute.cs
@@ -0,0 +1,30 @@
// 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;

namespace YamlDotNet.Serialization.Callbacks
{
[AttributeUsage(AttributeTargets.Method)]
public sealed class OnDeserializingAttribute : Attribute
{
}
}
30 changes: 30 additions & 0 deletions YamlDotNet/Serialization/Callbacks/OnSerializedAttribute.cs
@@ -0,0 +1,30 @@
// 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;

namespace YamlDotNet.Serialization.Callbacks
{
[AttributeUsage(AttributeTargets.Method)]
public sealed class OnSerializedAttribute : Attribute
{
}
}

0 comments on commit 1a73db7

Please sign in to comment.