45

I have this (simplified) class:

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

And I have to transform instances of it to a querystring like this:

"BaseNo=5&Width=100"

Additionally I have to transform such a querystring back in an object of that class.

I know that this is pretty much what a modelbinder does, but I don't have the controller context in my situation (some deep buried class running in a thread).

So, is there a simple way to convert a object in a query string and back without having a controller context?

It would be great to use the modelbinding but I don't know how.

6
  • All that I can think of is using reflection. Commented Mar 22, 2012 at 6:46
  • @ojlovecd How would reflection help here? Commented Mar 22, 2012 at 6:53
  • I am sure that this situation can be avoided. Could you describe your scenario? How did you end up with a query string in this class? Commented Mar 22, 2012 at 6:56
  • Hi Darin, I was waiting for your response because if you say it's not possible I believe it :) I try to store some information about manipulating an image and need that as a querystring because I use the ImageResizer and I have to pass a a query string there. Additionally I want to store some own data in it. And my imagegenerator runs in a thread over a couple of images each with its own manipulation info. Commented Mar 22, 2012 at 7:06
  • Wow, 1000 views and no upvote... Commented Jun 7, 2013 at 0:59

8 Answers 8

47

A solution with Newtonsoft Json serializer and 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);
Sign up to request clarification or add additional context in comments.

5 Comments

This doesn't work when the query string contains arrays, e.g. P[0][A]=xxx& P[0][B]=yyyy&P[1][A]=aaaa&P[1][B]=bbbb
This answer saved my entire career
Answer works, I had a queryString which contained multiple items. I could manage to convert them to a Poco object
i love you man ... your answer works like a charm !
For some reason this converts bool values to "true,false", I don't know if it's Cast<string> part or what, but it's not working and this is the closest solution I've found. Any reason why it would change true to true,false?
31

You can use reflection, something like this:

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

You'll need to implement the Parse method, just using int.Parse, decimal.Parse, DateTime.Parse, etc.

Comments

16

Use this Parse method with the ivowiblo's solution (accepted answer):

public object Parse(string valueToConvert, Type dataType)
{
    TypeConverter obj = TypeDescriptor.GetConverter(dataType);
    object value = obj.ConvertFromString(null, CultureInfo.InvariantCulture,  valueToConvert);
    return value;
}

Comments

6

Serialize query string and deserialize to your class object

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

1 Comment

This deserves more upvotes. TryReadQueryAsJson is available in System.Net.Http and creates a JObject. No dependencies on System.Web.
5

You can set the properties of this object in its constructor by retrieving the relevant values from the querystring

public StarBuildParams()
{
    this.BaseNo = Int32.Parse(Request.QueryString["BaseNo"].ToString());
    this.Width = Int32.Parse(Request.QueryString["Width"].ToString());
}

and you can ensure that the object is converted to the correct querystring format by overriding the ToString method.

public override string ToString()
{
    return String.Format("BaseNo={0}&Width={1}", this.BaseNo, this.Width);
}

You'll still need to construct and call ToString in the appropriate places, but this should help.

Comments

5

You can just use .NET's HttpUtility.ParseQueryString() method:

HttpUtility.ParseQueryString("a=b&c=d") produces a NameValueCollection as such:

[0] Key = "a", Value = "b"
[1] Key = "c", Value = "d"

Comments

4

This should work so long as none of the properties match any other route parameters like controller, action, id, etc.

new RouteValueDictionary(Model)

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

Initializes a new instance of the RouteValueDictionary class and adds values that are based on properties from the specified object.

To parse back from the query string you can use the model class as an action parameter and let the ModelBinder do it's job.

2 Comments

can you give an example?
I love you for that line of code brother. It worked out of the box, even on a aspnetcore 2.2 webapi code :)
0

Building off of Ivo and Anupam Singh's great solutions above, here is the code that I used to turn this into a base class for POST requests (in the event that you may only have the raw query string like in a Web API setup). This code works for lists of objects, but could easily be modified to parse a single object.

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

Then you can inherit from this class in wrapper classes for POST requests and parse to whichever objects you need. In this case, the code parses a list of objects passed as a query string to a list of wrapper class objects.

For example:

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.