using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Xml; using System.Xml.Linq; namespace Developpez.Dotnet.Xml { /// /// Fournit des méthodes pour convertir en XML n'importe quel objet, même anonyme /// public static class XmlDumper { /// /// Renvoie un élément XML avec le nom spécifié représentant l'objet passé en paramètre /// ///L'objet à convertir en XML ///Nom de l'élément XML racine ///Un élément XML représentant l'objet passé en paramètre /// Les propriétés de type primitif sont représentées par des attributs, celles de type complexe par des sous-éléments. /// Limitations: /// /// /// Seules les propriétés publiques sont prises en compte /// /// /// Les cycles ne sont pas détectés, donc si l'objet à convertir comporte des cycles, il se produira une StackOverflowException /// /// /// Les propriétés de type Delegate ne sont pas prises en compte /// /// /// public static XElement ToXml(object obj, string elementName) { if (obj == null || obj is Delegate) return null; if (IsSimpleType(obj.GetType())) return new XElement(elementName, obj); if (obj is IEnumerable) return (XElement)GetXmlObject(obj, elementName); var properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); var xmlProperties = from p in properties let name = XmlConvert.EncodeName(p.Name) let value = GetPropertyValue(obj, p) let xmlProperty = GetXmlObject(value, name) where xmlProperty != null select xmlProperty; return new XElement(elementName, xmlProperties); } /// /// Renvoie un élément XML représentant l'objet passé en paramètre /// ///L'objet à convertir en XML ///Un élément XML représentant l'objet passé en paramètre /// Les propriétés de type primitif sont représentées par des attributs, celles de type complexe par des sous-éléments. /// Limitations: /// /// /// Seules les propriétés publiques sont prises en compte /// /// /// Les cycles ne sont pas détectés, donc si l'objet à convertir comporte des cycles, il se produira une StackOverflowException /// /// /// Les propriétés de type Delegate ne sont pas prises en compte /// /// /// public static XElement ToXml(object obj) { if (obj == null) return null; return ToXml(obj, GetXmlTypeName(obj.GetType())); } private static XObject GetXmlObject(object obj, string name) { if (obj == null || obj is Delegate) return null; if (IsSimpleType(obj.GetType())) return new XAttribute(name, obj); if (obj is IEnumerable) { var xmlItems = from object item in (IEnumerable)obj select item == null ? new XElement("Null") : ToXml(item, GetXmlTypeName(item.GetType())); return new XElement(name, xmlItems); } return ToXml(obj, name); } private static string GetXmlTypeName(Type type) { if (type.Name.StartsWith("<>")) // Anonymous type return "Object"; if (!IsSimpleType(type) && typeof(IEnumerable).IsAssignableFrom(type)) { Type genericEnumerable = type.GetInterface("IEnumerable`1"); if (genericEnumerable != null) { Type itemType = genericEnumerable.GetGenericArguments()[0]; return "ArrayOf" + GetXmlTypeName(itemType); } return "ArrayOfObject"; } string baseName = type.Name.From(0).To("`", false); return XmlConvert.EncodeName(baseName); } private static bool IsSimpleType(Type t) { return t.IsPrimitive || t.IsEnum || t == typeof(string) || t == typeof(decimal) || t == typeof(Guid) || t == typeof(DateTime); } private delegate object Getter(object instance); private static readonly Dictionary _getters = new Dictionary(); private static object GetPropertyValue(object obj, PropertyInfo prop) { Getter getter; if (!_getters.TryGetValue(prop.MetadataToken, out getter)) { Type objType = obj.GetType(); ParameterExpression prm = Expression.Parameter(typeof(object), "obj"); Expression body = Expression.Property( Expression.Convert(prm, objType), prop); // Box value types if (prop.PropertyType.IsValueType) body = Expression.Convert(body, typeof(object)); Expression expr = Expression.Lambda(body, prm); getter = expr.Compile(); _getters[prop.MetadataToken] = getter; } return getter(obj); } } }