简体   繁体   English

C# Protobuf 反序列化

[英]C# Protobuf Deserialization

Why is protobuf deserialization not working for OffsetDictionary member variable?为什么 protobuf 反序列化不适用于 OffsetDictionary 成员变量? It works fine if I don't use Dictionary as backing field.如果我不使用 Dictionary 作为支持字段,它工作正常。 Also, it works fine if the type of OffsetDictionary is changed from complex type to simple SortedDictionary.此外,如果 OffsetDictionary 的类型从复杂类型更改为简单的 SortedDictionary,它也可以正常工作。 Am I missing something here?我在这里错过了什么吗?

[ProtoContract]
Public class Test
{
    [ProtoMember(1)]
    public DateTime BaseDate {get; set;};

    [ProtoMember(2)]
    public SortedDictionary<short, SortedDictionary<short, uint>> OffsetDictionary
    {
        get
        {
            var output = new SortedDictionary<short, SortedDictionary<short, uint>>();
            if (this.Dictionary != null)
            {
                foreach (var item in this.Dictionary)
                {
                    var timeSpan = item.Key - this.BaseDate;
                    short offset = Convert.ToInt16(timeSpan.TotalDays);
                    output.Add(offset, item.Value);
                }
            }

            return output;
        }

        set
        {
            if (this.Dictionary == null)
            {
                this.Dictionary = new SortedDictionary<DateTime, SortedDictionary<short, uint>>();
            }

            foreach (var item in value)
            {
                this.Dictionary.Add(this.BaseDate.AddDays(item.Key), item.Value);
            }
        }
    }

    public SortedDictionary<DateTime, SortedDictionary<short, uint>> Dictionary { get; set; }
}

The problem here is the assumption that the deserializer calls the setter - either at all, or exactly when you expect;这里的问题是假设解串器调用 setter——要么完全调用,要么完全按照你的预期调用; it is not required to .不需要 The serializer assumes reasonably typical implementations, in which case the following is perfectly reasonable:序列化器假定合理的典型实现,在这种情况下,以下是完全合理的:

// when field 2
var val = obj.OffsetDictionary;
bool setValue = false;
if (val == null)
{
    val = new SortedDictionary<short, SortedDictionary<short, uint>>();
    setValue = true;
}
do {
    val.Add(/* parse this entry */);
} while (/* still field 2 */)
if (setValue) obj.OffsetDictionary = val;

Although note that assigning at the start (where setValue is assigned) would also be legitimate.尽管请注意在开始时分配(分配setValue地方)也是合法的。

As it happens, you can sort of make this work by using当它发生时,您可以使用这项工作排序之

[ProtoMember(2, OverwriteList = true)]

but... it works for the wrong reasons IMO, as that could also be implemented with the same pseudo-code as above, but just adding a .Clear() , which wouldn't change the output.但是...它的工作原因是错误的 IMO,因为它也可以使用与上面相同的伪代码来实现,但只需添加一个.Clear() ,它不会改变输出。

Frankly, I'm not sure that I really like the overall design here;坦率地说,我不确定我是否真的喜欢这里的整体设计。 personally, I'd keep the data in the form you're going to serialize , and add utility methods on Test that do the flip, ie就个人而言,我会将数据保留在您要序列化的形式中,并在Test上添加实用方法来进行翻转,即

[ProtoContract]
public class Test
{
    [ProtoMember(1)]
    public DateTime BaseDate { get; set; }

    [ProtoMember(2)]
    public SortedDictionary<short, SortedDictionary<short, uint>> OffsetDictionary { get; }
        = new SortedDictionary<short, SortedDictionary<short, uint>>();

    private short ToInt16(DateTime value) => (short)(value - BaseDate).TotalDays;

    public void Add(DateTime key, SortedDictionary<short, uint> value)
        => OffsetDictionary.Add(ToInt16(key), value);
    public bool TryGetValue(DateTime key, out SortedDictionary<short, uint> value)
        => OffsetDictionary.TryGetValue(ToInt16(key), out value);
}

However, you could also do this using a wrapper layer - although it is much more work:但是,您也可以使用包装层来做到这一点 - 尽管它需要做更多的工作:

[ProtoContract]
public class Test
{
    [ProtoMember(1)]
    public DateTime BaseDate { get; set; }

