c# - google protobuf dll
SerializaciĆ³n de protobuf-net sin anotaciĆ³n. (3)
Es bastante experimental en esta fase, pero he creado una pequeña biblioteca que toma la mayoría de los Tipos y genera los serializadores Protobuf-net en tiempo de ejecución: https://github.com/fnicollier/AutoProtobuf
Miré this respuesta y estoy en una situación en la que no necesito mantener la compatibilidad con versiones anteriores y debo tener una solución que funcione sin tener que decorar docenas de clases con los atributos necesarios para protobuf-net. Así que intenté usar RuntimeTypeModel.Default.InferTagFromNameDefault = true;
pero es posible que no lo esté usando correctamente porque la llamada de Serializer.Serialize todavía produce una excepción que solicita un contrato. Aquí está mi prueba rápida, ¿qué estoy haciendo mal?
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<string> 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<Product> 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<string> { "Bob", "John" },
Company = new Company
{
Name = "Test Company",
Address = "Timbuktu",
Type = CompanyTypes.Startup,
Products = new List<Product>
{
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);
}
}
}
La pregunta es vieja, pero tal vez alguien necesite esto. Implementé la clase ProtobufSerializer que construirá su tipo gráfico en el uso. Solo necesita anotar su DTO con los atributos [KnownTypeAttribute] y [DataMember] / [IgnoreDataMember] . Principalmente esta es la versión refactorizada de otro proyecto nuget de algún tipo. De esta manera, no necesita incluir protobuf en las dependencias de su contrato:
internal sealed class ProtobufSerializer
{
private readonly RuntimeTypeModel _model;
private const BindingFlags Flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
private readonly Dictionary<Type, HashSet<Type>> _subTypes = new Dictionary<Type, HashSet<Type>>();
private readonly ConcurrentDictionary<Type, bool> _builtTypes = new ConcurrentDictionary<Type, bool>();
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<T>(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<Type> GetGraph(Type type)
{
return type.TraverseDistinct(GetConnections).Distinct().OrderBy(x=> x.FullName);
}
private static Type GetParent(Type type)
{
return type.BaseType;
}
private static IEnumerable<Type> GetChildren(Type type)
{
var knownTypes = type.GetCustomAttributes(typeof(KnownTypeAttribute)).Cast<KnownTypeAttribute>().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<Type> 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<Type> baseTypeEntry;
if (!_subTypes.TryGetValue(baseType, out baseTypeEntry))
{
baseTypeEntry = new HashSet<Type>();
_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);
}
}
}
}
Y también algunas extensiones simples:
public static IEnumerable<T> TraverseDistinct<T>(this T enumer, Func<T, IEnumerable<T>> getChildren)
{
return new[] { enumer }.TraverseDistinct(getChildren);
}
public static IEnumerable<T> TraverseDistinct<T>(this IEnumerable<T> enumer, Func<T, IEnumerable<T>> getChildren)
{
HashSet<T> visited = new HashSet<T>();
Stack<T> stack = new Stack<T>();
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);
}
}
}
}
}
InferTagFromName
(y es gemelo, InferTagFromNameDefault
) solo toma una mano cuando es necesario resolver un número de etiqueta para un miembro; no influyen en qué miembros necesitan ser serializados (por lo que actualmente la respuesta sería: ninguno, incluso si el sistema los conociera). La opción que podría haber elegido sería ImplicitFields
, pero actualmente solo está disponible como [ProtoContract(...)]
. Si no te importa una pequeña anotación, una solución pragmática puede ser:
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)]
en User
, Company
y Product
, y algo un poco más complejo para BaseUser
(debido a la herencia):
[ProtoContract(ImplicitFields = ImplicitFields.AllPublic, ImplicitFirstTag = 10)]
[ProtoInclude(1, typeof(User))]
Tenga en cuenta que no hemos tenido que agregar muchas anotaciones por miembro. Si realmente eres anti-atributos, entonces también es posible configurar todo el modelo a través del código, a través de:
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));