Преобразовать запрос с/на объект
У меня есть этот (упрощенный) класс:
public class StarBuildParams
{
public int BaseNo { get; set; }
public int Width { get; set; }
}
И мне нужно преобразовать экземпляры его в querystring следующим образом:
"BaseNo=5&Width=100"
Кроме того, мне нужно преобразовать такой запрос в объект этого класса.
Я знаю, что это в значительной степени то, что делает modelbinder, но у меня нет контекста контроллера в моей ситуации (некоторый глубокий скрытый класс работает в потоке).
Итак, есть ли простой способ конвертировать объект в строку запроса и обратно без контекста контроллера?
Было бы здорово использовать привязку к модели, но я не знаю, как это сделать.
Ответы
Ответ 1
Вы можете использовать отражение, что-то вроде этого:
public T GetFromQueryString<T>() where T : new(){
var obj = new T();
var properties = typeof(T).GetProperties();
foreach(var property in properties){
var valueAsString = HttpContext.Current.Request.QueryString[property.PropertyName];
var value = Parse( valueAsString, property.PropertyType);
if(value == null)
continue;
property.SetValue(obj, value, null);
}
return obj;
}
Вам нужно будет реализовать метод Parse, просто используя int.Parse, decimal.Parse, DateTime.Parse и т.д.
Ответ 2
Используйте этот метод Parse с решением ivowiblo (принятый ответ):
public object Parse(string valueToConvert, Type dataType)
{
TypeConverter obj = TypeDescriptor.GetConverter(dataType);
object value = obj.ConvertFromString(null, CultureInfo.InvariantCulture, valueToConvert);
return value;
}
Ответ 3
Решение с сериализатором Newtonsoft Json и linq:
string responseString = "BaseNo=5&Width=100";
var dict = HttpUtility.ParseQueryString(responseString);
string json = JsonConvert.SerializeObject(dict.Cast<string>().ToDictionary(k => k, v => dict[v]));
StarBuildParams respObj = JsonConvert.DeserializeObject<StarBuildParams>(json);
Ответ 4
Вы можете установить свойства этого объекта в своем конструкторе, извлекая соответствующие значения из строки запроса
public StarBuildParams()
{
this.BaseNo = Int32.Parse(Request.QueryString["BaseNo"].ToString());
this.Width = Int32.Parse(Request.QueryString["Width"].ToString());
}
и вы можете убедиться, что объект преобразован в правильный формат запроса, переопределив метод ToString
.
public override string ToString()
{
return String.Format("BaseNo={0}&Width={1}", this.BaseNo, this.Width);
}
Вам все равно нужно построить и вызвать ToString
в соответствующих местах, но это должно помочь.
Ответ 5
Это должно работать до тех пор, пока ни одно из свойств не будет соответствовать другим параметрам маршрута, таким как контроллер, действие, идентификатор и т.д.
new RouteValueDictionary(Model)
http://msdn.microsoft.com/en-us/library/cc680272.aspx
Инициализирует новый экземпляр класса RouteValueDictionary и добавляет значения, основанные на свойствах указанного объекта.
Для анализа из строки запроса вы можете использовать класс модели в качестве параметра действия и позволить ModelBinder выполнять эту работу.
Ответ 6
Вы можете просто использовать метод .NET HttpUtility.ParseQueryString()
:
HttpUtility.ParseQueryString("a=b&c=d")
создает a NameValueCollection
как таковой:
[0] Key = "a", Value = "b"
[1] Key = "c", Value = "d"
Ответ 7
Основываясь на великолепных решениях Ivo и Anupam Singh, приведенных выше, вот код, который я использовал, чтобы превратить его в базовый класс для запросов POST (в случае, если у вас может быть только необработанная строка запроса, как в настройке Web API). Этот код работает для списков объектов, но может быть легко изменен для анализа одного объекта.
public class PostOBjectBase
{
/// <summary>
/// Returns a List of List<string> - one for each object that is going to be parsed.
/// </summary>
/// <param name="entryListString">Raw query string</param>
/// <param name="firstPropertyNameOfObjectToParseTo">The first property name of the object that is sent in the list (unless otherwise specified). Used as a key to start a new object string list. Ex: "id", etc.</param>
/// <returns></returns>
public List<List<string>> GetQueryObjectsAsStringLists(string entryListString, string firstPropertyNameOfObjectToParseTo = null)
{
// Decode the query string (if necessary)
string raw = System.Net.WebUtility.UrlDecode(entryListString);
// Split the raw query string into it data types and values
string[] entriesRaw = raw.Split('&');
// Set the first property name if it is not provided
if (firstPropertyNameOfObjectToParseTo == null)
firstPropertyNameOfObjectToParseTo = entriesRaw[0];
// Create a list from the raw query array (more easily manipulable) for me at least
List<string> rawList = new List<string>(entriesRaw);
// Initialize List of string lists to return - one list = one object
List<List<string>> entriesList = new List<List<string>>();
// Initialize List for current item to be added to in foreach loop
bool isFirstItem = false;
List<string> currentItem = new List<string>();
// Iterate through each item keying off of the firstPropertyName of the object we will ultimately parse to
foreach (string entry in rawList)
{
if (entry.Contains(firstPropertyNameOfObjectToParseTo + "="))
{
// The first item needs to be noted in the beginning and not added to the list since it is not complete
if (isFirstItem == false)
{
isFirstItem = true;
}
// Finished getting the first object - we're on the next ones in the list
else
{
entriesList.Add(currentItem);
currentItem = new List<string>();
}
}
currentItem.Add(entry);
}
// Add the last current item since we could not in the foreach loop
entriesList.Add(currentItem);
return entriesList;
}
public T GetFromQueryString<T>(List<string> queryObject) where T : new()
{
var obj = new T();
var properties = typeof(T).GetProperties();
foreach (string entry in queryObject)
{
string[] entryData = entry.Split("=");
foreach (var property in properties)
{
if (entryData[0].Contains(property.Name))
{
var value = Parse(entryData[1], property.PropertyType);
if (value == null)
continue;
property.SetValue(obj, value, null);
}
}
}
return obj;
}
public object Parse(string valueToConvert, Type dataType)
{
if (valueToConvert == "undefined" || valueToConvert == "null")
valueToConvert = null;
TypeConverter obj = TypeDescriptor.GetConverter(dataType);
object value = obj.ConvertFromString(null, CultureInfo.InvariantCulture, valueToConvert);
return value;
}
}
Затем вы можете наследовать от этого класса в классах-оболочках для запросов POST и анализировать любые нужные вам объекты. В этом случае код анализирует список объектов, переданных в виде строки запроса, в список объектов класса-оболочки.
Например:
public class SampleWrapperClass : PostOBjectBase
{
public string rawQueryString { get; set; }
public List<ObjectToParseTo> entryList
{
get
{
List<List<string>> entriesList = GetQueryObjectsAsStringLists(rawQueryString);
List<ObjectToParseTo> entriesFormatted = new List<ObjectToParseTo>();
foreach (List<string> currentObject in entriesList)
{
ObjectToParseToentryPost = GetFromQueryString<ObjectToParseTo>(currentObject);
entriesFormatted.Add(entryPost);
}
return entriesFormatted;
}
}
}