60 votes

Puis-je sérialiser des types anonymes en xml ?

J'ai compris que les types anonymes sont marqués comme privés par le compilateur et que les propriétés sont en lecture seule. Y a-t-il un moyen de les sérialiser en XML (sans désérialisation) ? Ça fonctionne avec JSON, comment puis-je le faire avec XML ?

72voto

Matthew Whited Points 12255

Quelque chose comme ceci devrait vous aider à démarrer...

class Program
{
    static void Main(string[] args)
    {
        var moi = new
        {
            Bonjour = "Monde",
            Autre = new
            {
                Mon = "Objet",
                V = 1,
                B = (byte)2
            }
        };

        var x = moi.ToXml();
    }
}
public static class Tools
{
    private static readonly Type[] WriteTypes = new[] {
        typeof(string), typeof(DateTime), typeof(Enum), 
        typeof(decimal), typeof(Guid),
    };
    public static bool IsSimpleType(this Type type)
    {
        return type.IsPrimitive || WriteTypes.Contains(type);
    }
    public static XElement ToXml(this object input)
    {
        return input.ToXml(null);
    }
    public static XElement ToXml(this object input, string element)
    {
        if (input == null)
            return null;

        if (string.IsNullOrEmpty(element))
            element = "objet";
        element = XmlConvert.EncodeName(element);
        var ret = new XElement(element);

        if (input != null)
        {
            var type = input.GetType();
            var props = type.GetProperties();

            var elements = from prop in props
                           let name = XmlConvert.EncodeName(prop.Name)
                           let val = prop.GetValue(input, null)
                           let value = prop.PropertyType.IsSimpleType()
                                ? new XElement(name, val)
                                : val.ToXml(name)
                           where value != null
                           select value;

            ret.Add(elements);
        }

        return ret;
    }
}

... résultant xml ...

  Monde

    Objet
    1
    2

0 votes

Je suppose que Type.IsValueType pourrait être un raccourci intéressant pour la plupart des IsAssignableFrom. Ne capture pas le string cependant.

0 votes

IsValueType peut être un mauvais choix. Cela utiliserait la valeur ToString pour convertir le type. Mais j'ai apporté une modification qui devrait être beaucoup plus facile à comprendre.

3 votes

Salut - votre code fonctionne à merveille. J'ai apporté quelques modifications mineures pour qu'il prenne en charge les tableaux, j'en ai parlé ici: martinnormark.com/…

27voto

ricardo Points 681

Merci, excellent travail @Matthew et @Martin.

J'ai apporté quelques modifications pour prendre en compte les Nullables et les Enums. J'ai également modifié pour que les éléments du tableau soient nommés en fonction du nom de la propriété + index.

Voici le code si quelqu'un est intéressé

public static class ObjectExtensions {
    #region Champs Privés
    private static readonly Type[] WriteTypes = new[] {
        typeof(string), typeof(DateTime), typeof(Enum), 
        typeof(decimal), typeof(Guid),
    };
    #endregion Champs Privés
    #region .ToXml
    /// 
    /// Convertit un type anonyme en XElement.
    /// 
    /// L'entrée.
    /// Renvoie l'objet sous sa représentation XML dans un XElement.
    public static XElement ToXml(this object input) {
        return input.ToXml(null);
    }

    /// 
    /// Convertit un type anonyme en XElement.
    /// 
    /// L'entrée.
    /// Le nom de l'élément.
    /// Renvoie l'objet sous sa représentation XML dans un XElement.
    public static XElement ToXml(this object input, string element) {
        return _ToXml(input, element);
    }

    private static XElement _ToXml(object input, string element, int? arrayIndex = null, string arrayName = null) {
        if (input == null)
            return null;

        if (String.IsNullOrEmpty(element)) {
            string name = input.GetType().Name;
            element = name.Contains("AnonymousType") 
                ? "Object" 
                : arrayIndex != null
                    ? arrayName + "_" + arrayIndex
                    : name;
        }

        element = XmlConvert.EncodeName(element);
        var ret = new XElement(element);

        if (input != null) {
            var type = input.GetType();
            var props = type.GetProperties();

            var elements = props.Select(p => {
                var pType = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType;
                var name = XmlConvert.EncodeName(p.Name);
                var val = pType.IsArray ? "array" : p.GetValue(input, null);
                var value = pType.IsArray 
                    ? GetArrayElement(p, (Array)p.GetValue(input, null))
                    : pType.IsSimpleType() || pType.IsEnum 
                        ? new XElement(name, val) 
                        : val.ToXml(name);
                return value;
            })
            .Where(v=>v !=null);

            ret.Add(elements);
        }

        return ret;
    }

    #region aides
    /// 
    /// Obtient l'élément du tableau.
    /// 
    /// Les informations de la propriété.
    /// L'objet d'entrée.
    /// Renvoie un XElement avec la collection de tableau comme éléments enfants.
    private static XElement GetArrayElement(PropertyInfo info, Array input) {
        var name = XmlConvert.EncodeName(info.Name);

        XElement rootElement = new XElement(name);

        var arrayCount = input == null ? 0 : input.GetLength(0);

        for (int i = 0; i < arrayCount; i++) {
            var val = input.GetValue(i);
            XElement childElement = val.GetType().IsSimpleType() ? new XElement(name + "_" + i, val) : _ToXml(val, null, i, name);

            rootElement.Add(childElement);
        }

        return rootElement;
    }

