我有一个项目,我不能在我要序列化的类型上使用序列化属性。一般来说,我这样做是有效的:
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会使用消息
“无法将类型为'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。
答案 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; }
}
}
这样原始基本代码不需要属性,甚至不需要任何更改,但需要序列化的特定商店实现可以在内部隐藏它。