将查询字符串转换为/从对象转换

43

我有这个(简化版)类:

public class StarBuildParams
{
    public int BaseNo { get; set; }
    public int Width { get; set; }
}

我必须将它的实例转换为这样的查询字符串:

"BaseNo=5&Width=100"
此外,我还需要将这样的查询字符串转换回该类的对象。
我知道这基本上就是模型绑定器所做的事情,但在我的情况下(在一个线程中运行的某个深埋类),我没有控制器上下文。
因此,是否有一种简单的方法可以在没有控制器上下文的情况下将对象转换为查询字符串并相应地转换回来?
最好使用模型绑定,但我不知道如何使用。

我所能想到的就是使用反射。 - ojlovecd
@ojlovecd 在这里如何使用反射帮助? - Kirk Broadhurst
我相信这种情况是可以避免的。你能描述一下你的场景吗?你是怎么在这个类中得到查询字符串的? - Darin Dimitrov
嗨Darin,我在等你的回复,因为如果你说不可能,我就相信它不可能 :) 我尝试存储有关操作图像的一些信息,并需要将其作为查询字符串存储,因为我使用ImageResizer ,我必须在那里传递一个查询字符串。此外,我还想在其中存储一些自己的数据。我的图像生成器在一个线程中运行,覆盖多个具有自己处理信息的图像。 - Marc
哇,1000次浏览量却没有点赞... - Marc
可能会起作用。https://dev59.com/Emw15IYBdhLWcg3wD3rp#60936159 - giorgi02
8个回答

44

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

9
当查询字符串包含数组时,例如P[0][A]=xxx&P[0][B]=yyyy&P[1][A]=aaaa&P[1][B]=bbbb时,此方法无效。 - DrGriff
17
这个回答拯救了我的整个职业生涯。 - Jeremy
回答有效,我的queryString包含多个项目。我成功将它们转换为Poco对象。 - Ben
1
我喜欢你,伙计...你的答案非常有效! - Mohammad Zekrallah

31

您可以使用反射,像这样:

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等方法即可。


16
请使用ivowiblo的解决方案(被接受的答案)并使用此Parse方法:
public object Parse(string valueToConvert, Type dataType)
{
    TypeConverter obj = TypeDescriptor.GetConverter(dataType);
    object value = obj.ConvertFromString(null, CultureInfo.InvariantCulture,  valueToConvert);
    return value;
}

6

将查询字符串序列化并反序列化到您的类对象中

 JObject json;
 Request.RequestUri.TryReadQueryAsJson(out json);
 string sjson = JsonConvert.SerializeObject(json);
 StarBuildParams query = JsonConvert.DeserializeObject<StarBuildParams>(sjson);

1
这篇文章值得更多的点赞。TryReadQueryAsJson 可在 System.Net.Http 中使用,并创建了一个 JObject。不依赖于 System.Web - w5l

5

您可以通过从查询字符串中检索相关值,在构造函数中设置此对象的属性。

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
您可以使用.NET的HttpUtility.ParseQueryString()方法: HttpUtility.ParseQueryString("a=b&c=d")会生成以下NameValueCollection
[0] Key = "a", Value = "b"
[1] Key = "c", Value = "d"

我喜欢你展示了输出示例。谢谢。 - RedRose

4
只要没有任何属性与其他路由参数(如控制器、操作、ID等)匹配,这个应该就能正常工作。
new RouteValueDictionary(Model)

http://msdn.microsoft.com/en-us/library/cc680272.aspx

初始化RouteValueDictionary类的新实例,并添加基于指定对象属性的值。

要从查询字符串解析回来,您可以将模型类用作操作参数,并让ModelBinder完成其工作。


2
你能举个例子吗? - Toolkit
我因为你那行代码而爱你,兄弟。它直接可以运行,甚至在 aspnetcore 2.2 webapi 的代码上也是如此 :) - Tomamais

0
在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's data types and values
            string[] entriesRaw = raw.Split('&');

            // Set the first property name if it is not provided
            if (firstPropertyNameOfObjectToParseTo == null)
                firstPropertyNameOfObjectToParseTo = entriesRaw[0].Split("=").First();

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接