本文介绍了如何更改我之前保存的 List 类型以序列化为 Array 类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 之前,我们将属性序列化为 List现在我们想把它改成一个byte[].不知道你应该能够在版本之间自由交换集合类型,但我们得到了一个 ProtoBuf.ProtoExceptionPreviously, we serialized a property as a List<byte>Now we want to change it to be a byte[].It was out understanding that you should be able to swap out collection types freely between version but we get a ProtoBuf.ProtoException[TestFixture, Category("Framework")]class CollectionTypeChange{ [Test] public void TestRoundTrip() { var bytes = new List<byte>() {1,2,4}; var a = new ArrayHolder(bytes); var aCopy = Deserialize<ArrayHolder>(Serialize(a)); //Passes Assert.That(aCopy.CollectionOfBytes, Is.EquivalentTo(a.CollectionOfBytes)); } [Test] public void TestChangeArrayToList() { var bytes = new List<byte>() { 1, 2, 4 }; var a = new ArrayHolder(bytes); var aCopy = Deserialize<ListHolder>(Serialize(a)); //Passes Assert.That(aCopy.CollectionOfBytes, Is.EquivalentTo(a.CollectionOfBytes)); } [Test] public void TestChangeListToArray() { var bytes = new List<byte>() { 1, 2, 4 }; var a = new ListHolder(bytes); //Throws: ProtoBuf.ProtoException : Invalid wire-type; this usually means you have over-written a file without truncating or setting the length; see http://stackoverflow.com/q/2152978/23354 var aCopy = Deserialize<ArrayHolder>(Serialize(a)); Assert.That(aCopy.CollectionOfBytes, Is.EquivalentTo(a.CollectionOfBytes)); } public static byte[] Serialize<T>(T obj) { using (var stream = new MemoryStream()) { Serializer.Serialize(stream, obj); return stream.ToArray(); } } public static T Deserialize<T>(byte[] buffer) { using (var stream = new MemoryStream(buffer)) { return Serializer.Deserialize<T>(stream); } }}[ProtoContract]internal class ArrayHolder{ private ArrayHolder() { CollectionOfBytes = new byte[0] {}; } internal ArrayHolder(IEnumerable<byte> bytesToUse ) { CollectionOfBytes = bytesToUse.ToArray(); } [ProtoMember(1)] public byte[] CollectionOfBytes { get; set; }}[ProtoContract]internal class ListHolder{ private ListHolder() { CollectionOfBytes = new List<byte>(); } internal ListHolder(IEnumerable<byte> bytesToUse) { CollectionOfBytes = bytesToUse.ToList(); } [ProtoMember(1)] public List<byte> CollectionOfBytes { get; set; }}数组或字节是否有什么特别之处,这意味着它不像我们预期的那样工作?Is there a special thing about arrays, or bytes that means this doesn't work like we expected?推荐答案这看起来是 byte[] 属性的问题.如果我将属性类型更改为 int [] 和 List ,则行为不可重现.问题源于以下事实,即有两种方法可以在协议缓冲区中对数组进行编码:作为重复的键/值对或 "packed" 作为具有长度分隔的值块的单个键.This looks to be a problem specifically with byte[] properties. If I change the property types to int [] and List<int> the behavior is not reproducible. The problem arises from the fact that there are two ways to encode an array in a Protocol Buffer: as repeated key/value pairs or "packed" as a single key with a length-delimited block of values.对于字节数组,protobuf-net 使用了一个特殊的序列化器,BlobSerializer,它简单地写入字节数组长度,然后将内容块复制到输出缓冲区作为打包重复字段.它在读取时做相反的操作——不处理数据实际上是重复的键/值格式的情况.For byte arrays, protobuf-net uses a special serializer, BlobSerializer, which simply writes the byte array length then block-copies the contents into the output buffer as a packed repeated field. It does the reverse operation when reading -- not handling the case when the data is actually in repeated key/value format.另一方面,List 使用通用的ListDecorator.它的 Read() 方法测试以查看当前在输入缓冲区中的格式并适当地读取它——打包或解包.然而,它的 Write() 方法写入默认解压的字节数组.随后,当将缓冲区读入 byte [] 数组时,BlobSerializer 会抛出异常,因为格式不符合预期.可以说这是 protobuf-net 的 BlobSerializer 的一个错误.On the other hand, List<byte> is serialized using the general-purpose ListDecorator. Its Read() method tests to see the format currently in the input buffer and reads it appropriately -- either packed or unpacked. Its Write() method, however, writes the byte array unpacked by default. Subsequently, when reading the buffer into a byte [] array, BlobSerializer throws an exception because the format is not as expected. Arguably this is a bug with protobuf-net's BlobSerializer.This should be a more compact representation for your list of bytes as well.不幸的是,当字节集合包含设置了高位的字节时,上述解决方法失败.Protobuf-net 将打包的 List 序列化为 基于 128 个变量.因此,当一个设置了高位的字节被序列化时,它被编码为两个字节.另一方面,byte [] 成员像字符串一样序列化为 以长度分隔的原始字节序列.因此,字节数组中的一个字节总是在编码中被编码为字节——这与 List 的编码不兼容.Unfortunately, the above workaround fails when the byte collection contains bytes with the high bit set. Protobuf-net serializes a packed List<byte> as a length-delimited sequence of Base 128 Varints. Thus when a byte with its high bit set is serialized, it is encoded as two bytes. On the other hand a byte [] member is serialized like a string as a length-delimited sequence of raw bytes. Thus one byte in the byte array is always encoded as byte in the encoding - which is incompatible with the encoding for List<byte>.作为一种解决方法,可以在 ArrayHolder 类型中使用私有代理 List 属性:As a workaround, one could use a private surrogate List<byte> property in the ArrayHolder type:[ProtoContract]internal class ArrayHolder{ private ArrayHolder() { CollectionOfBytes = new byte[0] { }; } internal ArrayHolder(IEnumerable<byte> bytesToUse) { CollectionOfBytes = bytesToUse.ToArray(); } [ProtoIgnore] public byte[] CollectionOfBytes { get; set; } [ProtoMember(1, OverwriteList = true)] List<byte> ListOfBytes { get { if (CollectionOfBytes == null) return null; return new List<byte>(CollectionOfBytes); } set { if (value == null) return; CollectionOfBytes = value.ToArray(); } }}示例小提琴.或者,可以在(反)序列化期间使用 ArrayHolder 替换为 ListHolder-net/blob/master/protobuf-net/Meta/MetaType.cs#L1296" rel="nofollow noreferrer">MetaType.SetSurrogate() 例如这个答案.Alternatively, one could replace the ArrayHolder with a ListHolder during (de)serialization by using MetaType.SetSurrogate() as shown for instance in this answer. 这篇关于如何更改我之前保存的 List 类型以序列化为 Array 类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云! 09-02 11:14