尝试序列化PropertyInfo时引发异常

本文关键字:异常 PropertyInfo 序列化 | 更新日期: 2024-07-27 11:09:00

是否应该支持序列化PropertyInfo?如果没有,最好的解决方法是什么?我知道可以序列化类型(这是受支持的),然后根据属性查找属性——想象一下查找可能会很昂贵。

p.s.PropertyInfo用于实现类似于DataLoadOptions.LoadWith的功能http://msdn.microsoft.com/en-us/library/bb548760.aspx.

using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ProtoBuf;
[ProtoContract]
public class A
{
    [ProtoMember(1)]
    public PropertyInfo Property { get; set; }
}
[TestClass]
public class UnitTest
{
    [TestMethod]
    public void TestMethod1()
    {
        var value = new A { Property = typeof(string).GetProperty("Length") };
        using (var stream = new MemoryStream())
        {
            // Raises InvalidOperationException
            // "No serializer defined for type: System.Reflection.PropertyInfo"
            Serializer.Serialize(stream, value);
        }
    }
}

引发InvalidOperationException时调用堆栈:

protobuf-net.dll!ProtoBuf.Meta.ValueMember.BuildSerializer() Line 316   C#
protobuf-net.dll!ProtoBuf.Meta.ValueMember.Serializer.get() Line 188 + 0x24 bytes   C#
protobuf-net.dll!ProtoBuf.Meta.MetaType.BuildSerializer() Line 421 + 0x1f bytes C#
protobuf-net.dll!ProtoBuf.Meta.MetaType.Serializer.get() Line 344 + 0xe bytes   C#
protobuf-net.dll!ProtoBuf.Meta.RuntimeTypeModel.Serialize(int key, object value, ProtoBuf.ProtoWriter dest) Line 592 + 0x44 bytes   C#
protobuf-net.dll!ProtoBuf.Meta.TypeModel.SerializeCore(ProtoBuf.ProtoWriter writer, object value) Line 178 + 0x14 bytes C#
protobuf-net.dll!ProtoBuf.Meta.TypeModel.Serialize(System.IO.Stream dest, object value, ProtoBuf.SerializationContext context) Line 209 + 0xe bytes C#
protobuf-net.dll!ProtoBuf.Meta.TypeModel.Serialize(System.IO.Stream dest, object value) Line 193 + 0x10 bytes   C#
protobuf-net.dll!ProtoBuf.Serializer.Serialize<TestProtoBufNet.A>(System.IO.Stream destination, TestProtoBufNet.A instance) Line 88 + 0x18 bytes    C#
TestProtoBufNet.dll!TestProtoBufNet.UnitTest.TestMethod1() Line 24 + 0x10 bytes C#

尝试序列化PropertyInfo时引发异常

要添加@ikh已经说过的内容,不,这不是支持的类型。当我提到标准BCL类型时,我指的是公共数据类型,而不是反射类型。protobuf和protobuf-net的全部目的是以数据为中心,但PropertyInfo等是实现,而不是数据。事实上,对于PropertyInfo应该拥有多少信息,WinRT有一个非常不同的想法。

然而!如果您决定序列化它,请注意Type支持的IIRC。所以你可能可以通过代理:来做到这一点(完全未经测试,我在iPad上!)

[ProtoContract]
public class PropertyInfoSurrogate {
    [ProtoMember(1)]
    public Type Type { get; set; }
    [ProtoMember(2)]
    public string Name { get; set; }
    public static operator implicit PropertyInfoSurrogate(PropertyInfo value) {
        if(value == null) return null;
        return new PropertyInfoSurrogate {
             Type = value.DeclaringType, Name = value.Name
        };
    }
    public static operator implicit PropertyInfo(PropertyInfoSurrogate value) {
        if(value == null) return null;
        return value.Type.GetProperty(value.Name);
    }
}

然后,到您的型号:

model.Add(typeof(PropertyInfo), false)
    .SetSurrogate(typeof(PropertyInfoSurrogate));

强调:完全未经测试,但像这样的东西应该有效。让我知道你进展如何。

不幸的是,看起来PropertyInfo不可由protobuf-net序列化。要序列化其中的数据,您可能需要创建一个单独的ProtoContract类来存储PropertyInfo的相关位,并使用它。

具体来说,通过阅读protobuf-net如何为复杂类型(此处和此处)找到序列化程序的源代码,看起来protobuf只能处理具有类似[DataContract]属性的类,特别是以下任何类:

  • CCD_ 9
  • CCD_ 10
  • CCD_ 11

不会与他人合作。

一个简单的实验证实了这一点。下面的代码将毫无例外地运行,但如果[DataContract][DataMember]被注释掉,那么我会得到与您相同的异常:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using ProtoBuf;
namespace ProtobufNetTest2 {
    [DataContract(Name = "Pet", Namespace = "http://www.example.com")]
    public class Pet  {
        [DataMember(Name = "Name")]
        public string Name { get; set; }
    }
    [ProtoContract]
    public class DogMessage {
        [ProtoMember(1)]
        public Pet Dog { get; set; }
    }
    class Program {
        static void Main(string[] args) {
            var dog = new Pet() {
                Name = "The Dog",
            };
            var dogMessage = new DogMessage() {
                Dog = dog,
            };
            using (var stream = new MemoryStream()) {
                Serializer.Serialize(stream, dogMessage);
            }
        }
    }
}