没有注释的Protobuf-net序列化

我查看了这个答案,我处于这样一种情况:我不需要保持向后兼容性,我必须有一个解决方案,无需用protobuf-net所需的属性装饰数十个类。 所以我尝试使用RuntimeTypeModel.Default.InferTagFromNameDefault = true; 但我可能没有正确使用它,因为Serializer.Serialize调用仍然会引发exception,要求签订合同。 这是我的快速测试,我做错了什么?

 public enum CompanyTypes { None, Small, Big, Enterprise, Startup } public class BaseUser { public string SSN { get; set; } } public class User : BaseUser { public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } public DateTime BirthDate { get; set; } public List Friends { get; set; } public Company Company { get; set; } } public class Company { public string Name { get; set; } public string Address { get; set; } public CompanyTypes Type { get; set; } public List Products { get; set; } } public class Product { public string Name { get; set; } public string Sku { get; set; } } [TestClass] public class SerializationTest { [TestMethod] public void SerializeDeserializeTest() { var user = new User { Age = 10, BirthDate = DateTime.Now.AddYears(-10), FirstName = "Test First", LastName = "Test Last", Friends = new List { "Bob", "John" }, Company = new Company { Name = "Test Company", Address = "Timbuktu", Type = CompanyTypes.Startup, Products = new List { new Product{Name="Nerf Rocket", Sku="12324AC"}, new Product{Name="Nerf Dart", Sku="DHSN123"} } } }; RuntimeTypeModel.Default.InferTagFromNameDefault = true; using (var memoryStream = new MemoryStream()) { Serializer.Serialize(memoryStream, user); var serialized = Convert.ToBase64String(memoryStream.GetBuffer(), 0, (int)memoryStream.Length); } } } 

InferTagFromName (它是双胞胎, InferTagFromNameDefault )只有在需要解析成员的标签号时才会牵手; 它们不会影响哪些成员需要序列化(所以目前答案就是:没有,即使系统知道它们)。 您可能选择的选项是ImplicitFields ,但目前仅作为[ProtoContract(...)]标记提供。 如果你不介意一点注释,一个实用的修复可能是:

 [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)] 

UserCompanyProduct ,以及BaseUser更复杂的BaseUser (因为inheritance):

 [ProtoContract(ImplicitFields = ImplicitFields.AllPublic, ImplicitFirstTag = 10)] [ProtoInclude(1, typeof(User))] 

请注意,我们不必添加大量的每个成员注释。 如果你真的是反属性,那么也可以通过代码配置整个模型,通过:

 RuntimeTypeModel.Default.Add(typeof(Product), false).Add("Name", "Sku"); RuntimeTypeModel.Default.Add(typeof(Company), false).Add("Name", "Address", "Type", "Products"); RuntimeTypeModel.Default.Add(typeof(User), false).Add("FirstName", "LastName", "Age", "BirthDate", "Friends", "Company"); RuntimeTypeModel.Default.Add(typeof(BaseUser), false).Add(10, "SSN") .AddSubType(1, typeof(User)); 

