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;
using Developpez.Dotnet.Reflection;
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);
#if NETFX_CORE
var properties = obj.GetType().GetTypeInfo().DeclaredProperties.Where(p => p.CanRead && p.GetMethod.IsPublic && !p.GetMethod.IsStatic);
#else
var properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
#endif
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);
var enumerable = obj as IEnumerable;
if (enumerable != null)
{
var xmlItems =
from object item in enumerable
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", false);
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)
{
#if NETFX_CORE
var ti = t.GetTypeInfo();
return ti.IsPrimitive
|| ti.IsEnum
#else
return t.IsPrimitive
|| t.IsEnum
#endif
|| 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, 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 NETFX_CORE
if (prop.PropertyType.GetTypeInfo().IsValueType)
#else
if (prop.PropertyType.IsValueType)
#endif
body = Expression.Convert(body, typeof(object));
Expression expr = Expression.Lambda(body, prm);
getter = expr.Compile();
_getters[prop] = getter;
}
return getter(obj);
}
}
}