web-dev-qa-db-fra.com

En c # convertit le type anonyme en tableau clé/valeur?

J'ai le type anonyme suivant:

new {data1 = "test1", data2 = "sam", data3 = "bob"}

J'ai besoin d'une méthode qui prendra cela en compte et produira des paires clé-valeur dans un tableau ou un dictionnaire. 

Mon objectif est d'utiliser ceci comme données de publication dans une requête HttpRequest afin que je puisse éventuellement concaténer dans la chaîne suivante:

"data1=test1&data2=sam&data3=bob"
72
Chris Kooken

Cela prend juste un petit peu de réflexion à accomplir.

var a = new { data1 = "test1", data2 = "sam", data3 = "bob" };
var type = a.GetType();
var props = type.GetProperties();
var pairs = props.Select(x => x.Name + "=" + x.GetValue(a, null)).ToArray();
var result = string.Join("&", pairs);
107
kbrimington

Si vous utilisez .NET 3.5 SP1 ou .NET 4, vous pouvez (ab) utiliser RouteValueDictionary pour cela. Il implémente IDictionary<string, object> et a un constructeur qui accepte object et convertit les propriétés en paires clé-valeur.

Il serait alors facile de parcourir les clés et les valeurs pour créer votre chaîne de requête.

61
GWB

Voici comment ils le font dans RouteValueDictionary:

  private void AddValues(object values)
    {
        if (values != null)
        {
            foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
            {
                object obj2 = descriptor.GetValue(values);
                this.Add(descriptor.Name, obj2);
            }
        }
    }

La source complète est ici: http://Pastebin.com/c1gQpBMG

25
John

J'ai fait quelque chose comme ça:

public class ObjectDictionary : Dictionary<string, object>
{
    /// <summary>
    /// Construct.
    /// </summary>
    /// <param name="a_source">Source object.</param>
    public ObjectDictionary(object a_source)
        : base(ParseObject(a_source))
    {

    }

    /// <summary>
    /// Create a dictionary from the given object (<paramref name="a_source"/>).
    /// </summary>
    /// <param name="a_source">Source object.</param>
    /// <returns>Created dictionary.</returns>
    /// <exception cref="ArgumentNullException">Thrown if <paramref name="a_source"/> is null.</exception>
    private static IDictionary<String, Object> ParseObject(object a_source)
    {
        #region Argument Validation

        if (a_source == null)
            throw new ArgumentNullException("a_source");

        #endregion

        var type = a_source.GetType();
        var props = type.GetProperties();

        return props.ToDictionary(x => x.Name, x => x.GetValue(a_source, null));
    }
}
1
Jordan

En me basant sur la suggestion de @ GWB d'utiliser une RouteValueDictionary, j'ai écrit cette fonction récursive pour prendre en charge les types anonymes imbriqués, en préfixant ces paramètres imbriqués avec les clés de leurs parents. 

public static string EncodeHtmlRequestBody(object data, string parent = null) {
    var keyValuePairs = new List<string>();
    var dict = new RouteValueDictionary(data);

    foreach (var pair in dict) {
        string key = parent == null ? pair.Key : parent + "." + pair.Key;
        var type = pair.Value.GetType();
        if (type.IsPrimitive || type == typeof(decimal) || type == typeof(string)) {
            keyValuePairs.Add(key + "=" + Uri.EscapeDataString((string)pair.Value).Replace("%20", "+"));
        } else {
            keyValuePairs.Add(EncodeHtmlRequestBody(pair.Value, key));
        }
    }

    return String.Join("&", keyValuePairs);
}

Exemple d'utilisation:

var data = new {
    apiOperation = "AUTHORIZE",
    order = new {
        id = "order123",
        amount = "101.00",
        currency = "AUD"
    },
    transaction = new {
        id = "transaction123"
    },
    sourceOfFunds = new {
        type = "CARD",
        provided = new {
            card = new {
                expiry = new {
                    month = "1",
                    year = "20"
                },
                nameOnCard = "John Smith",
                number = "4444333322221111",
                securityCode = "123"
            }
        }
    }
};

string encodedData = EncodeHtmlRequestBody(data);

encodedData devient:

"apiOperation=AUTHORIZE&order.id=order123&order.amount=101.00&order.currency=AUD&transaction.id=transaction123&sourceOfFunds.type=CARD&sourceOfFunds.provided.card.expiry.month=1&sourceOfFunds.provided.card.expiry.year=20&sourceOfFunds.provided.card.nameOnCard=John+Smith&sourceOfFunds.provided.card.number=4444333322221111&sourceOfFunds.provided.card.securityCode=123"

J'espère que cela aide quelqu'un d'autre dans une situation similaire. 

Edit: Comme DrewG l'a fait remarquer, cela ne supporte pas les tableaux. Implémenter correctement la prise en charge des tableaux imbriqués arbitrairement avec des types anonymes ne serait pas trivial, et aucune des API que j'ai utilisées n'a accepté les tableaux (je ne suis pas sûr qu'il existe même un moyen standard de les sérialiser avec le codage de formulaire) Je vais laisser cela à vous, si vous avez besoin de les soutenir.

1
Extragorey

La solution de @ kbrimington fait une méthode d'extension Nice - mon cas me renvoyant un HtmlString

    public static System.Web.HtmlString ToHTMLAttributeString(this Object attributes)
    {
        var props = attributes.GetType().GetProperties();
        var pairs = props.Select(x => string.Format(@"{0}=""{1}""",x.Name,x.GetValue(attributes, null))).ToArray();
        return new HtmlString(string.Join(" ", pairs));
    }

Je l'utilise pour déposer des attributs arbitraires dans une vue Razor MVC. J'ai commencé avec du code en utilisant RouteValueDictionary et en boucle sur les résultats, mais c'est beaucoup plus net.

1
Andiih
using Newtonsoft.Json;
var data = new {data1 = "test1", data2 = "sam", data3 = "bob"};
var encodedData = new FormUrlEncodedContent(JsonConvert.DeserializeObject<Dictionary<string, string>>(JsonConvert.SerializeObject(data))

Il existe une méthode intégrée pour convertir les objets anonymes en dictionnaires:

HtmlHelper.AnonymousObjectToHtmlAttributes(yourObj)

Il retourne également RouteValueDictionary. Notez que c'est statique

0
Xymanek