问题是陈旧的,但也许有人需要这个。 我实现了ProtobufSerializer类,它将在使用时构造你的Type图。 您只需要使用[KnownTypeAttribute][DataMember] / [IgnoreDataMember]属性来注释您的DTO。 大多数情况下,这是来自某个人的另一个nuget项目的重构版本。 这样您就不需要在合同依赖项中包含protobuf:

  internal sealed class ProtobufSerializer { private readonly RuntimeTypeModel _model; private const BindingFlags Flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; private readonly Dictionary> _subTypes = new Dictionary>(); private readonly ConcurrentDictionary _builtTypes = new ConcurrentDictionary(); private static readonly Type[] ComplexPrimitives = new [] { typeof(object), typeof(ValueType), typeof(Enum), typeof(Array)}; private readonly object _sync = new object(); public ProtobufSerializer() { _model = TypeModel.Create(); } public void Serialize(Stream s, object input) { EnsureType(input.GetType()); _model.Serialize(s, input); } public T Deserialize(Stream s) { EnsureType(typeof(T)); return (T)_model.Deserialize(s, null, typeof(T)); } public void EnsureType(Type type) { if (_builtTypes.ContainsKey(type)) { return; } lock (_sync) { if (_builtTypes.ContainsKey(type)) { return; } var all = GetGraph(type).ToArray(); foreach (var t in all) { InternalBuild(t); } } } private void InternalBuild(Type type) { if (IsPrimitive(type)) { return; } FlatBuild(type); EnsureBaseClasses(type); EnsureGenerics(type); _builtTypes.TryAdd(type, false); } private bool IsPrimitive(Type type) { return type == null || type.IsPrimitive || _model.CanSerializeBasicType(type) || _builtTypes.ContainsKey(type) || ComplexPrimitives.Contains(type); } private static IEnumerable GetGraph(Type type) { return type.TraverseDistinct(GetConnections).Distinct().OrderBy(x=> x.FullName); } private static Type GetParent(Type type) { return type.BaseType; } private static IEnumerable GetChildren(Type type) { var knownTypes = type.GetCustomAttributes(typeof(KnownTypeAttribute)).Cast().Select(x => x.Type).ToArray(); foreach (var t in knownTypes) { yield return t; } var fields = GetFields(type); var props = GetProperties(type); foreach (var memberType in fields.Select(f => f.FieldType)) { yield return memberType; } foreach (var memberType in props.Select(f => f.PropertyType)) { yield return memberType; } } private static IEnumerable GetConnections(Type type) { var parent = GetParent(type); if (parent != null) { yield return parent; } var children = GetChildren(type); if (children != null) { foreach (var c in children) { yield return c; } } } private void FlatBuild(Type type) { if(type.IsAbstract) return; var meta = _model.Add(type, false); var fields = GetFields(type); var props = GetProperties(type); meta.Add(fields.Select(m => m.Name).ToArray()); meta.Add(props.Select(m => m.Name).ToArray()); meta.UseConstructor = false; foreach (var memberType in fields.Select(f => f.FieldType).Where(t => !t.IsPrimitive)) { InternalBuild(memberType); } foreach (var memberType in props.Select(f => f.PropertyType).Where(t => !t.IsPrimitive)) { InternalBuild(memberType); } } private static FieldInfo[] GetFields(Type type) { return type.GetFields(Flags).Where(x => x.IsDefined(typeof(DataMemberAttribute))).Where(x => !x.IsDefined(typeof(IgnoreDataMemberAttribute))).ToArray(); } private static PropertyInfo[] GetProperties(Type type) { return type.GetProperties(Flags).Where(x => x.IsDefined(typeof(DataMemberAttribute))).Where(x=> !x.IsDefined(typeof(IgnoreDataMemberAttribute))).ToArray(); } private void EnsureBaseClasses(Type type) { var baseType = type.BaseType; var inheritingType = type; while (!IsPrimitive(baseType)) { HashSet baseTypeEntry; if (!_subTypes.TryGetValue(baseType, out baseTypeEntry)) { baseTypeEntry = new HashSet(); _subTypes.Add(baseType, baseTypeEntry); } if (!baseTypeEntry.Contains(inheritingType)) { InternalBuild(baseType); _model[baseType].AddSubType(baseTypeEntry.Count + 500, inheritingType); baseTypeEntry.Add(inheritingType); } inheritingType = baseType; baseType = baseType.BaseType; } } private void EnsureGenerics(Type type) { if (type.IsGenericType || (type.BaseType != null && type.BaseType.IsGenericType)) { var generics = type.IsGenericType ? type.GetGenericArguments() : type.BaseType.GetGenericArguments(); foreach (var generic in generics) { InternalBuild(generic); } } } } 

还有一些简单的扩展:

  public static IEnumerable TraverseDistinct(this T enumer, Func> getChildren) { return new[] { enumer }.TraverseDistinct(getChildren); } public static IEnumerable TraverseDistinct(this IEnumerable enumer, Func> getChildren) { HashSet visited = new HashSet(); Stack stack = new Stack(); foreach (var e in enumer) { stack.Push(e); } while (stack.Count > 0) { var i = stack.Pop(); yield return i; visited.Add(i); var children = getChildren(i); if (children != null) { foreach (var child in children) { if (!visited.Contains(child)) { stack.Push(child); } } } } } 

它在这个阶段非常具有实验性,但是我创建了一个小型库,它可以获取大多数类型并在运行时生成Protobuf-net序列化器: https : //github.com/fnicollier/AutoProtobuf