aaubry / YamlDotNet

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

Enum serialization behavior changed after #856 #858

Closed DavidLievrouw closed 8 months ago

DavidLievrouw commented 8 months ago

Describe the bug

After updating from v13.5.2 to v13.7.0, the serialization behavior of enum values appears to have changed. The casing of enum values is now pascal-cased, even if I configure CamelCaseNamingConvention.

I traced the cause of the issue to commit 63f71e2b675cce1680c20c15a4648607c09ee610 for change #856 . In that commit, StaticTypeResolver has been changed, and it does not seem to take enum types into account.

I dont't understand exactly which problem #856 fixed. So I don't know if we could just extend it, to fix this case.

To Reproduce

Run this program:

using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.TypeResolvers;

var enumConverter = new YamlEnumConverter();

var builder = new SerializerBuilder()
    .WithNamingConvention(CamelCaseNamingConvention.Instance)
    .WithTypeResolver(new StaticTypeResolver())
    .WithTypeConverter(enumConverter);

var valueSerializer = builder.BuildValueSerializer();
enumConverter.ValueSerializer = valueSerializer;

var condition = new Condition
{
    Operator = ValuedOperatorYaml.NotEquals
};

var serializer = builder.Build();

var yaml = serializer.Serialize(condition);

Console.WriteLine(yaml);

internal class CamelCaseStringEnumConverter : StringEnumConverter
{
    public CamelCaseStringEnumConverter()
    {
        NamingStrategy = new CamelCaseNamingStrategy(true, true, false);
        AllowIntegerValues = false;
    }
}

internal class Condition
{
    public ValuedOperatorYaml? Operator { get; set; }
}

[JsonConverter(typeof(CamelCaseStringEnumConverter))]
internal enum ValuedOperatorYaml
{
    [EnumMember(Value = "notEquals")] NotEquals
}

internal class YamlEnumConverter : IYamlTypeConverter
{
    public IValueSerializer ValueSerializer { get; set; }

    public bool Accepts(Type type)
    {
        return type.IsEnum;
    }

    public object ReadYaml(IParser parser, Type type)
    {
        throw new NotImplementedException();
    }

    public void WriteYaml(IEmitter emitter, object value, Type type)
    {
        var converted = JsonConvert.SerializeObject(value);
        var cleanJsonObj = JsonConvert.DeserializeObject<string>(converted);
        ValueSerializer.SerializeValue(emitter, cleanJsonObj, typeof(string));
    }
}

Using v13.5.2, the output is operator: notEquals. Using v13.7.0, the output is operator: NotEquals. (see the casing difference in the value)

Workaround

For the time being, I can use a custom implementation of ITypeResolver. Or use the DynamicTypeResolver, in some cases.

EdwardCooke commented 8 months ago

I’ll take a look at this as soon as I can. I suspect it’s the type resolver changes where it looks at the type codes.

it’ll probably be later tonight or tomorrow.