using System; using System.Xml.Linq; namespace OpenTap.Plugins { class MixinSerializer : TapSerializerPlugin { static readonly XName rootElemName = "OpenTap.Mixins"; private static readonly XName mixinElemName = "Mixin"; public override double Order => 1.1; public override bool Deserialize(XElement node, ITypeData t, Action setter) { var rootElem = node.Element(rootElemName); if (rootElem != null) { var elements = rootElem.Elements(); void setter2(object targetObject) { foreach (var elem in elements) { Serializer.Deserialize(elem, mixin => { var mixin2 = (IMixinBuilder) mixin; MixinFactory.LoadMixin(targetObject, mixin2); }, TypeData.FromType(typeof(IMixinBuilder))); } setter(targetObject); } rootElem.Remove(); return Serializer.Deserialize(node, setter2, t); } return false; } public override bool Serialize(XElement node, object obj, ITypeData expectedType) { if (obj == null) return false; var dynamicMembers = DynamicMember.GetDynamicMembers(obj); XElement mixins = null; foreach (var dynamicMember in dynamicMembers) { if (dynamicMember is MixinMemberData mixin) { if (mixins == null) { mixins = node.Element(rootElemName); if (mixins == null) { mixins = new XElement(rootElemName); node.Add(mixins); } } var encodedName =Serializer.PropertyXmlName(dynamicMember.Name); var currentElement = mixins.Element(encodedName); // in some rare case this already happened once. This happens because we return false below. // In this case it is safe to ignore it and continue. if (currentElement == null) { XElement elem = new XElement(encodedName); mixins.Add(elem); Serializer.Serialize(elem, mixin.Source); } } } // the mixin members has been serialized now, but // continue serializing as before. return false; } } }