2010-08-14 2 views
65

У меня есть следующий анонимный тип:В C# конвертировать анонимный тип в массив ключей/значений?

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

Мне нужен метод, который будет принимать это, и выходные пары ключ-значение в массиве или словаре.

Моя цель состоит в том, чтобы использовать это в качестве почтовых данных в HttpRequest так я в конечном итоге сцепить в в следующей строке:

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

ответ

96

Это займет всего чуть-чуть отражения, чтобы достигнуть.

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

вар ДИКТ = props.ToDictionary (х => x.Name, х => x.GetValue (a_source, нуль)) – Jordan

+18

Мы получили это далеко ... мы можем сделать его один вкладыш: 'уаг dict = a.GetType(). GetProperties().ToDictionary (x => x.Name, x => x.GetValue (a, null)); ' – kape123

+0

@ kape123, действительно. На самом деле, последние версии .NET больше не требуют вызова 'ToArray()', что приятно. Во всяком случае, ответ, который он стоит, прекрасно вписывается в SO без переноса слов, поэтому я оставлю его как есть. – kbrimington

54

Если вы используете .NET 3.5 SP1 или .NET 4, вы можете (аb) использовать RouteValueDictionary для этого. Он реализует IDictionary<string, object> и имеет конструктор, который принимает object и преобразует свойства в пары значений ключа.

Тогда было бы тривиально прокручивать ключи и значения для построения строки запроса.

+4

Я говорю «злоупотреблять», потому что класс изначально был предназначен для маршрутизации (или, по крайней мере, это имя и пространство имен подразумевают это). Однако он не содержит функций, специфичных для маршрутизации, и уже используется для других функций (например, для преобразования анонимных объектов в словари для атрибутов HTML в методах расширения ASP.NET MVC 'HtmlHelper'. – GWB

+0

Я сделал именно это, но теперь мне нужно идти от RouteValueDictionary до аномального объекта, любые мысли? – Joshy

+0

Это даже не злоупотребление им .. Microsoft делает это. Например, когда вы вызываете HtmlHelper.TextBox ... вы должны передавать анонимный тип для установки значений атрибутов, что фактически вызовет ошибку привязки в Razor (например, попробуйте вызвать @ Html.Partial ("~/Shared/_PartialControl.cshtml", new {id = "id", value = "value"}) и он вызовет ошибку привязки даже при динамическом динамике @model, объявленном в частичном представлении. Метод TextBox внутренне вызывает «public static RouteValueDictionary AnonymousObjectToHtmlAttributes (object htmlAttributes)», который возвращает RouteValueDictionary. Итак, вы идете. – Triynko

24

Вот как они это делают в 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); 
      } 
     } 
    } 

Полный Источник здесь: http://pastebin.com/c1gQpBMG

+0

Я попытался использовать код из pastebin, и Visual Studio говорила, что куча методов Dictionary не была реализована. Мне пришлось сделать явный приведение к IDictionary. Я просто переключил пару «this._dictionary» на «((IDictionary ) this._dictionary)» –

1

@ решение kbrimington делает хороший метод расширения - мое мое дело возвращающая 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)); 
    } 

Я использую его для удаления произвольных атрибутов в виде Razor MVC. Я начал с кода, используя RouteValueDictionary, и зациклился на результатах, но это намного опрятно.

+5

Это уже существует в поле (по крайней мере, сейчас): 'HtmlHelper.AnonymousObjectToHtmlAttributes' – Andrew

1

я сделал что-то вроде этого:

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

здания на @ предложение GWB о использовании RouteValueDictionary, я написал эту рекурсивную функцию для поддержки вложенных анонимных типов, предваряя эти вложенные параметры с помощью клавиш их родителей.

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

Пример использование:

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 становится:

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

Надеется, что это помогает кто-то в подобной ситуации.

Редактировать: Как указал DrewG, это не поддерживает массивы. Правильно реализовать поддержку произвольно вложенных массивов с анонимными типами было бы нетривиальным, и поскольку ни один из API-интерфейсов, которые я использовал, не принял также массивы (я не уверен, что существует даже стандартизованный способ сериализации их с кодировкой формы) Я оставлю это вам, если вам нужно их поддержать.

+0

Заголовки, это неправильно обрабатывает массивы. Будет переполнение стека. нужно что-то вроде этого if (type.IsArray) { var arr = pair.Value as string []; если (arr! = Null) { foreach (var s in arr) { keyValuePairs.Add ((ключ + "[] =" + s) .Заменить ("", "+")); } } } – DrewG

Смежные вопросы