neuecc / Utf8Json

Definitely Fastest and Zero Allocation JSON Serializer for C#(NET, .NET Core, Unity, Xamarin).
MIT License
2.36k stars 267 forks source link

Exception thrown when serializing an enum with same value multiple times #128

Open doominator42 opened 5 years ago

doominator42 commented 5 years ago

Given an enum like that:

public enum Test
{
    Value1 = 0,
    Value2 = 1,
    Value3 = 0,
}

When serializing this enum it will throw a System.ArgumentException with message Key was already exists. Key:System.Byte[]. Probably same issue as #91.

The exception is thrown there:

// Utf8Json/IJsonFormatterResolver.cs:16-29
try
{
    formatter = resolver.GetFormatter<T>();
}
catch (TypeInitializationException ex)
{
    Exception inner = ex;
    while (inner.InnerException != null)
    {
        inner = inner.InnerException;
    }

    throw inner;
}

By digging a bit, I found the original throw:

// Utf8Json/Internal/ByteArrayStringHashTable.cs:36-42
public void Add(byte[] key, T value)
{
    if (!TryAddInternal(key, value))
    {
        throw new ArgumentException("Key was already exists. Key:" + key);
    }
}

Which is called from:

// Utf8Json/Formatters/EnumFormatter.cs:166-170
for (int i = 0; i < names.Count; i++)
{
    nameValueMapping.Add(JsonWriter.GetEncodedPropertyNameWithoutQuotation(names[i]), (T)values[i]);
    valueNameMapping[(T)values[i]] = names[i];
}

The names collection is built using Enum.GetName(type, value) which returns the name of the first matching value in the enum. So it will result with duplicated names.

Proposed solution

As the values are enumerated using type.GetFields() we can use field.Name which would give the exact name as written in the enum and the names collection would be garanteed to be distinct.