    #region .IsSimpleType
    public static bool IsSimpleType(this Type type) {
        return type.IsPrimitive || WriteTypes.Contains(type);
    }
    #endregion .IsSimpleType

    #endregion aides
    #endregion .ToXml
}

0 votes

Voici un joli code (+1 déjà), mais il ne gère pas les Listes (Les Listes doivent être converties en tableaux, .ToArray(), ce qui n'est pas toujours possible).

27voto

John Saunders Points 118808

Il ne peut pas être accompli en utilisant XmlSerializer ni DataContractSerializer. Cela peut être fait par un code écrit manuellement, comme démontré ci-dessous (je ne peux pas dire si le code est assez complet pour gérer tous les types - mais c'est un très bon début).

4 votes

Cela ne signifie pas que vous ne pouvez pas le faire avec une classe tierce, cependant.

0 votes

Est-il possible d'écrire quelque chose de générique ?

0 votes

@Radu : Je ne sais pas ce que vous voulez dire par "écrire quelque chose de générique". Pas si vous parlez de l'utilisation du sérialiseur XML. La réponse de "Matthew Whited" vous montre comment faire cela sans utiliser le sérialiseur XML.

16voto

Pavel Points 135

Je sais que c'est un ancien post, mais ma solution convertit un type anonyme en XML en seulement 2 lignes de code.

Commencez par convertir votre type anonyme en JSON, puis de JSON en XML.

var jsonText = JsonConvert.SerializeObject(data);           // convertir en JSON
XmlDocument doc = JsonConvert.DeserializeXmlNode(jsonText); // convertir le JSON en document XML

Exemple

var data = new       // data - Type anonyme
{
    Request = new
    {
        OrderNumber = 123,
        Note = "Bonjour le monde"
    }
};

var jsonText = JsonConvert.SerializeObject(data);           
XmlDocument doc = JsonConvert.DeserializeXmlNode(jsonText);

Console.WriteLine(doc.OuterXml);                            

Sortie

    123
    Bonjour le monde

4voto

VdesmedT Points 6831

Ma propre version du travail excellent de @Matthew et @Martin : Les tableaux d'énumérations sont désormais pris en charge et la notion de tableaux est généralisée en IEnumerable afin de prendre également en charge toutes sortes de collections.

public static class ObjectExtensions {
/// 
/// Convertit un type anonyme en XElement.
/// 
/// L'entrée.
/// Renvoie l'objet sous sa forme XML dans un XElement.
public static XElement ToXml2(this object input) {
    return input.ToXml2(null);
}

/// 
/// Convertit un type anonyme en XElement.
/// 
/// L'entrée.
/// Le nom de l'élément.
/// Renvoie l'objet sous sa forme XML dans un XElement.
public static XElement ToXml2(this object input, string element) {
    return _ToXml(input, element);
}

private static XElement _ToXml(object input, string element, int? arrayIndex = null, string arrayName = null) {
    if (input == null)
        return null;

    if (String.IsNullOrEmpty(element)) {
        string name = input.GetType().Name;
        element = name.Contains("AnonymousType") 
            ? "Object" 
            : arrayIndex != null
                ? arrayName + "_" + arrayIndex
                : name;
    }

    element = XmlConvert.EncodeName(element);
    var ret = new XElement(element);

    if (input != null) {
        var type = input.GetType();
        var props = type.GetProperties();

        var elements = props.Select(p => {
            var pType = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType;
            var name = XmlConvert.EncodeName(p.Name);
            var val = pType.IsArray ? "array" : p.GetValue(input, null);
            var value = pType.IsEnumerable()
                ? GetEnumerableElements(p, (IEnumerable)p.GetValue(input, null))
                : pType.IsSimpleType2() || pType.IsEnum 
                    ? new XElement(name, val) 
                    : val.ToXml2(name);
            return value;
        })
        .Where(v=>v !=null);

        ret.Add(elements);
    }

    return ret;
}

#region helpers

private static XElement GetEnumerableElements(PropertyInfo info, IEnumerable input) {
    var name = XmlConvert.EncodeName(info.Name);

    XElement rootElement = new XElement(name);

    int i = 0;
    foreach(var v in input)
    {
        XElement childElement = v.GetType().IsSimpleType2() || v.GetType().IsEnum ? new XElement(name + "_" + i, v) : _ToXml(v, null, i, name);
        rootElement.Add(childElement);
        i++;
    }
    return rootElement;
}

private static readonly Type[] WriteTypes = new[] {
    typeof(string), typeof(DateTime), typeof(Enum), 
    typeof(decimal), typeof(Guid),
};
public static bool IsSimpleType2(this Type type) {
    return type.IsPrimitive || WriteTypes.Contains(type);
}

private static readonly Type[] FlatternTypes = new[] {
    typeof(string)
};
public static bool IsEnumerable(this Type type) {
    return typeof(IEnumerable).IsAssignableFrom(type) && !FlatternTypes.Contains(type);
}
#endregion
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X