    private DictionaryWrapper _offsetDictionary;

    [ProtoMember(2)]
    public IDictionary<short, SortedDictionary<short, uint>> OffsetDictionary
        => _offsetDictionary ?? (_offsetDictionary = new DictionaryWrapper(this));

    public SortedDictionary<DateTime, SortedDictionary<short, uint>> Dictionary { get; }
        = new SortedDictionary<DateTime, SortedDictionary<short, uint>>();

    class DictionaryWrapper : IDictionary<short, SortedDictionary<short, uint>>
    {
        public DictionaryWrapper(Test parent)
        {
            _parent = parent;
        }
        private readonly Test _parent;
        private DateTime ToDateTime(short value) => _parent.BaseDate.AddDays(value);
        private short ToInt16(DateTime value) => (short)(value - _parent.BaseDate).TotalDays;

        SortedDictionary<short, uint> IDictionary<short, SortedDictionary<short, uint>>.this[short key]
        {
            get => _parent.Dictionary[ToDateTime(key)];
            set => _parent.Dictionary[ToDateTime(key)] = value;
        }

        int ICollection<KeyValuePair<short, SortedDictionary<short, uint>>>.Count => _parent.Dictionary.Count;

        bool ICollection<KeyValuePair<short, SortedDictionary<short, uint>>>.IsReadOnly => false;

        void IDictionary<short, SortedDictionary<short, uint>>.Add(short key, SortedDictionary<short, uint> value)
            => _parent.Dictionary.Add(ToDateTime(key), value);

        void ICollection<KeyValuePair<short, SortedDictionary<short, uint>>>.Add(KeyValuePair<short, SortedDictionary<short, uint>> item)
            => _parent.Dictionary.Add(ToDateTime(item.Key), item.Value);

        void ICollection<KeyValuePair<short, SortedDictionary<short, uint>>>.Clear()
            => _parent.Dictionary.Clear();

        private ICollection<KeyValuePair<DateTime, SortedDictionary<short, uint>>> AsCollection => _parent.Dictionary;
        bool ICollection<KeyValuePair<short, SortedDictionary<short, uint>>>.Contains(KeyValuePair<short, SortedDictionary<short, uint>> item)
            => AsCollection.Contains(new KeyValuePair<DateTime, SortedDictionary<short, uint>>(ToDateTime(item.Key), item.Value));

        bool IDictionary<short, SortedDictionary<short, uint>>.ContainsKey(short key)
            => _parent.Dictionary.ContainsKey(ToDateTime(key));

        private IEnumerator<KeyValuePair<short, SortedDictionary<short, uint>>> GetEnumerator()
        {
            foreach (var item in _parent.Dictionary)
                yield return new KeyValuePair<short, SortedDictionary<short, uint>>(ToInt16(item.Key), item.Value);
        }
        IEnumerator<KeyValuePair<short, SortedDictionary<short, uint>>> IEnumerable<KeyValuePair<short, SortedDictionary<short, uint>>>.GetEnumerator()
            => GetEnumerator();

        IEnumerator IEnumerable.GetEnumerator()
            => GetEnumerator();

        bool IDictionary<short, SortedDictionary<short, uint>>.Remove(short key)
            => _parent.Dictionary.Remove(ToDateTime(key));

        bool ICollection<KeyValuePair<short, SortedDictionary<short, uint>>>.Remove(KeyValuePair<short, SortedDictionary<short, uint>> item)
            => AsCollection.Remove(new KeyValuePair<DateTime, SortedDictionary<short, uint>>(ToDateTime(item.Key), item.Value));

        bool IDictionary<short, SortedDictionary<short, uint>>.TryGetValue(short key, out SortedDictionary<short, uint> value)
            => _parent.Dictionary.TryGetValue(ToDateTime(key), out value);

        // these are kinda awkward to implement
        ICollection<short> IDictionary<short, SortedDictionary<short, uint>>.Keys
            => throw new NotSupportedException();

        ICollection<SortedDictionary<short, uint>> IDictionary<short, SortedDictionary<short, uint>>.Values
            => throw new NotSupportedException();

        void ICollection<KeyValuePair<short, SortedDictionary<short, uint>>>.CopyTo(KeyValuePair<short, SortedDictionary<short, uint>>[] array, int arrayIndex)
            => throw new NotSupportedException();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM