-
-
Notifications
You must be signed in to change notification settings - Fork 754
Description
Bug description
The source generator does not create a formatter for custom collection types which have a default constructor even if that type has additional fields or properties that should be serialized.
MessagePack 2.1 code gen did support this, so this is impeding an upgrade where we need to maintain binary backwards compatibility.
Repro steps
Create a new console program dependent on NUnit and MessagePack v3
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using MessagePack;
using NUnit.Framework;
CustomDictionary<int, int> dictionary = new()
{
{ 1, 2 },
{ 2, 3 }
};
dictionary.CustomValue = 12345;
MemoryStream memoryStream = new();
MessagePackSerializer.Serialize(memoryStream, dictionary);
memoryStream.Seek(0, SeekOrigin.Begin);
var clone = MessagePackSerializer.Deserialize<CustomDictionary<int, int>>(memoryStream);
Assert.That(clone.Count, Is.EqualTo(dictionary.Count), "Counts do not match");
Assert.That(clone.CustomValue, Is.EqualTo(dictionary.CustomValue), "Custom values do not match");
Console.WriteLine("Test Passed!");
[MessagePackObject]
public sealed class CustomDictionary<TKey, TValue> : IDictionary<TKey, TValue>
where TKey : notnull
{
private readonly Dictionary<TKey, TValue> m_actualDictionary = new();
[Key(0)] public IReadOnlyDictionary<TKey, TValue> ActualDictionary => m_actualDictionary;
[Key(1)] public int CustomValue { get; set; }
public int Count => m_actualDictionary.Count;
public bool IsReadOnly => false;
public ICollection<TKey> Keys => m_actualDictionary.Keys;
public ICollection<TValue> Values => m_actualDictionary.Values;
public TValue this[TKey key]
{
get => m_actualDictionary[key];
set => m_actualDictionary[key] = value;
}
public CustomDictionary()
{
}
[SerializationConstructor]
public CustomDictionary(IReadOnlyDictionary<TKey, TValue> actualDictionary, int customValue)
{
m_actualDictionary = new Dictionary<TKey, TValue>(actualDictionary);
CustomValue = customValue;
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => m_actualDictionary.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => m_actualDictionary.GetEnumerator();
public void Add(KeyValuePair<TKey, TValue> item) => m_actualDictionary.Add(item.Key, item.Value);
public void Add(TKey key, TValue value) => m_actualDictionary.Add(key, value);
public void Clear() => m_actualDictionary.Clear();
public bool Contains(KeyValuePair<TKey, TValue> item) => m_actualDictionary.Contains(item);
public bool ContainsKey(TKey key) => m_actualDictionary.ContainsKey(key);
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) =>
(m_actualDictionary as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
public bool Remove(KeyValuePair<TKey, TValue> item) => m_actualDictionary.Remove(item.Key);
public bool Remove(TKey key) => m_actualDictionary.Remove(key);
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) =>
m_actualDictionary.TryGetValue(key, out value);
}
Expected behavior
The source generator should use a custom formatter instead of using GenericCollectionFormatter for types with other keys.
Actual behavior
The source generator uses GenericCollectionFormatter for the type. This causes deserialization of any content saved using MessagePack 2.1 to become corrupted since it's not including the additional serialized fields.
- Version used: 3.1.4
- Runtime: .NET 6.0
Additional context
This worked okay using the code generator for MessagePack 2.1