The `application/x-www-form-urlencoded` format (which is used for GET url parameters) is extremely limited and cannot represent complex structures. HTTP does not require that the query portion of URLs be in this format, but since it provides for no way to negotiate the format of this data Tornado (like most web frameworks) unconditionally tries to interpret the query as form-urlencoded.
Some frameworks and libraries (including php, rails, and jquery) support a non-standard extension (or one of several similar extensions; it's unclear how compatible these encodings are across languages) to encode additional structure with square brackets in the query parameters (
http://stackoverflow.com/questions/30261513/is-there-a-spec-for-nested-urlencoded-params). Tornado doesn't know anything about this but it should be straightforward to parse and reconstruct yourself given the data available in self.request.query_arguments (once you define what exactly the rules are for the format you want to use).
However, as soon as you use any of these square-bracket encodings, you're no longer compatible with plain HTML forms so there is little reason to stick with the form-encoded format. Personally I would just stuff a json-encoded string into a single form-encoded argument. It's not very human-legible but it's easier for machines to work with than an extension of the form-encoded format: `?q=%7B%22keyC%22%3A%7B%221%22%3A%5B%22sth%22%2C%22sth_else%22%5D%7D%2C%22keyB%22%3A%5B%22blah%22%2C%22blah%22%5D%2C%22keyA%22%3A%7B%221%22%3A%22value1%22%2C%222%22%3A%22value2%22%7D%7D`
Finally, a general piece of advice: Tornado's get_argument() family of methods is for use with HTML forms. If you're doing something that is not an HTML form, then instead of thinking "how do I make get_argument() work with my data", you should think "what do I use instead of get_argument()".
-Ben