#if (FEAT_SERVICEMODEL && PLAT_XMLSERIALIZER) || (SILVERLIGHT && !PHONE7) using System.IO; using System.Runtime.Serialization; using System.Xml; using ProtoBuf.Meta; using System; namespace ProtoBuf.ServiceModel { /// /// An xml object serializer that can embed protobuf data in a base-64 hunk (looking like a byte[]) /// public sealed class XmlProtoSerializer : XmlObjectSerializer { private readonly TypeModel model; private readonly int key; private readonly bool isList, isEnum; private readonly Type type; internal XmlProtoSerializer(TypeModel model, int key, Type type, bool isList) { if (model == null) throw new ArgumentNullException(nameof(model)); if (key < 0) throw new ArgumentOutOfRangeException(nameof(key)); if (type == null) throw new ArgumentOutOfRangeException(nameof(type)); this.model = model; this.key = key; this.isList = isList; this.type = type; this.isEnum = Helpers.IsEnum(type); } /// /// Attempt to create a new serializer for the given model and type /// /// A new serializer instance if the type is recognised by the model; null otherwise public static XmlProtoSerializer TryCreate(TypeModel model, Type type) { if (model == null) throw new ArgumentNullException(nameof(model)); if (type == null) throw new ArgumentNullException(nameof(type)); bool isList; int key = GetKey(model, ref type, out isList); if (key >= 0) { return new XmlProtoSerializer(model, key, type, isList); } return null; } /// /// Creates a new serializer for the given model and type /// public XmlProtoSerializer(TypeModel model, Type type) { if (model == null) throw new ArgumentNullException(nameof(model)); if (type == null) throw new ArgumentNullException(nameof(type)); key = GetKey(model, ref type, out isList); this.model = model; this.type = type; this.isEnum = Helpers.IsEnum(type); if (key < 0) throw new ArgumentOutOfRangeException(nameof(type), "Type not recognised by the model: " + type.FullName); } static int GetKey(TypeModel model, ref Type type, out bool isList) { if (model != null && type != null) { int key = model.GetKey(ref type); if (key >= 0) { isList = false; return key; } Type itemType = TypeModel.GetListItemType(model, type); if (itemType != null) { key = model.GetKey(ref itemType); if (key >= 0) { isList = true; return key; } } } isList = false; return -1; } /// /// Ends an object in the output /// public override void WriteEndObject(System.Xml.XmlDictionaryWriter writer) { if (writer == null) throw new ArgumentNullException(nameof(writer)); writer.WriteEndElement(); } /// /// Begins an object in the output /// public override void WriteStartObject(System.Xml.XmlDictionaryWriter writer, object graph) { if (writer == null) throw new ArgumentNullException(nameof(writer)); writer.WriteStartElement(PROTO_ELEMENT); } private const string PROTO_ELEMENT = "proto"; /// /// Writes the body of an object in the output /// public override void WriteObjectContent(System.Xml.XmlDictionaryWriter writer, object graph) { if (writer == null) throw new ArgumentNullException(nameof(writer)); if (graph == null) { writer.WriteAttributeString("nil", "true"); } else { using (MemoryStream ms = new MemoryStream()) { if (isList) { model.Serialize(ms, graph, null); } else { using (ProtoWriter protoWriter = new ProtoWriter(ms, model, null)) { model.Serialize(key, graph, protoWriter); } } byte[] buffer = ms.GetBuffer(); writer.WriteBase64(buffer, 0, (int)ms.Length); } } } /// /// Indicates whether this is the start of an object we are prepared to handle /// public override bool IsStartObject(System.Xml.XmlDictionaryReader reader) { if (reader == null) throw new ArgumentNullException(nameof(reader)); reader.MoveToContent(); return reader.NodeType == System.Xml.XmlNodeType.Element && reader.Name == PROTO_ELEMENT; } /// /// Reads the body of an object /// public override object ReadObject(System.Xml.XmlDictionaryReader reader, bool verifyObjectName) { if (reader == null) throw new ArgumentNullException(nameof(reader)); reader.MoveToContent(); bool isSelfClosed = reader.IsEmptyElement, isNil = reader.GetAttribute("nil") == "true"; reader.ReadStartElement(PROTO_ELEMENT); // explicitly null if (isNil) { if(!isSelfClosed) reader.ReadEndElement(); return null; } if(isSelfClosed) // no real content { if (isList || isEnum) { return model.Deserialize(Stream.Null, null, type, null); } ProtoReader protoReader = null; try { protoReader = ProtoReader.Create(Stream.Null, model, null, ProtoReader.TO_EOF); return model.Deserialize(key, null, protoReader); } finally { ProtoReader.Recycle(protoReader); } } object result; Helpers.DebugAssert(reader.CanReadBinaryContent, "CanReadBinaryContent"); using (MemoryStream ms = new MemoryStream(reader.ReadContentAsBase64())) { if (isList || isEnum) { result = model.Deserialize(ms, null, type, null); } else { ProtoReader protoReader = null; try { protoReader = ProtoReader.Create(ms, model, null, ProtoReader.TO_EOF); result = model.Deserialize(key, null, protoReader); } finally { ProtoReader.Recycle(protoReader); } } } reader.ReadEndElement(); return result; } } } #endif