我有以下匿名类型:
new {data1 = "test1", data2 = "sam", data3 = "bob"}
我需要一种可以接受此方法的方法,并在数组或字典中输出键值对。
我的目标是将其用作HttpRequest中的发布数据,因此我最终将连接到以下字符串中:
"data1=test1&data2=sam&data3=bob"
Answers:
这只需要一点点反思即可完成。
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);
var dict = a.GetType().GetProperties().ToDictionary(x => x.Name, x => x.GetValue(a, null));
ToArray()
,这很好。无论如何,它的响应完全适合SO而无需自动换行,因此我将其保持原样。
如果您使用的是.NET 3.5 SP1或.NET 4,则可以(滥用)RouteValueDictionary
此功能。它实现IDictionary<string, object>
并具有一个构造函数,该构造函数接受object
属性并将其转换为键值对。
这样,遍历键和值以构建查询字符串将变得很简单。
HtmlHelper
扩展方法中HTML属性的字典。)
他们在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
有一种将匿名对象转换为词典的内置方法:
HtmlHelper.AnonymousObjectToHtmlAttributes(yourObj)
它还返回RouteValueDictionary
。注意它是静态的
using Newtonsoft.Json;
var data = new {data1 = "test1", data2 = "sam", data3 = "bob"};
var encodedData = new FormUrlEncodedContent(JsonConvert.DeserializeObject<Dictionary<string, string>>(JsonConvert.SerializeObject(data))
@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的代码开始,然后在结果上循环,但这很整洁。
HtmlHelper.AnonymousObjectToHtmlAttributes
我做了这样的事情:
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));
}
}
在@GWB使用a的建议的基础上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都没有接受数组(我不确定甚至没有标准化的方法可以使用形式编码序列化它们),如果您需要支持他们,我会留给你们。
为时已晚,但是无论如何我都将其添加为更可靠的解决方案。我在这里看到的有一些问题(例如说DateTime不能正常工作)。因此,我建议先转换为json(Newtonsoft Json.Net):
var data = new {data1 = "test1", data2 = "sam", data3 = "bob"};
var result = string.Join("&",
JsonConvert.DeserializeObject<Dictionary<string, string>>(
JsonConvert.SerializeObject(data))
.Select(x => $"{x.Key}={x.Value}")
);