ProtoBuf Net Словарь дубликатов обработки ключей (карта) - PullRequest
2 голосов
/ 14 января 2020

У меня есть некоторые данные, которые я сериализовал с protobuf. * ​​1025 *. Данные являются картой и содержат несколько дубликатов (что произошло, поскольку мой ключ не реализовал IEquatable)

Я хочу десериализовать данные в словарь и игнорировать дубликаты. Кажется, для этого есть атрибут, т. Е. [ProtoMap(DisableMap=false)], о котором говорится в документации:

Отключить обработку «карты»; словари будут использовать .Add (ключ, значение) вместо [ключ] = значение. ...

По сути, я хочу, чтобы поведение было [key] = value, но, видимо, атрибут игнорируется.

Я что-то не так делаю? Есть ли способ достичь желаемого (и задокументированного) поведения игнорирования дубликатов?

Пример кода: 1. Создание данных с дубликатами:

            // ------------- ------------- ------------- ------------- ------------- ------------- -------------
            //  The following part generated the bytes, which requires the key NOT to implement IEquatable
            // ------------- ------------- ------------- ------------- ------------- ------------- -------------

            var cache = new MyTestClass() { Dictionary = new Dictionary<MyTestKey, string>() };

            cache.Dictionary[new MyTestKey { Value = "X" }] = "A";
            cache.Dictionary[new MyTestKey { Value = "X" }] = "B";

            var bytes = cache.Serialize();

            var bytesStr = string.Join(",", bytes); // "10,8,10,3,10,1,88,18,1,65,10,8,10,3,10,1,88,18,1,66";

    //..



    [DataContract]
    public class MyTestKey
    {
        [DataMember(Order = 1)]
        public string Value { get; set; }
    }

    [DataContract]
    public class MyTestClass
    {
        [DataMember(Order = 1)]
        [ProtoMap(DisableMap = false)]
        public Dictionary<MyTestKey, string> Dictionary { get; set; }
    }

´´´

2. Try deserialize the data, with property IEquatable, which fails..:

[DataContract]
public class MyTestKey : IEquatable<MyTestKey>
{
    [DataMember(Order = 1)]
    public string Value { get; set; }

    public bool Equals(MyTestKey other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Value == other.Value;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((MyTestKey) obj);
    }

    public override int GetHashCode()
    {
        return (Value != null ? Value.GetHashCode() : 0);
    }
}
//..
var bytesStr2 = "10,8,10,3,10,1,88,18,1,65,10,8,10,3,10,1,88,18,1,66";
var bytes2 = bytesStr2.Split(',').Select(byte.Parse).ToArray();
var cache = bytes2.DeserializeTo<MyTestClass>(); 

´´´

Исключение Элемент с тем же ключом уже добавлен.

public static class SerializationExtensions
{
    public static T DeserializeTo<T>(this byte[] bytes)
    {
        if (bytes == null)
            return default(T);

        using (var ms = new MemoryStream(bytes))
        {

            return Serializer.Deserialize<T>(ms);
        }
    }

    public static byte[] Serialize<T>(this T setup)
    {
        using (var ms = new MemoryStream())
        {
            Serializer.Serialize(ms, setup);
            return ms.ToArray();
        }
    }

1 Ответ

0 голосов
/ 14 января 2020

Здесь происходит несколько разных вещей; Режим "карта" на самом деле тот, который вы хотите здесь - так что это не значит, что вы пытаетесь отключить карту, но на самом деле вы пытаетесь заставить его на (теперь он включен по умолчанию в большинстве распространенных сценариев словаря ios).

Есть некоторые сложности:

  1. библиотека обрабатывает только [ProtoMap(...)] при обработке [ProtoMember(...)] для [ProtoContract(...)]
  2. даже тогда, он обрабатывает только [ProtoMap(...)] для типов ключей, которые действительны как ключи "карты" в спецификации прото
  3. you может включить его вручную (не через атрибуты), но в v2. * он выполняет ту же проверку, что и # 2 во время выполнения, что означает, что он не будет работать

ручное включение из # 3 работает в версии 3. * (в настоящее время в альфа-версии):

RuntimeTypeModel.Default[typeof(MyTestClass)][1].IsMap = true;

однако, это явно не элегантно, и сегодня требует использования альфа-сборки (мы ' Я использовал его в производстве в Stack Overflow в течение длительного периода времени, мне просто нужно собрать релиз - docs, et c).

* 103 3 * Учитывая, что работает , я испытываю желание предложить смягчить # 2 в v3. *, Так что, хотя поведение default остается прежним, оно все равно проверяет для [ProtoMap(...)] для пользовательских типов и включите этот режим. Я нахожусь на грани того, чтобы смягчить # 1.

Мне было бы интересно ваши мысли об этих вещах!

Но для подтверждения: в v3. * Хорошо работает следующее выводит "B" (незначительное объяснение кода: в protobuf добавьте === merge для root объектов, поэтому сериализация двух полезных данных одна за другой имеет тот же эффект, что и сериализация словаря с объединенным содержимым, поэтому два Serialize вызывает подмену полезной нагрузки с двумя одинаковыми ключами):

static class P
{
    static void Main()
    {
        using var ms = new MemoryStream();
        var key = new MyTestKey { Value = "X" };
        RuntimeTypeModel.Default[typeof(MyTestClass)][1].IsMap = true;
        Serializer.Serialize(ms, new MyTestClass() { Dictionary = 
          new Dictionary<MyTestKey, string> { { key, "A" } } });
        Serializer.Serialize(ms, new MyTestClass() { Dictionary =
          new Dictionary<MyTestKey, string> { { key, "B" } } });


        ms.Position = 0;
        var val = Serializer.Deserialize<MyTestClass>(ms).Dictionary[key];
        Console.WriteLine(val); // B
    }
}

Я думаю, что я хотел бы , если бы в v3. * Он работал без строка IsMap = true, с:

[ProtoContract]
public class MyTestClass
{
    [ProtoMember(1)]
    [ProtoMap] // explicit enable here, because not a normal map type
    public Dictionary<MyTestKey, string> Dictionary { get; set; }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...