public ICollection <BlobObject> StreamObjects <TDoc, TCursor>(IO.TagElementStream <TDoc, TCursor, string> s, ICollection <BlobObject> results, Engine.BlamEngineTargetHandle gameTarget) where TDoc : class where TCursor : class { Contract.Requires <ArgumentNullException>(!gameTarget.IsNone); if (s.IsReading) { results = new List <BlobObject>(); } var ctxt = new SerializeObjectContext(this, gameTarget); s.StreamElements("BlobObject", results, ctxt, SerializeObject, _ctxt => null); return(results); }
static void SerializeObject <TDoc, TCursor>(IO.TagElementStream <TDoc, TCursor, string> s, SerializeObjectContext ctxt, ref BlobObject obj) where TDoc : class where TCursor : class { Contract.Requires(obj != null || s.IsReading); const string kAttributeNameSignature = "signature"; const string kAttributeNameVersion = "version"; if (s.IsReading) { string group_tag = null; s.ReadAttribute(kAttributeNameSignature, ref group_tag); int version = TypeExtensions.kNone; s.ReadAttribute(kAttributeNameVersion, ref version); BlobGroup blob_group; BlobGroupVersionAndBuildInfo info_for_version; if (ctxt.System.TryGetBlobGroup(group_tag, version, out blob_group, out info_for_version)) { var target_build = ctxt.GameTarget.Build; if (!info_for_version.BuildHandle.IsWithinSameBranch(target_build)) { s.ThrowReadException(SerializeObjectFoundBuildIncompatibility(ctxt.System, blob_group, version, info_for_version.BuildHandle, target_build)); } obj = ctxt.System.CreateObject(Blam.Engine.BlamEngineTargetHandle.None, blob_group, version); } else { string msg = string.Format("{0}: No blob matching '{1}'/v{2} exists", ctxt.GameTarget.ToDisplayString(), group_tag, version); s.ThrowReadException(new System.IO.InvalidDataException(msg)); } } else if (s.IsWriting) { s.WriteAttribute(kAttributeNameSignature, obj.SystemGroup.GroupTag.TagString); s.WriteAttribute(kAttributeNameVersion, obj.Version); } s.StreamAttribute("flags", obj, _obj => _obj.BlobFlags); obj.Serialize(s); }
private void SerializeObject(object obj, bool typeNeeded, XmlWriter writer, DesignerSerializationVisibility visibility) { #region Local Methods to reduce complexity bool TrySerializeKeyValue(ref SerializeObjectContext ctx) { // 1.) KeyValue 1: DictionaryEntry: can be serialized recursively. Just handling to avoid binary serialization. if (ctx.Type == Reflector.DictionaryEntryType) { if (ctx.TypeNeeded) { ctx.Writer.WriteAttributeString(XmlSerializer.AttributeType, GetTypeString(ctx.Type)); } SerializeMembers(ctx.Object, ctx.Writer, ctx.Visibility); return(true); } // 2.) KeyValue 2: KeyValuePair: properties are read-only so special support needed if (ctx.Type.IsGenericTypeOf(Reflector.KeyValuePairType)) { if (ctx.TypeNeeded) { ctx.Writer.WriteAttributeString(XmlSerializer.AttributeType, GetTypeString(ctx.Type)); } object key = Accessors.GetPropertyValue(ctx.Object, nameof(KeyValuePair <_, _> .Key)); object value = Accessors.GetPropertyValue(ctx.Object, nameof(KeyValuePair <_, _> .Value)); ctx.Writer.WriteStartElement(nameof(KeyValuePair <_, _> .Key)); if (key == null) { ctx.Writer.WriteEndElement(); } else { SerializeObject(key, key.GetType() != ctx.Type.GetGenericArguments()[0], ctx.Writer, ctx.Visibility); ctx.Writer.WriteFullEndElement(); } ctx.Writer.WriteStartElement(nameof(KeyValuePair <_, _> .Value)); if (value == null) { ctx.Writer.WriteEndElement(); } else { SerializeObject(value, value.GetType() != ctx.Type.GetGenericArguments()[1], ctx.Writer, ctx.Visibility); ctx.Writer.WriteFullEndElement(); } return(true); } return(false); } bool TrySerializeComplexObject(ref SerializeObjectContext ctx) { // 1.) collection: if can be trusted in all circumstances if (ctx.Object is IEnumerable enumerable) { Type elementType = null; // if can be trusted in all circumstances if (IsTrustedCollection(ctx.Type) // or recursive is requested || ((ctx.Visibility == DesignerSerializationVisibility.Content || RecursiveSerializationAsFallback) // and is a supported collection or serialization is forced && (ForceReadonlyMembersAndCollections || ctx.Type.IsSupportedCollectionForReflection(out var _, out var _, out elementType, out var _)))) { SerializeCollection(enumerable, elementType ?? ctx.Type.GetCollectionElementType(), ctx.TypeNeeded, ctx.Writer, ctx.Visibility); return(true); } if (ctx.Visibility == DesignerSerializationVisibility.Content || RecursiveSerializationAsFallback) { Throw.SerializationException(Res.XmlSerializationCannotSerializeUnsupportedCollection(ctx.Type, Options)); } Throw.SerializationException(Res.XmlSerializationCannotSerializeCollection(ctx.Type, Options)); } // 2.) recursive serialization of any object, if requested if (RecursiveSerializationAsFallback || ctx.Visibility == DesignerSerializationVisibility.Content // or when it has public properties/fields only || IsTrustedType(ctx.Type)) { if (ctx.TypeNeeded) { ctx.Writer.WriteAttributeString(XmlSerializer.AttributeType, GetTypeString(ctx.Type)); } SerializeMembers(ctx.Object, ctx.Writer, ctx.Visibility); return(true); } return(false); } #endregion if (obj == null) { return; } Type type = obj.GetType(); // a.) If type can be natively parsed, simple writing if (type.CanBeParsedNatively()) { if (typeNeeded) { writer.WriteAttributeString(XmlSerializer.AttributeType, GetTypeString(type)); } WriteStringValue(obj, writer); return; } // b.) IXmlSerializable if (obj is IXmlSerializable xmlSerializable && ProcessXmlSerializable) { if (typeNeeded) { writer.WriteAttributeString(XmlSerializer.AttributeType, GetTypeString(type)); } SerializeXmlSerializable(xmlSerializable, writer); return; } // c.) Using type converter of the type if applicable TypeConverter converter = TypeDescriptor.GetConverter(type); if (converter.CanConvertTo(Reflector.StringType) && converter.CanConvertFrom(Reflector.StringType)) { if (typeNeeded) { writer.WriteAttributeString(XmlSerializer.AttributeType, GetTypeString(type)); } // ReSharper disable once AssignNullToNotNullAttribute writer.WriteString(converter.ConvertToInvariantString(obj)); return; } var context = new SerializeObjectContext { Object = obj, Type = type, TypeNeeded = typeNeeded, Writer = writer, Visibility = visibility }; // d.) Key/Value if (TrySerializeKeyValue(ref context)) { return; } // e.) value type as binary only if enabled if (type.IsValueType && CompactSerializationOfStructures && BinarySerializer.TrySerializeValueType((ValueType)obj, out byte[] data))