Désérialiser type inconnu avec protobuf-net
J'ai 2 en réseau des applications qui devraient envoyer sérialisé protobuf-net des messages les uns aux autres. Je peux sérialiser les objets et de les envoyer, cependant, je ne peux pas comprendre comment désérialiser les octets reçus.
J'ai essayé de désérialiser avec cela et il a échoué avec une exception NullReferenceException.
//Where "ms" is a memorystream containing the serialized
//byte array from the network.
Messages.BaseMessage message =
ProtoBuf.Serializer.Deserialize<Messages.BaseMessage>(ms);
Je suis de passage d'un en-tête de la sérialisé octets qui contient ID type de message, que je peux utiliser dans un géant de l'instruction switch pour le retour attendu sous-classe Type. Avec le bloc ci-dessous, je reçois l'erreur: System.De la réflexion.TargetInvocationException ---> Système.NullReferenceException.
//Where "ms" is a memorystream and "messageType" is a
//Uint16.
Type t = Messages.Helper.GetMessageType(messageType);
System.Reflection.MethodInfo method =
typeof(ProtoBuf.Serializer).GetMethod("Deserialize").MakeGenericMethod(t);
message = method.Invoke(null, new object[] { ms }) as Messages.BaseMessage;
Voici la fonction que j'utilise pour envoyer un message sur le réseau:
internal void Send(Messages.BaseMessage message){
using (System.IO.MemoryStream ms = new System.IO.MemoryStream()){
ProtoBuf.Serializer.Serialize(ms, message);
byte[] messageTypeAndLength = new byte[4];
Buffer.BlockCopy(BitConverter.GetBytes(message.messageType), 0, messageTypeAndLength, 0, 2);
Buffer.BlockCopy(BitConverter.GetBytes((UInt16)ms.Length), 0, messageTypeAndLength, 2, 2);
this.networkStream.Write(messageTypeAndLength);
this.networkStream.Write(ms.ToArray());
}
}
Cette classe, avec de la classe de base, je suis la sérialisation:
[Serializable,
ProtoContract,
ProtoInclude(50, typeof(BeginRequest))]
abstract internal class BaseMessage
{
[ProtoMember(1)]
abstract public UInt16 messageType { get; }
}
[Serializable,
ProtoContract]
internal class BeginRequest : BaseMessage
{
[ProtoMember(1)]
public override UInt16 messageType
{
get { return 1; }
}
}
Fixe à l'aide de Marc Gravel de la suggestion. J'ai enlevé le ProtoMember attribut à partir de la lecture seule des propriétés. Est également passé à l'aide de SerializeWithLengthPrefix. Voici ce que j'ai maintenant:
[Serializable,
ProtoContract,
ProtoInclude(50, typeof(BeginRequest))]
abstract internal class BaseMessage
{
abstract public UInt16 messageType { get; }
}
[Serializable,
ProtoContract]
internal class BeginRequest : BaseMessage
{
public override UInt16 messageType
{
get { return 1; }
}
}
Pour recevoir un objet:
//where "this.Ssl" is an SslStream.
BaseMessage message =
ProtoBuf.Serializer.DeserializeWithLengthPrefix<BaseMessage>(
this.Ssl, ProtoBuf.PrefixStyle.Base128);
Pour envoyer un objet:
//where "this.Ssl" is an SslStream and "message" can be anything that
//inherits from BaseMessage.
ProtoBuf.Serializer.SerializeWithLengthPrefix<BaseMessage>(
this.Ssl, message, ProtoBuf.PrefixStyle.Base128);
- J'ai oublié de le mentionner, je suis de la sérialisation dans .NET 3.5 sur Windows et la désérialisation en Mono 2.2 et je suis à l'aide de la protobuf-net dll sur chaque plate-forme.
- Je serai de retour à lire ceci et poste une réponse dans environ une demi-heure... faut courir pour le moment, désolé. BTW - la prochaine version non-generic wrappers construite en encore sur mon portable à l'instant.
- btw - je suis en train de travailler sur la fusion de ma copie locale, afin que je puisse valider les modifications pour rendre cela plus facile. J'ai un remarquable essai de l'échec, mais qui couvre le nouveau code, donc je suis content de le commettre (marqué comme ignoré) si ça aide.
- Re le fix - je vais vous mettre dans une meilleure manipulation afin de le rendre plus évident dans le futur...
- J'apprécie l'offre à engager le code, mais je avoir de travail à l'aide de médicaments génériques et d'une classe de base. Si vous pensez que ça va être plus rapide ou le moins de lignes de code, je serai heureux de l'essayer.
Vous devez vous connecter pour publier un commentaire.
Premier; pour l'utilisation du réseau, il est
SerializeWithLengthPrefix
etDeserializeWithLengthPrefix
qui longueur de la poignée pour vous (éventuellement avec un tag). LeMakeGenericMethod
semble OK à première vue; et de ce fait des liens en de très près à l'attente de la livraison du travail que je fais pour mettre en œuvre un RPC de la pile: le code en attentehas an override of DeserializeWithLengthPrefix
qui prend (essentiellement) unFunc<int,Type>
, pour résoudre une balise à un type pour le rendre plus facile pour désérialiser des données inattendues à la volée.Si le type de message se rapporte en fait à l'héritage entre
BaseMessage
etBeginRequest
, alors vous n'avez pas besoin de cela, il va toujours au plus haut type de contrat dans la hiérarchie et travaille son chemin vers le bas (à cause de certains détails du virement).Aussi - je n'ai pas eu l'occasion de le tester, mais ce qui suit peut être dérangeant c':
Il est marqué pour la sérialisation, mais n'a pas de mécanisme pour le réglage de la valeur. C'est peut-être le problème? Essayez de supprimer le
[ProtoMember]
ici, puisque je n'ai pas cette fonction est utile - c'est (aussi loin que la sérialisation est concerné), en grande partie une copie de la[ProtoInclude(...)]
marqueur.ou
Serializer.NonGeneric.Deserialize(...)
(prend unType
paramètre, pas un<T>
argument de type générique)Une autre façon de gérer cela est d'utiliser protobuf-net pour le "gros œuvre", mais d'utiliser votre propre en-tête de message. Le problème avec le réseau de traitement des messages, c'est qu'ils peuvent être cassés à travers les frontières. Cela nécessite généralement l'aide d'un tampon à accumuler de lit. Si vous utilisez votre propre en-tête, vous pouvez être sûr que le message est là, dans son intégralité avant de remettre à protobuf-net.
Comme un exemple:
Pour envoyer
Pour recevoir
La méthode de ce dernier serait "essayé" sur un accumulateur tampon jusqu'à ce qu'il y avait suffisamment de données. Une fois que la condition est remplie, le message peut être désérialisé.