44 votes

En C#, comment sérialiser à sens unique ce qui n'est pas sérialisable ?

Il m'arrive souvent de devoir sérialiser un objet, que ce soit pour la journalisation ou le débogage. Il s'agit d'une sérialisation à sens unique - je n'ai pas besoin de le récupérer plus tard, j'ai juste besoin de transformer un objet en une chaîne pour l'écrire quelque part.

Oui, oui c'est pourquoi vous devez toujours remplacer l'attribut ToString méthode. Je le sais. Mais j'ai souvent affaire à des objets que je n'ai pas écrits et que je ne peux pas modifier. De plus, je ne veux pas avoir à écrire et mettre à jour une méthode ToString pour chaque classe que j'écris.

La sérialisation XML offre une solution apparemment parfaite : il suffit d'aplatir cet objet en XML. Mais il y a tellement de limitations, en particulier que vous ne pouvez pas sérialiser IDictionary, et vous devez avoir un constructeur sans paramètre. Je peux les contourner dans mes classes, mais -- encore une fois -- je travaille souvent avec les classes d'autres personnes.

Alors, quelle est la solution pour obtenir une représentation complète d'un objet sous forme de chaîne ? Y a-t-il quelque chose de simple qui m'échappe ?

38voto

Brad Christie Points 58505

Que diriez-vous d'une méthode d'extension avec votre propre logique (et peut-être un peu de Reflection) ?

public static class SerializerExtension
{
    public static String OneWaySerialize(this Object obj)
    {
        if (Object.ReferenceEquals(obj, null))
        {
            return "NULL";
        }
        if (obj.GetType().IsPrimitive || obj.GetType() == typeof(String))
        {
            if (obj is String)
                return String.Format("\"{0}\"", obj);
            if (obj is Char)
                return String.Format("'{0}'", obj);
            return obj.ToString();
        }

        StringBuilder builder = new StringBuilder();
        Type objType = obj.GetType();
        if (IsEnumerableType(objType))
        {
            builder.Append("[");

            IEnumerator enumerator = ((IEnumerable)obj).GetEnumerator();
            Boolean moreElements = enumerator.MoveNext();
            while (moreElements)
            {
                builder.Append(enumerator.Current.OneWaySerialize());
                moreElements = enumerator.MoveNext();
                if (moreElements)
                {
                    builder.Append(",");
                }
            }

            builder.Append("]");
        }
        else
        {
            builder.AppendFormat("{0} {{ ", IsAnonymousType(objType) ? "new" : objType.Name);

            PropertyInfo[] properties = objType.GetProperties();
            for (Int32 p = properties.Length; p > 0; p--)
            {
                PropertyInfo prop = properties[p-1];
                String propName = prop.Name;
                Object propValue = prop.GetValue(obj, null);
                builder.AppendFormat("{0} = {1}", propName, propValue.OneWaySerialize());
                if (p > 1)
                {
                    builder.Append(", ");
                }
            }

            builder.Append(" }");
        }

        return builder.ToString();
    }

    // http://stackoverflow.com/a/2483054/298053
    private static Boolean IsAnonymousType(Type type)
    {
        if (type == null)
        {
            return false;
        }
        return Attribute.IsDefined(type, typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false)
            && type.IsGenericType && type.Name.Contains("AnonymousType")
            && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
            && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
    }

    private static Boolean IsEnumerableType(Type type)
    {
        if (type == null)
        {
            return false;
        }
        foreach (Type intType in type.GetInterfaces())
        {
            if (intType.GetInterface("IEnumerable") != null || (intType.IsGenericType && intType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
            {
                return true;
            }
        }
        return false;
    }
}

Appelez ça comme ça :

someDefinedObject.OneWaySerialize();

Révisions

  1. Version initiale
  2. Mis à jour le 26.12.2012
    • Ajout d'une vérification pour IEnumerable (merci aboveyou00)
    • Ajout d'une vérification pour le type anonyme (et juste l'étiqueter "nouveau" lors de la sortie)

14voto

David Peden Points 3532

Si vous êtes à l'aise avec la sérialisation en JSON, Json.NET est une excellente solution à ce problème.

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