From 63f71e2b675cce1680c20c15a4648607c09ee610 Mon Sep 17 00:00:00 2001 From: Christian Prochnow Date: Fri, 6 Oct 2023 11:30:29 +0200 Subject: [PATCH 1/2] Added support for serialization and deserialization of 'object' to static code generation. --- .../StaticContextFile.cs | 46 +++++++++++++++-- .../StaticObjectFactoryFile.cs | 48 ++++++++++++----- .../StaticPropertyDescriptorFile.cs | 8 ++- .../StaticTypeInspectorFile.cs | 8 ++- .../StaticTypeResolverFile.cs | 51 +++++++++++++++++++ .../TypeFactoryGenerator.cs | 1 + YamlDotNet.Core7AoTCompileTest/Program.cs | 6 +++ .../Analyzers/StaticGenerator/ObjectTests.cs | 32 +++++++++++- .../StaticGenerator/RootCollectionTests.cs | 30 +++++++++++ YamlDotNet/Serialization/StaticContext.cs | 19 +++++++ .../StaticDeserializerBuilder.cs | 2 +- .../TypeResolvers/StaticTypeResolver.cs | 28 ++++++++-- 12 files changed, 255 insertions(+), 24 deletions(-) create mode 100644 YamlDotNet.Analyzers.StaticGenerator/StaticTypeResolverFile.cs diff --git a/YamlDotNet.Analyzers.StaticGenerator/StaticContextFile.cs b/YamlDotNet.Analyzers.StaticGenerator/StaticContextFile.cs index e300f8f7..eb4be5db 100644 --- a/YamlDotNet.Analyzers.StaticGenerator/StaticContextFile.cs +++ b/YamlDotNet.Analyzers.StaticGenerator/StaticContextFile.cs @@ -34,10 +34,48 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver) { Write($"public partial class {classSyntaxReceiver.YamlStaticContextType?.Name ?? "StaticContext"} : YamlDotNet.Serialization.StaticContext"); Write("{"); Indent(); - Write("public YamlDotNet.Serialization.ObjectFactories.StaticObjectFactory ObjectFactory { get; } = new StaticObjectFactory();"); - Write("public StaticTypeInspector TypeInspector { get; } = new StaticTypeInspector();"); - Write("public override YamlDotNet.Serialization.ObjectFactories.StaticObjectFactory GetFactory() => ObjectFactory;"); - Write("public override YamlDotNet.Serialization.ITypeInspector GetTypeInspector() => TypeInspector;"); + Write("private readonly YamlDotNet.Serialization.ObjectFactories.StaticObjectFactory _objectFactory;"); + Write("private readonly YamlDotNet.Serialization.ITypeResolver _typeResolver;"); + Write("private readonly YamlDotNet.Serialization.ITypeInspector _typeInspector;"); + Write($"public {classSyntaxReceiver.YamlStaticContextType?.Name ?? "StaticContext"}()"); + Write("{"); Indent(); + Write("_objectFactory = new StaticObjectFactory();"); + Write("_typeResolver = new StaticTypeResolver(this);"); + Write("_typeInspector = new StaticTypeInspector(_typeResolver);"); + UnIndent(); Write("}"); + Write("public override YamlDotNet.Serialization.ObjectFactories.StaticObjectFactory GetFactory() => _objectFactory;"); + Write("public override YamlDotNet.Serialization.ITypeInspector GetTypeInspector() => _typeInspector;"); + Write("public override YamlDotNet.Serialization.ITypeResolver GetTypeResolver() => _typeResolver;"); + Write("public override bool IsKnownType(Type type)"); + Write("{"); Indent(); + foreach (var o in classSyntaxReceiver.Classes) + { + var classObject = o.Value; + if (classObject.IsArray) + { + Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return true;"); + } + else if (classObject.IsList) + { + Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return true;"); + } + else if (classObject.IsDictionary) + { + Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return true;"); + } + else + { + Write($"if (type == typeof({o.Value.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return true;"); + //always support a array, list and dictionary of the type + Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)}[])) return true;"); + Write($"if (type == typeof(System.Collections.Generic.List<{classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)}>)) return true;"); + Write($"if (type == typeof(System.Collections.Generic.Dictionary)) return true;"); + } + } + // always support dictionary object + Write("if (type == typeof(System.Collections.Generic.Dictionary)) return true;"); + Write("return false;"); + UnIndent(); Write("}"); UnIndent(); Write("}"); } } diff --git a/YamlDotNet.Analyzers.StaticGenerator/StaticObjectFactoryFile.cs b/YamlDotNet.Analyzers.StaticGenerator/StaticObjectFactoryFile.cs index 60c5c529..01dafdfb 100644 --- a/YamlDotNet.Analyzers.StaticGenerator/StaticObjectFactoryFile.cs +++ b/YamlDotNet.Analyzers.StaticGenerator/StaticObjectFactoryFile.cs @@ -43,8 +43,12 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver) { var classObject = o.Value; Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return new {classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)}();"); + //always support a list and dictionary of the type Write($"if (type == typeof(System.Collections.Generic.List<{classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)}>)) return new System.Collections.Generic.List<{classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)}>();"); + Write($"if (type == typeof(System.Collections.Generic.Dictionary)) return new System.Collections.Generic.Dictionary();"); } + // always support dictionary when deserializing object + Write("if (type == typeof(System.Collections.Generic.Dictionary)) return new System.Collections.Generic.Dictionary();"); Write($"throw new ArgumentOutOfRangeException(\"Unknown type: \" + type.ToString());"); UnIndent(); Write("}"); @@ -67,10 +71,21 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver) Write("public override bool IsDictionary(Type type)"); Write("{"); Indent(); - foreach (var o in classSyntaxReceiver.Classes.Where(c => c.Value.IsDictionary)) + foreach (var o in classSyntaxReceiver.Classes) { - Write($"if (type == typeof({o.Value.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return true;"); + var classObject = o.Value; + if (classObject.IsDictionary) + { + Write($"if (type == typeof({o.Value.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return true;"); + } + else + { + //always support a dictionary of the type + Write($"if (type == typeof(System.Collections.Generic.Dictionary)) return true;"); + } } + // always support dictionary object + Write("if (type == typeof(System.Collections.Generic.Dictionary)) return true;"); Write("return false;"); UnIndent(); Write("}"); @@ -79,7 +94,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver) foreach (var o in classSyntaxReceiver.Classes) { var classObject = o.Value; - if (o.Value.IsArray) + if (classObject.IsArray) { Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return true;"); } @@ -115,20 +130,27 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver) foreach (var o in classSyntaxReceiver.Classes) { var classObject = o.Value; - if (!classObject.IsDictionary) + if (classObject.IsDictionary) { - continue; - } + var keyType = "object"; + var type = (INamedTypeSymbol)classObject.ModuleSymbol; - var keyType = "object"; - var type = (INamedTypeSymbol)classObject.ModuleSymbol; + if (type.IsGenericType) + { + keyType = type.TypeArguments[0].GetFullName().Replace("?", string.Empty); + } - if (type.IsGenericType) + Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return typeof({keyType});"); + } + else if (!classObject.IsArray && !classObject.IsList) { - keyType = type.TypeArguments[0].GetFullName().Replace("?", string.Empty); + //always support a dictionary of the type + Write($"if (type == typeof(System.Collections.Generic.Dictionary)) return typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)});"); } - Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return typeof({keyType});"); } + + // always support dictionary object + Write("if (type == typeof(System.Collections.Generic.Dictionary)) return typeof(object);"); Write("throw new ArgumentOutOfRangeException(\"Unknown type: \" + type.ToString());"); UnIndent(); Write("}"); @@ -159,14 +181,16 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver) Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)})) return typeof({valueType});"); } - //always support array and list of all types + //always support array, list and dictionary of all types foreach (var o in classSyntaxReceiver.Classes) { var classObject = o.Value; Write($"if (type == typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)}[])) return typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)});"); Write($"if (type == typeof(System.Collections.Generic.List<{classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)}>)) return typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)});"); + Write($"if (type == typeof(System.Collections.Generic.Dictionary)) return typeof({classObject.ModuleSymbol.GetFullName().Replace("?", string.Empty)});"); } + Write("if (type == typeof(System.Collections.Generic.Dictionary)) return typeof(object);"); Write("throw new ArgumentOutOfRangeException(\"Unknown type: \" + type.ToString());"); UnIndent(); Write("}"); WriteExecuteMethod(classSyntaxReceiver, "ExecuteOnDeserializing", (c) => c.OnDeserializingMethods); diff --git a/YamlDotNet.Analyzers.StaticGenerator/StaticPropertyDescriptorFile.cs b/YamlDotNet.Analyzers.StaticGenerator/StaticPropertyDescriptorFile.cs index 0fab0651..34d16cb2 100644 --- a/YamlDotNet.Analyzers.StaticGenerator/StaticPropertyDescriptorFile.cs +++ b/YamlDotNet.Analyzers.StaticGenerator/StaticPropertyDescriptorFile.cs @@ -34,6 +34,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver) { Write("class StaticPropertyDescriptor : YamlDotNet.Serialization.IPropertyDescriptor"); Write("{"); Indent(); + Write("private readonly YamlDotNet.Serialization.ITypeResolver _typeResolver;"); Write("private YamlDotNet.Serialization.IObjectAccessor _accessor;"); Write("private readonly Attribute[] _attributes;"); Write("public string Name { get; }"); @@ -52,14 +53,17 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver) UnIndent(); Write("}"); Write("public YamlDotNet.Serialization.IObjectDescriptor Read(object target)"); Write("{"); Indent(); - Write("return new YamlDotNet.Serialization.ObjectDescriptor(_accessor.Read(Name, target), Type, Type, this.ScalarStyle);"); + Write("var propertyValue = _accessor.Read(Name, target);"); + Write("var actualType = _typeResolver.Resolve(Type, propertyValue);"); + Write("return new YamlDotNet.Serialization.ObjectDescriptor(propertyValue, actualType, Type, this.ScalarStyle);"); UnIndent(); Write("}"); Write("public void Write(object target, object value)"); Write("{"); Indent(); Write("_accessor.Set(Name, target, value);"); UnIndent(); Write("}"); - Write("public StaticPropertyDescriptor(YamlDotNet.Serialization.IObjectAccessor accessor, string name, bool canWrite, Type type, Attribute[] attributes)"); + Write("public StaticPropertyDescriptor(YamlDotNet.Serialization.ITypeResolver typeResolver, YamlDotNet.Serialization.IObjectAccessor accessor, string name, bool canWrite, Type type, Attribute[] attributes)"); Write("{"); Indent(); + Write("this._typeResolver = typeResolver;"); Write("this._accessor = accessor;"); Write("this._attributes = attributes;"); Write("this.Name = name;"); diff --git a/YamlDotNet.Analyzers.StaticGenerator/StaticTypeInspectorFile.cs b/YamlDotNet.Analyzers.StaticGenerator/StaticTypeInspectorFile.cs index 410ba1d2..336b6f12 100644 --- a/YamlDotNet.Analyzers.StaticGenerator/StaticTypeInspectorFile.cs +++ b/YamlDotNet.Analyzers.StaticGenerator/StaticTypeInspectorFile.cs @@ -38,6 +38,12 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver) Write("public class StaticTypeInspector : YamlDotNet.Serialization.ITypeInspector"); Write("{"); Indent(); + Write("private readonly YamlDotNet.Serialization.ITypeResolver _typeResolver;"); + Write("public StaticTypeInspector(YamlDotNet.Serialization.ITypeResolver typeResolver)"); + Write("{"); Indent(); + Write("_typeResolver = typeResolver;"); + UnIndent(); Write("}"); + #region GetProperties Write("public IEnumerable GetProperties(Type type, object container)"); Write("{"); Indent(); @@ -96,7 +102,7 @@ public override void Write(ClassSyntaxReceiver classSyntaxReceiver) private void WritePropertyDescriptor(string name, ITypeSymbol type, bool isReadonly, ImmutableArray attributes, char finalChar) { - Write($"new StaticPropertyDescriptor(accessor, \"{name}\", {(!isReadonly).ToString().ToLower()}, typeof({type.GetFullName().Replace("?", string.Empty)}), new Attribute[] {{"); + Write($"new StaticPropertyDescriptor(_typeResolver, accessor, \"{name}\", {(!isReadonly).ToString().ToLower()}, typeof({type.GetFullName().Replace("?", string.Empty)}), new Attribute[] {{"); foreach (var attribute in attributes) { switch (attribute.AttributeClass?.ToDisplayString()) diff --git a/YamlDotNet.Analyzers.StaticGenerator/StaticTypeResolverFile.cs b/YamlDotNet.Analyzers.StaticGenerator/StaticTypeResolverFile.cs new file mode 100644 index 00000000..3ead85c3 --- /dev/null +++ b/YamlDotNet.Analyzers.StaticGenerator/StaticTypeResolverFile.cs @@ -0,0 +1,51 @@ +// 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 Microsoft.CodeAnalysis; + +namespace YamlDotNet.Analyzers.StaticGenerator +{ + public class StaticTypeResolverFile : File + { + public StaticTypeResolverFile(Action write, Action indent, Action unindent, GeneratorExecutionContext context) : base(write, indent, unindent, context) + { + } + + public override void Write(ClassSyntaxReceiver classSyntaxReceiver) + { + Write($"class StaticTypeResolver : YamlDotNet.Serialization.TypeResolvers.StaticTypeResolver"); + Write("{"); Indent(); + Write("private readonly YamlDotNet.Serialization.StaticContext _context;"); + Write($"public StaticTypeResolver(YamlDotNet.Serialization.StaticContext context)"); + Write("{"); Indent(); + Write("_context = context;"); + UnIndent(); Write("}"); + Write("public override Type Resolve(Type staticType, object actualValue)"); + Write("{"); Indent(); + Write("var result = base.Resolve(staticType, actualValue);"); + Write("if (result == staticType && actualValue != null && _context.IsKnownType(actualValue.GetType())) result = actualValue.GetType();"); + Write("return result;"); + UnIndent(); Write("}"); + UnIndent(); Write("}"); + } + } +} diff --git a/YamlDotNet.Analyzers.StaticGenerator/TypeFactoryGenerator.cs b/YamlDotNet.Analyzers.StaticGenerator/TypeFactoryGenerator.cs index 954cf4b0..97671e7c 100644 --- a/YamlDotNet.Analyzers.StaticGenerator/TypeFactoryGenerator.cs +++ b/YamlDotNet.Analyzers.StaticGenerator/TypeFactoryGenerator.cs @@ -96,6 +96,7 @@ private string GenerateSource(ClassSyntaxReceiver classSyntaxReceiver) new StaticContextFile(write, indent, unindent, _context).Write(classSyntaxReceiver); new StaticObjectFactoryFile(write, indent, unindent, _context).Write(classSyntaxReceiver); + new StaticTypeResolverFile(write, indent, unindent, _context).Write(classSyntaxReceiver); new StaticPropertyDescriptorFile(write, indent, unindent, _context).Write(classSyntaxReceiver); new StaticTypeInspectorFile(write, indent, unindent, _context).Write(classSyntaxReceiver); new ObjectAccessorFileGenerator(write, indent, unindent, _context).Write(classSyntaxReceiver); diff --git a/YamlDotNet.Core7AoTCompileTest/Program.cs b/YamlDotNet.Core7AoTCompileTest/Program.cs index 254c4368..246cbb4e 100644 --- a/YamlDotNet.Core7AoTCompileTest/Program.cs +++ b/YamlDotNet.Core7AoTCompileTest/Program.cs @@ -76,6 +76,10 @@ NotInherited: world External: Text: hello +SomeObject: a +SomeDictionary: + a: 1 + b: 2 "); var input = new StringReader(yaml); @@ -211,6 +215,8 @@ public class PrimitiveTypes public List? MyList { get; set; } public Inherited Inherited { get; set; } public ExternalModel External { get; set; } + public object SomeObject { get; set; } + public object SomeDictionary { get; set; } } public class InheritedBase diff --git a/YamlDotNet.Test/Analyzers/StaticGenerator/ObjectTests.cs b/YamlDotNet.Test/Analyzers/StaticGenerator/ObjectTests.cs index d2796c7c..1a0b6207 100644 --- a/YamlDotNet.Test/Analyzers/StaticGenerator/ObjectTests.cs +++ b/YamlDotNet.Test/Analyzers/StaticGenerator/ObjectTests.cs @@ -18,6 +18,8 @@ // 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.Collections.Generic; using Xunit; using YamlDotNet.Serialization; using YamlDotNet.Serialization.Callbacks; @@ -53,6 +55,15 @@ public void RegularObjectWorks() Prop2: 2 Nested: NestedProp: abc +DictionaryOfArrays: + a: + - 1 + b: + - 2 +SomeValue: ""abc"" +SomeDictionary: + a: 1 + b: 2 "; var actual = deserializer.Deserialize(yaml); Assert.Equal("hello", actual.Prop1); @@ -64,6 +75,11 @@ public void RegularObjectWorks() Assert.Equal(2, actual.Inner.Prop2); Assert.NotNull(actual.Nested); Assert.Equal("abc", actual.Nested.NestedProp); + Assert.Equal("1", actual.DictionaryOfArrays["a"][0]); + Assert.Equal("2", actual.DictionaryOfArrays["b"][0]); + Assert.Equal("abc", actual.SomeValue); + Assert.Equal("1", ((IDictionary)actual.SomeDictionary)["a"]); + Assert.Equal("2", ((IDictionary)actual.SomeDictionary)["b"]); var serializer = new StaticSerializerBuilder(new StaticContext()).Build(); var actualYaml = serializer.Serialize(actual); @@ -76,7 +92,15 @@ public void RegularObjectWorks() Prop2: 2 Nested: NestedProp: abc -"; +DictionaryOfArrays: + a: + - 1 + b: + - 2 +SomeValue: abc +SomeDictionary: + a: 1 + b: 2"; Assert.Equal(yaml.NormalizeNewLines().TrimNewLines(), actualYaml.NormalizeNewLines().TrimNewLines()); } @@ -143,6 +167,12 @@ public class RegularObjectOuter public RegularObjectInner Inner { get; set; } public NestedClass Nested { get; set; } + public Dictionary DictionaryOfArrays { get; set; } + + public object SomeValue { get; set; } + + public object SomeDictionary { get; set; } + [YamlSerializable] public class NestedClass { diff --git a/YamlDotNet.Test/Analyzers/StaticGenerator/RootCollectionTests.cs b/YamlDotNet.Test/Analyzers/StaticGenerator/RootCollectionTests.cs index 29849aa4..d3ca1147 100644 --- a/YamlDotNet.Test/Analyzers/StaticGenerator/RootCollectionTests.cs +++ b/YamlDotNet.Test/Analyzers/StaticGenerator/RootCollectionTests.cs @@ -54,6 +54,36 @@ public void RootListWorks() Assert.Equal("hello", actual[0].Test); Assert.Equal("world", actual[1].Test); } + + [Fact] + public void RootDictionaryWorks() + { + var deserializer = new StaticDeserializerBuilder(new StaticContext()).Build(); + var yaml = @" +a: + Test: hello +b: + Test: world +"; + + var actual = deserializer.Deserialize>(yaml); + Assert.Equal("hello", actual["a"].Test); + Assert.Equal("world", actual["b"].Test); + } + + [Fact] + public void RootObjectWorks() + { + var deserializer = new StaticDeserializerBuilder(new StaticContext()).Build(); + var yaml = @" +a: hello +b: world +"; + + var actual = (IDictionary) deserializer.Deserialize(yaml); + Assert.Equal("hello", actual["a"]); + Assert.Equal("world", actual["b"]); + } } [YamlSerializable] public class RootObject diff --git a/YamlDotNet/Serialization/StaticContext.cs b/YamlDotNet/Serialization/StaticContext.cs index 1ea331e2..098e8530 100644 --- a/YamlDotNet/Serialization/StaticContext.cs +++ b/YamlDotNet/Serialization/StaticContext.cs @@ -29,6 +29,25 @@ namespace YamlDotNet.Serialization /// public abstract class StaticContext { + /// + /// Gets whether the type is known to the context + /// + /// Type to check + /// + public virtual bool IsKnownType(Type type) + { + throw new NotImplementedException(); + } + + /// + /// Gets the to use for serialization + /// + /// + public virtual ITypeResolver GetTypeResolver() + { + throw new NotImplementedException(); + } + /// /// Gets the factory to use for serialization and deserialization /// diff --git a/YamlDotNet/Serialization/StaticDeserializerBuilder.cs b/YamlDotNet/Serialization/StaticDeserializerBuilder.cs index 13b18596..50229134 100644 --- a/YamlDotNet/Serialization/StaticDeserializerBuilder.cs +++ b/YamlDotNet/Serialization/StaticDeserializerBuilder.cs @@ -61,7 +61,7 @@ public sealed class StaticDeserializerBuilder : StaticBuilderSkeleton using the default component registrations. /// public StaticDeserializerBuilder(StaticContext context) - : base(new StaticTypeResolver()) + : base(context.GetTypeResolver()) { this.context = context; factory = context.GetFactory(); diff --git a/YamlDotNet/Serialization/TypeResolvers/StaticTypeResolver.cs b/YamlDotNet/Serialization/TypeResolvers/StaticTypeResolver.cs index 7bd96bc6..80129cfa 100644 --- a/YamlDotNet/Serialization/TypeResolvers/StaticTypeResolver.cs +++ b/YamlDotNet/Serialization/TypeResolvers/StaticTypeResolver.cs @@ -24,12 +24,34 @@ namespace YamlDotNet.Serialization.TypeResolvers { /// - /// The type returned will always be the static type. + /// Except for primitive types, the type returned will always be the static type. /// - public sealed class StaticTypeResolver : ITypeResolver + public class StaticTypeResolver : ITypeResolver { - public Type Resolve(Type staticType, object? actualValue) + public virtual Type Resolve(Type staticType, object? actualValue) { + if (actualValue != null) + { + switch (actualValue.GetType().GetTypeCode()) + { + case TypeCode.Boolean: return typeof(bool); + case TypeCode.Char: return typeof(char); + case TypeCode.SByte: return typeof(sbyte); + case TypeCode.Byte: return typeof(byte); + case TypeCode.Int16: return typeof(short); + case TypeCode.UInt16: return typeof(ushort); + case TypeCode.Int32: return typeof(int); + case TypeCode.UInt32: return typeof(uint); + case TypeCode.Int64: return typeof(long); + case TypeCode.UInt64: return typeof(ulong); + case TypeCode.Single: return typeof(float); + case TypeCode.Double: return typeof(double); + case TypeCode.Decimal: return typeof(decimal); + case TypeCode.String: return typeof(string); + case TypeCode.DateTime: return typeof(DateTime); + } + } + return staticType; } } From 8255d17185cb25d0478328fe381bd8ba47d3f63b Mon Sep 17 00:00:00 2001 From: Christian Prochnow Date: Fri, 6 Oct 2023 15:05:02 +0200 Subject: [PATCH 2/2] Added test for nested untyped deserialization. --- .../StaticGenerator/RootCollectionTests.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/YamlDotNet.Test/Analyzers/StaticGenerator/RootCollectionTests.cs b/YamlDotNet.Test/Analyzers/StaticGenerator/RootCollectionTests.cs index d3ca1147..70614e63 100644 --- a/YamlDotNet.Test/Analyzers/StaticGenerator/RootCollectionTests.cs +++ b/YamlDotNet.Test/Analyzers/StaticGenerator/RootCollectionTests.cs @@ -84,6 +84,22 @@ public void RootObjectWorks() Assert.Equal("hello", actual["a"]); Assert.Equal("world", actual["b"]); } + + [Fact] + public void RootNestedObjectWorks() + { + var deserializer = new StaticDeserializerBuilder(new StaticContext()).Build(); + var yaml = @" +a: + Test: hello +b: + Test: world +"; + + var actual = (IDictionary) deserializer.Deserialize(yaml); + Assert.Equal("hello", ((IDictionary)actual["a"])["Test"]); + Assert.Equal("world", ((IDictionary)actual["b"])["Test"]); + } } [YamlSerializable] public class RootObject