Protobuf-net: Serializing a 3rd party class with a Stream data member

前端 未结 2 1125
孤独总比滥情好
孤独总比滥情好 2021-01-23 01:54

How do you serialize a Stream (or more correctly Stream derived) data member of a class?

Assuming we have a 3rd Party class that we can\'t attribute:

pub         


        
相关标签:
2条回答
  • 2021-01-23 02:09

    Stream is a very complex beast, and there is no inbuilt serialization mechanism for that. Your code configures it as though it were a type with no interesting members, which is why it is coming back as empty.

    For this scenario, I'd probably create a surrogate, and set it up with just:

    RuntimeTypeModel.Default.Add(typeof(Fubar), false)
           .SetSurrogate(typeof(FubarSurrogate));
    

    where:

    [ProtoContract]
    public class FubarSurrogate
    {
        [ProtoMember(1)]
        public string Label { get; set; }
        [ProtoMember(2)]
        public int DataType { get; set; }
        [ProtoMember(3)]
        public byte[] Data { get; set; }
    
        public static explicit operator Fubar(FubarSurrogate value)
        {
            if(value == null) return null;
            return new Fubar {
                Label = value.Label,
                DataType = value.DataType,
                Data = value.Data == null ? null : new MemoryStream(value.Data)
            };
        }
        public static explicit operator FubarSurrogate(Fubar value)
        {
            if (value == null) return null;
            return new FubarSurrogate
            {
                Label = value.Label,
                DataType = value.DataType,
                Data = value.Data == null ?
                     null : ((MemoryStream)value.Data).ToArray()
            };
        }
    }
    
    0 讨论(0)
  • 2021-01-23 02:28

    Not to make Marc claw his own hands off... but in case anyone else wants to create a surrogate for Stream I've adapted Marc's surrogate example from the answer:

    [ProtoContract]
    public class StreamSurrogate
    {
        [ProtoMember(1)]
        public byte[] Data { get; set; }
    
        public static explicit operator Stream(StreamSurrogate value)
        {
            if (value == null)
            {
                return null;
            }
    
            return new MemoryStream(value.Data);
    }
    
        public static explicit operator StreamSurrogate(Stream value)
        {
            if (value == null)
            {
                return null;
            }
    
            if (value is MemoryStream)
            {
                return new StreamSurrogate { Data = ((MemoryStream)value).ToArray() };
            }
            else
            {
                // Probably a better way to do this...
                StreamSurrogate ss = new StreamSurrogate();
    
                ss.Data = new byte[value.Length];
                value.Read(ss.Data, 0, (int)value.Length);
                return ss;
            }
        }
    }
    

    And then for the RuntimeTypeModel:

    MetaType mt2 = RuntimeTypeModel.Default.Add(typeof(Stream), true);
    
        mt2.AddSubType(1, typeof(MemoryStream));
        mt2.SetSurrogate(typeof(StreamSurrogate));
    

    In which case f2 in my example appears to be fully correct!

    Yes there are a lot of potential troubles with trying to serialize Stream - perhaps it would be wiser for me to set the surrogate on the MemoryStream subtype only since I know my particular case will always be using MemoryStreams for Data. However my line of thinking for registering the surrogate on Stream is as follows:

    1. The data member of the class is a Stream.
    2. Any Stream derived class could have been used on the original object and most of them likely can't be recreated.
    3. However since the data member is a Stream, any Stream derived class should suffice for the deserialized object (since it only must support Stream).
    4. MemoryStream is probably the best candidate in most cases for the deserialized stream.
    0 讨论(0)
提交回复
热议问题