8000 MessagePack 3.1 doesn't serialize or deserialize keys in custom collections · Issue #2218 · MessagePack-CSharp/MessagePack-CSharp · GitHub
[go: up one dir, main page]

Skip to content

MessagePack 3.1 doesn't serialize or deserialize keys in custom collections #2218

@abond-sd

Description

@abond-sd

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0