在不使用Attributes的情况下序列化复杂属性

时间:2013-07-09 17:53:26

标签: protobuf-net

我有一个项目,我不能在我要序列化的类型上使用序列化属性。一般来说,我这样做是有效的:

private byte[] Serialize(object value)
{
    var type = value.GetType();
    var typeModel = RuntimeTypeModel.Default.Add(type, false);

    foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
    {
        typeModel.Add(prop.Name);
    }

    using (var stream = new MemoryStream())
    {
        try
        {
            Serializer.Serialize(stream, value);
            return stream.ToArray();
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

但是我有一种类型DynamicEntity(见下文),这对于不会序列化的整个解决方案至关重要。我一直在研究它,但我无法想办法让RuntimeTypeModel保存正确的序列化信息。相反,Serialize会使用消息

继续抛出InvalidCastException
  
    

“无法将类型为'OpenNETCF.ORM.FieldValue'的对象强制转换为'System.String'。”

  

以下是相关的类定义:

public class DynamicEntity
{
    public DynamicEntity();

    public string EntityName { get; set; }
    public FieldCollection Fields { get; }
}

public class FieldCollection : IEnumerable<FieldValue>, IEnumerable
{
    public int Count { get; }

    public object this[string fieldName] { get; set; }

    public void Add(string fieldName);
    public void Add(string fieldName, object value);
    public IEnumerator<FieldValue> GetEnumerator();
}

public class FieldValue
{
    public string Name { get; }
    public object Value { get; set; }
}

FieldValue通常只包含简单值 - 数据库字段可能包含的内容。我能够修改上面类的定义(即我拥有它们),但我不想强迫其他类型的消费者反过来引用或使用protobuf。

2 个答案:

答案 0 :(得分:1)

Protobuf-net不太喜欢object - 它想要理解架构。这里的一个选择是将FieldValue作为抽象,使用通用子类FieldValue<T>,显式提供不同的子类标识符。这可以通过[ProtoInclude]RuntimeTypeModel完成。然而,对我来说,消费者的问题在这里并不明显,即属性是否是一个问题。你能澄清一下吗?

答案 1 :(得分:1)

嗯,最后它肯定不像我希望的那么简单,而且它不像我想的那么强大,因为我不得不在可行的类型中进行硬编码,但解决方案我是我正在使用有一组有限的类型(现在无论如何)所以这一点封装古怪,在Marc的间接帮助下工作:

[ProtoContract]
internal class SerializableDynamicEntity
{
    [ProtoMember(1)]
    public string EntityName { get; set; }

    [ProtoMember(2)]
    public List<SerializableFieldValue> Fields { get; set; }

    public SerializableDynamicEntity()
    {
        Fields = new List<SerializableFieldValue>();
    }

    private SerializableDynamicEntity(string name)
        : this()
    {
        EntityName = name;
    }

    public static explicit operator SerializableDynamicEntity(DynamicEntity de)
    {
        var sde = new SerializableDynamicEntity(de.EntityName);

        foreach (var f in de.Fields)
        {
            sde.Fields.Add(SerializableFieldValue.Create(f));
        }

        return sde;
    }

    public static explicit operator DynamicEntity(SerializableDynamicEntity sde)
    {
        var de = new DynamicEntity(sde.EntityName);

        foreach (var f in sde.Fields)
        {
            de.Fields.Add(f.Name, f.UntypedValue);
        }

        return de;
    }
}

[ProtoContract]
[ProtoInclude(3, typeof(SerializableFieldValue<bool>))]
[ProtoInclude(4, typeof(SerializableFieldValue<int>))]
[ProtoInclude(5, typeof(SerializableFieldValue<double>))]
[ProtoInclude(6, typeof(SerializableFieldValue<string>))]
[ProtoInclude(7, typeof(SerializableFieldValue<DateTime>))]
[ProtoInclude(8, typeof(SerializableFieldValue<long>))]
[ProtoInclude(9, typeof(SerializableFieldValue<short>))]
internal abstract class SerializableFieldValue
{
    public static SerializableFieldValue<T> Create<T>(string name, T value)
    {
        return new SerializableFieldValue<T>()
        {
            Name = name,
            Value = value
        };
    }

    public static SerializableFieldValue Create(FieldValue f)
    {
        var type = f.Value.GetType();

        switch (Type.GetTypeCode(type))
        {
            case TypeCode.Boolean:
                return Create(f.Name, (bool)f.Value);
            case TypeCode.Int32:
                return Create(f.Name, (int)f.Value);
            case TypeCode.Double:
                return Create(f.Name, (double)f.Value);
            case TypeCode.String:
                return Create(f.Name, (string)f.Value);
            case TypeCode.DateTime:
                return Create(f.Name, (DateTime)f.Value);
            case TypeCode.Int64:
                return Create(f.Name, (long)f.Value);
            case TypeCode.Int16:
                return Create(f.Name, (short)f.Value);
            default:
                throw new NotSupportedException();
        }
    }

    [ProtoMember(1)]
    public string Name { get; set; }
    public abstract object UntypedValue { get; set; }
}

[ProtoContract]
internal sealed class SerializableFieldValue<T> : SerializableFieldValue
{
    public SerializableFieldValue()
    {
    }

    [ProtoMember(2)]
    public T Value { get; set; }

    public override object UntypedValue
    {
        get { return Value; }
        set { Value = (T)value; }
    }
}

这样原始基本代码不需要属性,甚至不需要任何更改,但需要序列化的特定商店实现可以在内部隐藏它。