2010-08-14 14 views
65

Tengo el siguiente tipo anónimo:En C#, ¿se convierte el tipo anónimo en matriz de clave/valor?

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

I necesita un método que se llevará a esto en, y pares de valores clave de salida en una matriz o diccionario.

Mi objetivo es utilizar esto como datos de envío en un HttpRequest así que con el tiempo se concatena en en la siguiente cadena:

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

Respuesta

96

Esto toma sólo un poco de reflexión para lograr.

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

var dict = props.ToDictionary (x => x.Name, x => x.GetValue (a_source, null)) – Jordan

+18

Hemos llegado hasta aquí ... podemos hacer que sea un trazador de líneas: 'var dict = a .GetType(). GetProperties(). ToDictionary (x => x.Name, x => x.GetValue (a, null)); ' – kape123

+0

@ kape123, de hecho.De hecho, las últimas versiones de .NET ya no requieren la llamada a 'ToArray()', lo cual es bueno. En cualquier caso, la respuesta, tal como está, encaja muy bien en SO sin envoltura de palabras, así que lo dejo tal como está. – kbrimington

54

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

Sería trivial recorrer las claves y valores para construir su cadena de consulta.

+4

Digo "abuso" porque la clase se diseñó originalmente para el enrutamiento (o al menos su nombre y espacio de nombres lo implican). Sin embargo, no contiene ninguna funcionalidad específica de enrutamiento y ya se usa para otras características (como convertir objetos anónimos a diccionarios para atributos HTML en los métodos de extensión ASP.NET MVC 'HtmlHelper'. – GWB

+0

He hecho exactamente esto, pero ahora tengo que ir desde RouteValueDictionary al objeto anomous, cualquier idea: – Joshy

+0

Esto ni siquiera es un abuso de ella ... Microsoft lo hace. Por ejemplo, cuando llamas a HtmlHelper.TextBox ... se supone que pasas un tipo anónimo para establecer los valores de los atributos. Eso realmente causará un error vinculante en Razor (por ejemplo, intente llamar a @ Html.Partial ("~/Shared/_PartialControl.cshtml", new {id = "id", value = "value"}), y arrojará un error de enlace, incluso con el @model dinámico declarado en la vista parcial. El método TextBox llama internamente a "RouteValueDictionary AnonymousObjectToHtmlAttributes (objeto htmlAttributes) estático público", que está devolviendo un RouteValueDictionary. Así que ahí lo tienes. – Triynko

24

Aquí es cómo 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); 
      } 
     } 
    } 

fuente completo está aquí: http://pastebin.com/c1gQpBMG

+0

Intenté usar el código de pastebin y Visual Studio decía que muchos de los métodos del diccionario no se habían implementado. Tuve que hacer un lanzamiento explícito a IDictionary. Acabo de cambiar un par de "this._dictionary" por "((IDictionary ) this._dictionary)" –

1

@ solución de kbrimington hace que un método de extensión agradable - mi mi caso el retorno de una 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 usando RouteValueDictionary y haciendo un bucle en los resultados, pero esto es mucho más ordenado.

+5

Esto ya existe en el recuadro (al menos, ahora): 'HtmlHelper.AnonymousObjectToHtmlAttributes' – Andrew

1

he hecho algo como esto:

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

Sobre la base @ sugerencia de utilizar un RouteValueDictionary de masa de agua subterránea, escribí esta función recursiva para apoyar los tipos anónimos anidados, anteponiendo los parámetros anidados por las teclas de sus padres.

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

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

Esperanza esto ayuda a otra persona en una situación similar.

Editar: Como DrewG señaló, esto no es compatible con las matrices. Implementar adecuadamente el soporte para matrices anidadas arbitrariamente con tipos anónimos no sería trivial, y como ninguna de las API que he usado también acepta matrices (no estoy seguro de que haya incluso una forma estandarizada de serializarlas con codificación de formularios), Les dejaré eso a ustedes si necesitan apoyarlos.

+0

Cabezas, esto no maneja las matrices correctamente. Se acumulará desbordamiento. necesita algo como esto if (type.IsArray) { var arr = pair.Value como cadena []; if (arr! = Null) { foreach (var s in arr) { keyValuePairs.Add ((tecla + "[] =" + s) .Replace ("", "+")); } } } – DrewG

Cuestiones relacionadas