Rišo
Rišo
CC#
Created by Rišo on 5/22/2024 in #help
Duplicate enum values serialization problems
So, for the context, we have a public enum in which we had to introduce/rename one value. The problem is, that it would be a breaking change, therefore we had to mark the old value as obsolete and introduce a new one. To avoid any migrations, we did a bit controversial act, and we set the two values as equal. We have yet another value which is almost identical and we quite often map them onto each other. The problem is, that one enum has even and the other odd amount of values. Therefore, the other enum has reversed order. This way methods such as ToString, TryParse, Enum.GetName and so on always return the new value. An example:
public enum MyEnum
{
ValueA = 0,
[Obsolete($"Use {nameof(ValueC)} instead."), ObsoleteSince(69, 42)]
ValueB = ValueC,
ValueC = 1,
ValueD = 2,
}

public enum MyEnum2
{
ValueA = 0,
ValueC = 1,
[Obsolete($"Use {nameof(ValueC)} instead."), ObsoleteSince(69, 42)]
ValueB = ValueC,
ValueD = 2,
ValueD = 3,
}
public enum MyEnum
{
ValueA = 0,
[Obsolete($"Use {nameof(ValueC)} instead."), ObsoleteSince(69, 42)]
ValueB = ValueC,
ValueC = 1,
ValueD = 2,
}

public enum MyEnum2
{
ValueA = 0,
ValueC = 1,
[Obsolete($"Use {nameof(ValueC)} instead."), ObsoleteSince(69, 42)]
ValueB = ValueC,
ValueD = 2,
ValueD = 3,
}
Everything seemed alright with this approach, until serialization came into play. Both Rider debugger and JsonStringEnumConverter seem to serialize into the now obsolete enum value for the first enum, where obsolete value is the first. After some investigation, I've found a reason to be inside an EnumCoverter<> class from .net source (https://github.com/dotnet/runtime/blob/main/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs) There, in the constructor you get all the names, in order as they are in the file, and one by one add them into the dict/cache. Once obsolete value is added, the new one can not due to the key uniqueness constraint. Once you read from cache in the write method, the new name is simply not there and an old value will be always used. If the cache weren't used/filled in, it would work correctly, due to the usage of ToString method which retrieves the correct string value.
9 replies