¿En c # convertir el tipo anónimo en matriz clave / valor?

Tengo el siguiente tipo anónimo:

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

Necesito un método que tome esto, y genere pares de valores clave en una matriz o diccionario.

Mi objective es usar esto como datos de publicación en una HttpRequest para que finalmente concatene en la siguiente cadena:

 "data1=test1&data2=sam&data3=bob" 

Esto requiere solo un poquito de reflexión para lograrlo.

 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); 

Si está utilizando .NET 3.5 SP1 o .NET 4, puede (ab) usar RouteValueDictionary para esto. Implementa IDictionary y tiene un constructor que acepta object y convierte las propiedades en pares clave-valor.

Entonces sería trivial recorrer las claves y los valores para construir su cadena de consulta.

Así es como lo hacen en 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 fuente completa está aquí: http://pastebin.com/c1gQpBMG

La solución de @kbrimington es un buen método de extensión: mi caso devuelve 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)); } 

Lo estoy usando para colocar atributos arbitrarios en una vista de Razor MVC. Comencé con el código utilizando RouteValueDictionary y repitiendo los resultados, pero esto es mucho mejor.

Hice algo como esto:

 public class ObjectDictionary : Dictionary { ///  /// Construct. ///  /// Source object. public ObjectDictionary(object a_source) : base(ParseObject(a_source)) { } ///  /// Create a dictionary from the given object (). ///  /// Source object. /// Created dictionary. /// Thrown if  is null. private static IDictionary 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)); } } 

Sobre la base de la sugerencia de @ GWB de usar un RouteValueDictionary , escribí esta función recursiva para admitir tipos anónimos nesteds, prefijando esos parámetros nesteds con las claves de sus padres.

 public static string EncodeHtmlRequestBody(object data, string parent = null) { var keyValuePairs = new List(); 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); } 

Ejemplo de uso:

 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 convierte en:

"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"

Espero que esto ayude a alguien más en una situación similar.

Edición: como señaló DrewG, esto no es compatible con matrices. Implementar adecuadamente el soporte para arrays nesteds arbitrariamente con tipos anónimos no sería trivial, y como ninguna de las API que he usado tampoco han aceptado arrays (no estoy seguro de que haya una forma estandarizada de serializarlos con encoding de formularios), Les dejo eso a ustedes, amigos, si necesitan apoyarlos.

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

Existe un método incorporado para convertir objetos anónimos a diccionarios:

 HtmlHelper.AnonymousObjectToHtmlAttributes(yourObj) 

También devuelve RouteValueDictionary . Tenga en cuenta que es estático