/// <summary> /// Returns a collection with infos about all fields and properties of the type that can be (xml-)serialized /// </summary> /// <param name="type">Type to get information about</param> /// <returns></returns> public static IEnumerable <SFieldInfo> GetFieldInfos(this Type type) { List <SFieldInfo> result; if (_CacheFields.TryGetValue(type, out result)) { return(result); } result = new List <SFieldInfo>(); FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo field in fields) { if (field.HasAttribute <XmlIgnoreAttribute>() || field.Name.EndsWith("Specified")) { continue; } SFieldInfo info = new SFieldInfo(field); _FillInfo(ref info, field); result.Add(info); } PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo property in properties) { if (property.HasAttribute <XmlIgnoreAttribute>() || property.Name.EndsWith("Specified") || !property.CanRead || !property.CanWrite) { continue; } SFieldInfo info = new SFieldInfo(property); _FillInfo(ref info, property); result.Add(info); } _CacheFields.Add(type, result); return(result); }
/// <summary> /// Sets the field to its default value if it has one (specified as DefaultValueAttribute or an empty collection if it is an embedded one) /// </summary> /// <param name="result"></param> /// <param name="field"></param> /// <returns>True if default value was set</returns> private static bool _CheckAndSetDefaultValue(object result, SFieldInfo field) { if (field.IsEmbeddedList) { _AddList(result, field, new List <object>()); return(true); } if (!field.HasDefaultValue) { return(false); } field.SetValue(result, field.DefaultValue); return(true); }
/// <summary> /// Reads childnodes of the given node (either nodes or attributes) /// </summary> /// <param name="parent">Node to process</param> /// <param name="o">Object to put the values in</param> /// <param name="attributes">True for processing attributes, false for nodes</param> private void _ReadChildNodes(XmlNode parent, object o, bool attributes) { IEnumerable nodes; if (parent == null) { nodes = null; } else if (attributes) { nodes = parent.Attributes; } else { nodes = parent.ChildNodes; } List <SFieldInfo> fields = o.GetType().GetFields(attributes); if (nodes != null) { //Dictionary of all embedded lists to allow interleaved/mixed elements Dictionary <string, Tuple <SFieldInfo, List <object> > > embLists = new Dictionary <string, Tuple <SFieldInfo, List <object> > >(); foreach (XmlNode node in nodes) { if (node is XmlComment || node.LocalName == "xsd" || node.LocalName == "xsi") { continue; } SFieldInfo field = new SFieldInfo(); int curField; for (curField = 0; curField < fields.Count; curField++) { field = fields[curField]; if (field.Name == node.Name || field.AltName == node.Name || (field.IsEmbeddedList && node.Name.StartsWith(field.Name))) { break; } } if (curField >= fields.Count) { string msg = "Unexpected element: %n"; if (fields.Count > 0) { msg += " Expected: " + string.Join(", ", fields.Select(f => f.Name)); } _ErrorHandler.HandleError(new CXmlException(msg, node)); continue; } if (field.IsByteArray) { fields.RemoveAt(curField); //Do this also on error byte[] value; try { value = Convert.FromBase64String(node.InnerText); } catch (Exception) { _ErrorHandler.HandleError(new CXmlInvalidValueException("Invalid value in %n: %v", node, node.InnerText)); if (!_CheckAndSetDefaultValue(o, field)) { _ErrorHandler.HandleError(new CXmlException("No default value for %n", node)); } continue; } field.SetValue(o, value); } else if (field.IsEmbeddedList) { Tuple <SFieldInfo, List <object> > entry; if (!embLists.TryGetValue(field.Name, out entry)) { entry = new Tuple <SFieldInfo, List <object> >(field, new List <object>()); embLists.Add(field.Name, entry); } object subValue = _GetValue(node, field.SubType); if (subValue != null) { entry.Item2.Add(subValue); } } else { fields.RemoveAt(curField); //Do this also on error object value = _GetValue(node, field.Type, field.ArrayItemName); if (value == null) { if (_CheckAndSetDefaultValue(o, field)) { continue; } if (!field.IsNullable) { _ErrorHandler.HandleError(new CXmlException("No default value for unset field at %n", node)); continue; } } if (field.Ranged != null && value != null && !field.Ranged.IsValid(field.IsNullable ? field.SubType : field.Type, value)) { _ErrorHandler.HandleError(new CXmlInvalidValueException("Value in %n is not in the range " + field.Ranged + ". (Value=%v)", node, value.ToString())); } field.SetValue(o, value); } } //Add embedded lists foreach (Tuple <SFieldInfo, List <object> > entry in embLists.Values) { _AddList(o, entry.Item1, entry.Item2); fields.Remove(entry.Item1); } } foreach (SFieldInfo field in fields) { if (!_CheckAndSetDefaultValue(o, field)) { if (parent != null) { _ErrorHandler.HandleError(new CXmlMissingElementException(parent, field)); } object value = _GetValue(null, field.Type, field.ArrayItemName, field.GetValue(o)); if (value != null) { field.SetValue(o, value); } } } }
/// <summary> /// Creates a collection (array or list) and puts it into the specified field /// </summary> /// <param name="o">Object which listField belongs to</param> /// <param name="listField">Field that will contain the list</param> /// <param name="values">Collection of values that are used to fill the list</param> private static void _AddList(object o, SFieldInfo listField, ICollection values) { listField.SetValue(o, _CreateList(listField.Type, values)); }
public CXmlMissingElementException(XmlNode parent, SFieldInfo field, bool isError = true) : base("Element: " + field.Name + " is missing in %n", parent, isError) { Field = field; }
/// <summary> /// Fills the struct with information about the field /// </summary> /// <param name="info">Struct to fill</param> /// <param name="field">Field descrived by the struct</param> private static void _FillInfo(ref SFieldInfo info, MemberInfo field) { XmlAttributeAttribute attribute = field.GetAttribute <XmlAttributeAttribute>(); if (attribute != null) { info.IsAttribute = true; info.Name = attribute.AttributeName; } else { XmlElementAttribute element = field.GetAttribute <XmlElementAttribute>(); if (element != null) { info.Name = element.ElementName; } else { XmlArrayAttribute array = field.GetAttribute <XmlArrayAttribute>(); if (array != null) { Debug.Assert(info.Type.IsList() || info.Type.IsArray, "Only lists and arrays can have the array attribute"); Debug.Assert(!info.IsAttribute, "Lists cannot be attributes"); info.Name = array.ElementName; info.IsList = true; } } } if (string.IsNullOrEmpty(info.Name)) { info.Name = field.Name; } XmlAltNameAttribute altName = field.GetAttribute <XmlAltNameAttribute>(); if (altName != null) { info.AltName = altName.AltName; } if (info.Type.IsGenericType) { info.SubType = info.Type.GetGenericArguments().Last(); } else if (info.Type.IsArray) { info.SubType = info.Type.GetElementType(); } if (info.Type.IsList() || info.Type.IsArray) { if (!info.IsList) { Debug.Assert(!field.HasAttribute <XmlArrayAttribute>(), "A field cannot have an XmlElement- and XmlArray-Attribute"); if (info.Type.IsArray && info.SubType == typeof(byte)) { info.IsByteArray = true; } else { info.IsEmbeddedList = true; } } else { XmlArrayItemAttribute arrayItem = field.GetAttribute <XmlArrayItemAttribute>(); if (arrayItem != null && !string.IsNullOrEmpty(arrayItem.ElementName)) { info.ArrayItemName = arrayItem.ElementName; } } } else if (info.Type.IsNullable()) { info.IsNullable = true; } else if (info.Type.IsDictionary()) { Debug.Assert(info.Type.GetGenericArguments()[0] == typeof(string), "Keys of dictionaries must be strings"); info.IsDictionary = true; } if (field.HasAttribute <XmlNormalizedAttribute>()) { Debug.Assert(info.Type == typeof(float) || (info.IsNullable && info.SubType == typeof(float)), "Only floats can be normalized"); } info.Ranged = field.GetAttribute <XmlRangedAttribute>(); Type tmpType = info.IsNullable ? info.SubType : info.Type; Debug.Assert(info.Ranged == null || tmpType == typeof(int) || tmpType == typeof(float) || tmpType == typeof(double), "Only ints,floats and double can be ranged"); DefaultValueAttribute defAttr = field.GetAttribute <DefaultValueAttribute>(); if (defAttr != null) { Debug.Assert(!info.Type.IsList(), "Lists cannot have a default value"); info.HasDefaultValue = true; info.DefaultValue = defAttr.Value; } else if (info.IsNullable) { info.HasDefaultValue = true; info.DefaultValue = null; } }