aaubry / YamlDotNet

YamlDotNet is a .NET library for YAML
MIT License
2.48k stars 466 forks source link

ObjectNodeDeserializer: Naming Convention Error on Deserialization #915

Open BlankDev117 opened 3 months ago

BlankDev117 commented 3 months ago

Describe the bug Deserializing an object, using PascalCase naming convention, appears to break YamlDotNet if the serialization process was not already in PascalCase, i.e. serialized with CamelCase or UnderScored naming conventions.

To Reproduce

  1. Set a naming convention for serialization other than PascalCase
  2. Serialize data with this naming convention.
  3. Attempt to deserialize this data, using the deserialization naming convention, with PascalCase
  4. See an error regarding the {LOWER_CASE PROPERTY_NAME} not matching any of the property names on the main object.

image [Test Data for serialization]

image [Serialization Settings. Notice the differences in the naming convetions]

image [Output of the above serialization to a local file. Notice the today property listed on the yaml file on line 1]

The resulting error:

  Message: 
    YamlDotNet.Core.YamlException : Property 'today' not found on type 'Discriminators.UnitTests.Helpers.TestChildA'.

  Stack Trace: 
    ObjectNodeDeserializer.Deserialize(IParser parser, Type expectedType, Func`3 nestedObjectDeserializer, Object& value)
    TypeDiscriminatingNodeDeserializer.Deserialize(IParser reader, Type expectedType, Func`3 nestedObjectDeserializer, Object& value)
    NodeValueDeserializer.DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer)
    AliasValueDeserializer.DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer)
    <>c__DisplayClass5_0.<DeserializeValue>b__0(IParser r, Type t)
    CollectionNodeDeserializer.DeserializeHelper(Type tItem, IParser parser, Func`3 nestedObjectDeserializer, IList result, Boolean canUpdate, INamingConvention enumNamingConvention)
    ArrayNodeDeserializer.Deserialize(IParser parser, Type expectedType, Func`3 nestedObjectDeserializer, Object& value)
    NodeValueDeserializer.DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer)
    AliasValueDeserializer.DeserializeValue(IParser parser, Type expectedType, SerializerState state, IValueDeserializer nestedObjectDeserializer)
    Deserializer.Deserialize(IParser parser, Type type)
    Deserializer.Deserialize(TextReader input, Type type)
    YamlSerializer.DeserializeAsync(Byte[] data, Type type, CancellationToken cancellationToken) line 57
    PolymorphismYamlTypeDiscriminatorTests.Validate_ServiceCollectionExtensions() line 95
    <>c.<ThrowAsync>b__128_0(Object state)

Expected Result Deserialization of the input file or data stream should find the expected property name when using PascalCase

Notes From what I can tell, the issue arises in the ObjectNodeDeserializer, here. Problem appears to be a direct usage of the property name, rather than applying the NamingConvention for the Deserialization naming convention. In fact, this class does not reference the deserialization naming convention at all and instead only uses an enum naming convention.

The current workaround seems to simply use PascalCase for both the serialization and deserialization processes, unless I'm horribly wrong about what I'm seeing and it being some kind of other configuration, or user, issue.

skrysmanski commented 3 months ago

I also ran into this but with CamelCaseNamingConvention (I didn't know it was different than PascalCase).

I have a class like this:

namespace WishListApp.DataModels;

public sealed class WishListAppSettings
{
    public string? LastOpenedFile { get; set; }
}

And with this YAML:

LastOpenedFile: C:\Users\...

I was getting this exception:

Property 'LastOpenedFile' not found on type 'WishListApp.DataModels.WishListAppSettings'.

Which is quite confusing because it's obviously wrong. The problem here is that YAML.NET also uses the name from the file for the exception message but it should use the name after applying the naming convention to avoid confusion for developers like me.

I also think that the deserialization sample is wrong by using CamelCaseNamingConvention - it should use PascalCaseNamingConvention.

EdwardCooke commented 1 month ago

Using the naming convention when deserializing is sketchy at best. There's not much of a way to guarantee what the resulting property casing or name should really actually be. They work fantastic for serializing though. My recommendation would be to use the yamlmember attribute and specify what the name should actually be in the yaml file, that's the only way to guarantee what should be what when different than the property/field name.