/// <summary> /// Convert an XElement to the assigned type using mappings provided by Mappable/Mapping attributes /// </summary> /// <typeparam name="T"></typeparam> /// <param name="element"></param> /// <returns></returns> public static bool TryDeserialize <T>(XElement element, T obj, DeserializeCaution caution = DeserializeCaution.CheckElementName, string rootMappableName = null) { Type type = obj.GetType(); Mappable mappable = type.GetCustomAttribute(typeof(Mappable)) as Mappable; if (mappable == null) { throw new MappableException("Object type is not Mappable"); } if (rootMappableName != null) { mappable.Name = rootMappableName; } if ((element.Name.LocalName != mappable.Name) && (caution == DeserializeCaution.CheckElementName)) { return(false); } var members = ((object[])type.GetFields()).Union(type.GetProperties()); var fields = type.GetFields().Where(f => f.GetCustomAttribute(typeof(Mapping)) != null); var properties = type.GetProperties().Where(p => p.GetCustomAttribute(typeof(Mapping)) != null); // Assign members foreach (var member in members.Where(m => GetCustomAttribute(m, typeof(Mapping)) != null)) { Mapping mapping = (Mapping)GetCustomAttribute(member, typeof(Mapping)); string value = element.Attribute(mapping.Name)?.Value; if (value == null) { if (mapping.Optional == false) { throw new MappableException($"Mapped property {mapping.Name} was not found in XElement '{element}'"); } } else { object convertedValue = null; try { convertedValue = ConvertIn(member, element.Attribute(mapping.Name)?.Value); } catch { convertedValue = default; } SetValue(member, obj, convertedValue); } } // get simple lists var lists = members.Where(m => GetCustomAttribute(m, typeof(ListMapping)) != null); foreach (var listMember in lists) { ListMapping listMapping = (ListMapping)GetCustomAttribute(listMember, typeof(ListMapping)); Mappable childMappable = (Mappable)listMapping.ChildType.GetCustomAttribute(typeof(Mappable)); if (childMappable == null) { throw new MappableException("Children of ListMappings should be Mappables"); } IList children = GetValue(listMember, obj) as IList; if (children == null) { throw new MappableException($"List '{listMapping.Name}' cannot be serialized: is not collection or is not initialized"); } XElement listElm = element.Descendants().FirstOrDefault(e => e.Name.LocalName == listMapping.Name); if (listElm == null) { if (!listMapping.Optional) { throw new MappableException("Required list is missing"); } else { continue; } } foreach (var child in listElm?.XPathSelectElements(childMappable.Name) ?? new List <XElement>()) { object newChil = Activator.CreateInstance(listMapping.ChildType); if (!TryDeserialize(child, newChil, caution)) { throw new MappableException($"Could not assign value from element {listElm}"); } children.Add(newChil); } } // assign ValueMapping var valueMapping = members.FirstOrDefault(m => GetCustomAttribute(m, typeof(ValueMapping)) != null); if (valueMapping != null) { SetValue(valueMapping, obj, ConvertIn(valueMapping, element.Value)); } // Assign children var childMembers = members.Where(m => GetCustomAttribute(m, typeof(ChildMapping)) != null); foreach (var member in childMembers) { ChildMapping mapping = (ChildMapping)GetCustomAttribute(member, typeof(ChildMapping)); if (mapping.ChildType == null) { // maps only one value object childObj = Activator.CreateInstance(MemberType(member)); // rename mapping if necessary bool overrideMappableName = false; Mappable childObjMappable = (Mappable)childObj.GetType().GetCustomAttribute(typeof(Mappable)); if (childObjMappable != null && childObjMappable.Name == null) { overrideMappableName = true; } XElement child = element.Descendants(mapping.Name).FirstOrDefault(); if (child == null) { if (!mapping.Optional) { return(false); } else { continue; } } if (!TryDeserialize(child, childObj, caution, rootMappableName: overrideMappableName?mapping.Name : null)) { return(false); } SetValue(member, obj, childObj); } else { // maps many values into a list Mappable childTypeMapping = (Mappable)mapping.ChildType.GetCustomAttribute(typeof(Mappable)); IList children = GetValue(member, obj) as IList; if (children == null) { throw new MappableException($"Object member {member} is not initilaized or is not a collection"); } IEnumerable <XElement> elmChildren = element.Descendants().Where(elm => elm.Name.LocalName == childTypeMapping.Name); foreach (var child in elmChildren) { object childObj = Activator.CreateInstance(mapping.ChildType); if (!TryDeserialize(child, childObj, caution)) { return(false); } children.Add(childObj); } } } // assign simple value children (non-Mappable children mapped to members of model) foreach (var member in members.Where(m => GetCustomAttribute(m, typeof(ValueChildMapping)) != null)) { ValueChildMapping mapping = (ValueChildMapping)GetCustomAttribute(member, typeof(ValueChildMapping)); XElement prop = element.Descendants(mapping.Name).FirstOrDefault(); if (prop == null) { if (!mapping.Optional) { throw new MappableException($"Missing required property '{mapping.Name}' from XElement '{prop}'"); } else { continue; } } SetValue(member, obj, ConvertIn(member, prop.Value)); } return(true); }
/// <summary> /// Serializes a class into an XElement using the Mappable/Mapping attributes. /// Only public properties may be serialized /// </summary> /// <param name="serializable"></param> /// <returns></returns> public static XElement Serialize(object serializable, string rootElementName = null) { if (serializable == null) { return(null); } Type t = serializable.GetType(); Attribute[] attrs = Attribute.GetCustomAttributes(t); Mappable l = (Mappable)attrs.FirstOrDefault(a => a is Mappable); if (l == null) { return(null); } if (rootElementName != null) { l.Name = rootElementName; } // make the element XElement elm = new XElement(l.Name); var members = ((object[])t.GetFields()).Union(t.GetProperties()); // assign the attributes foreach (var member in members.Where(m => GetCustomAttribute(m, typeof(Mapping)) != null)) { Mapping trait = (Mapping)GetCustomAttribute(member, typeof(Mapping)); MappingConversion conversion = (MappingConversion)GetCustomAttribute(member, typeof(MappingConversion)); object value = ConvertOut(member, serializable); if (value == null && trait.Optional) { continue; } XAttribute attr = new XAttribute(trait.Name, value ?? ""); elm.Add(attr); } // assign values to simple lists var lists = members.Where(m => GetCustomAttribute(m, typeof(ListMapping)) != null); foreach (var listMember in lists) { ListMapping listMapping = (ListMapping)GetCustomAttribute(listMember, typeof(ListMapping)); IList children = GetValue(listMember, serializable) as IList; if (children == null) { throw new MappableException($"List '{listMapping.Name}' cannot be serialized: is not collection or is null"); } XElement listElm = new XElement(listMapping.Name); foreach (var child in children) { listElm.Add(Serialize(child)); } elm.Add(listElm); } // assign a value to the XElement var valueMapping = members.FirstOrDefault(m => GetCustomAttribute(m, typeof(ValueMapping)) != null); if (valueMapping != null) { string val = ConvertOut(valueMapping, serializable); if (val != null) { elm.Value = val; } } // assign the children foreach (var member in members.Where(m => GetCustomAttribute(m, typeof(ChildMapping)) != null)) { ChildMapping mapping = (ChildMapping)GetCustomAttribute(member, typeof(ChildMapping)); if (mapping.ChildType == null) { Mappable childMappable = (Mappable)MemberType(member).GetCustomAttribute(typeof(Mappable)); // maps only one value XElement childElm = Serialize(GetValue(member, serializable), rootElementName: childMappable.Name == null ? mapping.Name : null); if (childElm != null) { elm.Add(childElm); } } else { // mapped to many values IList children = GetValue(member, serializable) as IList; if (children == null) { return(null); } foreach (var child in children) { XElement childElm = Serialize(child); elm.Add(childElm); } } } // assign simple value children (non-Mappable children mapped to members of model) foreach (var member in members.Where(m => GetCustomAttribute(m, typeof(ValueChildMapping)) != null)) { ValueChildMapping mapping = (ValueChildMapping)GetCustomAttribute(member, typeof(ValueChildMapping)); XElement simpleChild = new XElement(mapping.Name); string val = ConvertOut(member, serializable); if (val != null) { simpleChild.Value = val; elm.Add(simpleChild); } } return(elm); }