How to pass an array within a query string?
Asked Answered
E

12

592

Is there a standard way of passing an array through a query string?

To be clear, I have a query string with multiple values, one of which would be an array value. I want that query string value to be treated as an array- I don't want the array to be exploded so that it is indistinguishable from the other query string variables.

Also, according to this post answer, the author suggests that query string support for arrays is not defined. Is this accurate?

EDIT:

Based on @Alex's answer, there is no standard way of doing this, so my follow-up is then what is an easy way to recognize that the parameter I'm reading is an array in both PHP and Javascript?

Would it be acceptable to name multiple params the same name, and that way I would know that they belong to an array? Example:

?myarray=value1&myarray=value2&myarray=value3...

Or would this be a bad practice?

Epitomize answered 5/6, 2011 at 13:3 Comment(5)
What framework are you using? Some frameworks have methods to help pass arrays to querystrings.Cambodia
@keyboardP- PHP and Javascript, depending on use caseEpitomize
Why would you want to do that when you could just do:?myarray=value1,value2,value3Brett
@seroids: what if he has to pass commas, question marks, equal signes, and non-printable characters?Inge
how ever, ?myarray=value1&myarray=value2&myarray=value3... looks like too long, it's too heavy for record each request in system, I'd like ?myarray=value1,value2,value3...Clawson
E
643

Here's what I figured out:

Submitting multi-value form fields, i.e. submitting arrays through GET/POST vars, can be done several different ways, as a standard is not necessarily spelled out.

Three possible ways to send multi-value fields or arrays would be:

  • ?cars[]=Saab&cars[]=Audi (Best way- PHP reads this into an array)
  • ?cars=Saab&cars=Audi (Bad way- PHP will only register last value)
  • ?cars=Saab,Audi (Haven't tried this)

Form Examples

On a form, multi-valued fields could take the form of a select box set to multiple:

<form> 
    <select multiple="multiple" name="cars[]"> 
        <option>Volvo</option> 
        <option>Saab</option> 
        <option>Mercedes</option> 
    </select>
</form>

(NOTE: In this case, it would be important to name the select control some_name[], so that the resulting request vars would be registered as an array by PHP)

... or as multiple hidden fields with the same name:

<input type="hidden" name="cars[]" value="Volvo">
<input type="hidden" name="cars[]" value="Saab">
<input type="hidden" name="cars[]" value="Mercedes">

NOTE: Using field[] for multiple values is really poorly documented. I don't see any mention of it in the section on multi-valued keys in Query string - Wikipedia, or in the W3C docs dealing with multi-select inputs.


UPDATE

As commenters have pointed out, this is very much framework-specific. Some examples:

Query string:

?list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3

Rails:

"list_a": "3", 
"list_b":[
    "1",
    "2",
    "3"
  ], 
"list_c": "1,2,3"

Angular:

 "list_a": [
    "1",
    "2",
    "3"
  ],
  "list_b[]": [
    "1",
    "2",
    "3"
  ],
  "list_c": "1,2,3"

(Angular discussion)

See comments for examples in node.js, Wordpress, ASP.net


Maintaining order: One more thing to consider is that if you need to maintain the order of your items (i.e. array as an ordered list), you really only have one option, which is passing a delimited list of values, and explicitly converting it to an array yourself.

Epitomize answered 3/3, 2012 at 16:1 Comment(18)
I think the reason that this functionality is not documented in general sources like Wiki or W3C is that it is not generally supported by web frameworks. PHP and I think Rails automatically convert multiple "key[]" query parameters into an array. Others don't.Include
The nodejs querystring module uses the ?cars=Saab&cars=Audi formSixtyfour
In express (node.js), both ?cars=Saab&cars=Audi and ?cars[]=Saab&cars[]=Audi get turned into arrays. However, ?cars=Saab ends up as a string, but ?cars[]=Saab is an array with a single element.Gregale
In the select, you miss values for options (you have got only labels). I didn't find a spec that would say that value = label if the value attribute is missing. Another thing is that "?cars=Saab&cars=Audi" is not a bad way generally. E.g. in Django (python framework), it is the right way.Mcewen
Can you set keys using this approach? And what about multi-dimensional arrays?Except
ASP.NET MVC doesn't appear to read the format ?cars[]=Saab&cars[]=Audi There have either be no square brakets or the square brackets need indexes/Todd
This is framework dependent as well. For eg, ?cars=Saab,Audi will work for Wordpress but ?cars[]=Saab&cars[]=Audi won't.Mayman
Responding to you're note, RFC 3986 says : A host identified by an Internet Protocol literal address, version 6 [RFC3513] or later, is distinguished by enclosing the IP literal within square brackets ("[" and "]"). This is the only place where square bracket characters are allowed in the URI syntax. tools.ietf.org/html/rfc3986Sabellian
@tetram, this quote is talking about ipv6 ip address formatting. this thread is about formatting of array values in the query string for which square brackets are used in an escaped format as %5B for [ and %5D for ].Plasm
This quote is talking about ipv6 and square brackets... in URI syntax. And as i understand it, the array query is passed through the URL (that is an URI rigth ?).Sabellian
The square bracket notation would be great if it was also generated by http_build_query, which unfortunately it does not. Supposedly because of dealing with arbitrary nesting.Scanderbeg
What do you thing: could it be an option to transform the array to an JSON string before adding this to an url?Abingdon
This part of your answer was helpful (and I wonder where I can read the docs about this): "One more thing to consider is that if you need to maintain the order of your items (i.e. array as an ordered list), you really only have one option, which is passing a delimited list of values, and explicitly converting it to an array yourself."Atheist
@Except yes, at least with php, the key can be set in the query string, as in: ?array[keyone]=value1&array[keytwo]=value2Aorta
For encoding top-level arrays in PHP: http_build_query([['foo'=>'bar'],['baz'=>'qux']]); gives a percent-encoded 0[foo]=bar&1[baz]=qux. Confirming it plays well with Laravel's input functionality. See http_build_query and rawurldecode for more info.Dubbing
?cars=Saab,Audi I am using this in PHP, works fine for me.Teeth
@Yarin, Could you please give an example of the last point you mentioned, about maintaining order of an array queryparam?Nepotism
3rd option is not bad! you should url encode the value before passing. and backend you need to again url decode and retrieve the value.Scammon
V
47

A query string carries textual data so there is no option but to explode the array, encode it correctly and pass it in a representational format of your choice:

p1=value1&pN=valueN...
data=[value1,...,valueN]
data={p1:value1,...,pN:valueN}

and then decode it in your server side code.

Vespers answered 5/6, 2011 at 13:17 Comment(4)
OK thanks for clarification. Can you see my revised question- is it necessary to do p1...pn, for keys, or can I just name multiple keys 'p', and that way I would know they all belong to same array?Epitomize
You can use the same name; #2203930 or just p=aa,bb,cc which is the most obvious way as Serodis commented.Vespers
very useful with JSON.stringifyand JSON.parse!Gingham
A simple and common representational format is JSON, in PHP using json_encode then rawurlencode and json_decode on the other page.Baroque
I
29

I don't think there's a standard.
Each web environment provides its own 'standard' for such things. Besides, the url is usually too short for anything (256 bytes limit on some browsers). Of course longer arrays/data can be send with POST requests.

However, there are some methods:

  1. There's a PHP way, which uses square brackets ([,]) in URL queries. For example a query such as ?array_name[]=item&array_name[]=item_2 has been said to work, despite being poorly documented, with PHP automatically converting it into an array. Source: https://mcmap.net/q/64320/-how-to-pass-an-array-within-a-query-string

  2. Object data-interchange formats (e.g. JSON - official website, PHP documentation) can also be used if they have methods of converting variables to and from strings as JSON does.
    Also an url-encoder (available for most programming languages) is required for HTTP get requests to encode the string data correctly.

Although the "square brackets method" is simple and works, it is limited to PHP and arrays.
If other types of variable such as classes or passing variables within query strings in a language other than PHP is required, the JSON method is recommended.

Example in PHP of JSON method (method 2):

$myarray = array(2, 46, 34, "dfg");
$serialized = json_encode($myarray)
$data = 'myarray=' . rawurlencode($serialized);
// Send to page via cURL, header() or other service.

Code for receiving page (PHP):

$myarray = json_decode($_GET["myarray"]); // Or $_POST["myarray"] if a post request.
Inge answered 5/6, 2011 at 13:46 Comment(2)
Express.js also supports square bracket notation, tested on 4.13.4.Acton
if you are going to use json_encode, it's good to base64_encode and decode the string when passing it as a get paramHeritor
E
25

This works for me:

In link, to attribute has value:

to="/filter/arr?fruits=apple&fruits=banana"

Route can handle this:

path="/filter/:arr"

For Multiple arrays:

to="filter/arr?fruits=apple&fruits=banana&vegetables=potato&vegetables=onion"

Route stays same.

SCREENSHOT

enter image description here

UPDATE: In order to achieve this string structure, query-string is the best package.

For example:

import { stringify, parse } from 'query-string';

const queryParams={
   fruits:['apple','banana'],
   vegetables:['potato','onion']
}
//arrayFormat can be bracket or comma 
stringify(queryParams, { arrayFormat: 'bracket' });
Economic answered 30/11, 2018 at 6:38 Comment(0)
A
21

Although there isn't a standard on the URL part, there is more or less a standard for JavaScript.

The Array.toString method returns a comma-separated string of items.

Due to this, if you pass objects containing arrays to URLSearchParams, and call toString() on it, it will in turn call toString on each value, resulting in something like this:

let data = {
  name: 'foo',
  values: ['bar', 123]
}

new URLSearchParams(data).toString();
// ?name=foo&values=bar,123 (with escaped comma characters)

This format is really easy to serialize on the frontend and to parse on any server, besides being easy to understand regardless of context, so it tends to be my go-to for sending array data in URLs.

Aphasia answered 4/3, 2020 at 21:7 Comment(7)
Accually it produces escaped result: "str=abc&arr=abc%2C123"Doit
Indeed, I'll update the code to display that. The point is still the same tho, which is that the 'standard' for js is using commas in arrays.Vienne
Is the comma separated better/compatible compared to the arr=abc&arra=123 ?Warlord
@DimitriKopriwa guessing you meant arr=abc&arr=123. I'd say what JS does is slightly better, since it uses less characters, the frontend code will be simpler and it'll maintain the order of the items, but just go with whatever your backend framework (django, PHP, laravel, etc) supports. If I were to implement the parsing myselft though, I'd probably go with the comma separated approach.Vienne
URLSearchParams only accepts string parameters, not arrays! Any non-string is simply being auto-converted to a string, via normal toString behaviors. The fact that JavaScript converts arrays to strings by joining the entries with commas should not be taken as the standard way in which JavaScript encodes array data in URLs. That's simply the way JavaScript converts arrays to strings across the language.Morality
(Minor correction on my comment - if you're passing an object to URLSearchParam's constructor, the only supported property value type is a string, any non-string property value gets coerced. But the constructor does support other, unrelated data types).Morality
@ScottyJamison a bit of necroing, but yeah, that's for sure what's happening. I still think it's a nice thing for the server to follow the same standards as the frontend, but I'll change the description to denote that comma-separated arrays is the standard for array string coercion.Vienne
I
16

Note that the query-string module lists the different types of array encoding it supports (https://www.npmjs.com/package/query-string):

For instance {foo: ['1', '2', '3']} can be encoded as:

'foo[]=1&foo[]=2&foo[]=3'
'foo[0]=1&foo[1]=2&foo[3]=3'
'foo=1,2,3'
'foo=1&foo=2&foo=3'
// Any custom separator can be used:
'foo=1|2|3'
// ... and more custom formats

This shows that there are many solutions adopted out there...

Interurban answered 14/4, 2021 at 16:17 Comment(0)
R
7

I feel it would be helpful for someone who is looking for passing the array in a query string to a servlet. I tested below query string and was able to get the array values using req.getgetParameterValues(); method. Below is the query string I passed through browser.

  http://localhost:8080/ServletsTutorials/*.html? 
  myname=abc&initial=xyz&checkbox=a&checkbox=b

checkbox is my parameter array here.

Raeannraeburn answered 8/1, 2013 at 11:38 Comment(0)
H
4

I use React and Rails. I did:

js

  let params = {
    filter_array: ['A', 'B', 'C']
  }

  ...

  //transform params in URI

  Object.keys(params).map(key => {
    if (Array.isArray(params[key])) {
      return params[key].map((value) => `${key}[]=${value}`).join('&')
    }
  }
  //filter_array[]=A&filter_array[]=B&filter_array[]=C
Hoenir answered 11/9, 2019 at 18:20 Comment(0)
N
2

You mention PHP and Javascript in your question, but not in the tags. I reached this question with the intention of passing an array to an MVC.Net action.

I found the answer to my question here: the expected format is the one you proposed in your question, with multiple parameters having the same name.

Nablus answered 24/3, 2015 at 0:59 Comment(2)
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes.Gassaway
@Gassaway It's not a link only answer. "The expected format is the one you proposed in your question, with multiple parameters having the same name."Nablus
C
1

You can use http_build_query to generate a URL-encoded querystring from an array in PHP. Whilst the resulting querystring will be expanded, you can decide on a unique separator you want as a parameter to the http_build_query method, so when it comes to decoding, you can check what separator was used. If it was the unique one you chose, then that would be the array querystring otherwise it would be the normal querystrings.

Cambodia answered 5/6, 2011 at 15:51 Comment(0)
T
1

You can use those functions, but make sur you don't use '-' on your object keys.

// convert obj to url params
function objToUrlParams(obj) {
    let toUrlParams = (obj, prefex = '') => {

        // create url params
        let urlParams = "";

        // loop through obj
        for (let key in obj) {
            let val = obj[key];

            if (val == null) continue;
            if (val == undefined) continue;
            // if(val == '') continue;

            // if val is an object then call toUrlParams
            if (val instanceof Array) {
                // convert val from Array to object
                let valToObj = {};
                val.forEach((v, i) => {
                    valToObj[i] = v;
                });

                val = valToObj;
            }

            let newPrefex = prefex + key;

            if (val instanceof Object) {
                urlParams += toUrlParams(val, newPrefex + '-');
            } else {
                urlParams += newPrefex + '=' + val;
            }

            urlParams += '&';
        }

        // remove last &
        urlParams = urlParams.slice(0, -1);

        // return url params
        return urlParams;

    }

    // encodeURI
    return encodeURI(toUrlParams(obj));
}

// convert url params to obj
function urlParamsToObj(urlParams) {

    // decodeURI
    urlParams = decodeURI(urlParams);

    let toObj = (urlParams) => {

        let obj = {};

        let urlParamsArr = urlParams.split('&');

        let subUrlParramsObj = {};

        // loop through urlParams
        for (let i = 0; i < urlParamsArr.length; i++) {
            let item = urlParamsArr[i];

            // get key and value
            let key = item.split('=')[0];
            let val = item.split('=')[1] ?? null;
            let keys = key.split('-');

            if (val == "null") {
                val = null;
            } else if (val == "undefined") {
                val = undefined;
            } else if (val == "true") {
                val = true;
            } else if (val == "false") {
                val = false;
            } else if (val == "NaN") {
                val = NaN;
            } else if (val == "Infinity") {
                val = Infinity;
            }

            // if keys length is 1 then set obj[key] to val
            if (keys.length == 1) {
                // check if obj contains key
                if (obj.hasOwnProperty(key)) {
                    // if obj[key] is an array then push val
                    if (obj[key] instanceof Array) {
                        obj[key].push(val);
                    } else {
                        // create array and push val
                        obj[key] = [obj[key], val];
                    }
                } else {
                    obj[key] = val;
                }
            }
            // if keys length is 2 then set obj[keys[0]][keys[1]] to val
            else if (keys.length > 1) {

                let key0 = keys[0];

                // check if subUrlParramsObj contains keys[0]
                if (!subUrlParramsObj[key0]) {
                    subUrlParramsObj[key0] = [];
                }

                // remove keys[0] from keys
                keys.shift();
                // join keys with -
                key = keys.join('-');

                let param = key + '=' + val;

                // add param to subUrlParramsObj[keys[0]]
                subUrlParramsObj[key0].push(param);

            }

        }

        // loop through subUrlParramsObj
        for (let key in subUrlParramsObj) {

            // join subUrlParramsObj[key] with &
            let val = subUrlParramsObj[key].join('&');

            // set obj[key] to val
            obj[key] = toObj(val);

        }

        // return obj
        return obj;

    }

    return checkIfObjShouldBeArrayAndConvert(toObj(urlParams));

}

// check if object should be converted to array, if its keys are numbers
function checkIfObjShouldBeArrayAndConvert(obj) {

    // if obj is an array
    if (obj instanceof Array) {
        // loop through obj
        obj.forEach((item, i) => {
            // check if item is an object
            if (item instanceof Object) {
                // convert item to array
                obj[i] = checkIfObjShouldBeArrayAndConvert(item);
            }
        });

        // return obj
        return obj;
    }

    // check if all keys are numbers
    let canConvertToArray = true;
    for (let key in obj) {

        // get value
        let val = obj[key];

        // check if value is an object or Array
        if (val instanceof Object || val instanceof Array) {
            obj[key] = checkIfObjShouldBeArrayAndConvert(val);
        }

        if (isNaN(key)) {
            canConvertToArray = false;
        }
    }

    // order obj by keys
    let orderedObj = {};
    Object.keys(obj).sort().forEach(function(key) {
        orderedObj[key] = obj[key];
    });

    // check if the first key is 0
    if (Object.keys(orderedObj)[0] != 0) {
        canConvertToArray = false;
    }

    // check if the keys step is 1
    let keys = Object.keys(orderedObj);
    // loop through keys
    for (let i = 0; i < keys.length - 1; i++) {
        // get key
        let key = keys[i];
        // get next key
        let nextKey = keys[i + 1];
        // get key step
        let keyStep = nextKey - key;
        // check if key step is 1
        if (keyStep != 1) {
            canConvertToArray = false;
            break;
        }
    }

    // if all keys are numbers then convert obj to array
    if (canConvertToArray) {
        let arr = [];
        for (let key in orderedObj) {
            arr.push(orderedObj[key]);
        }
        return arr;
    }

    // return obj
    return obj;
}

// add params to url
function addParamsToUrl(params, url = window.location.href) {

    // check if url has params
    if (url.indexOf('?') == -1) {
        url += '?';
    } else {
        url += '&';
    }

    return url + params ?? '';
}

function addObjToUrl(obj, url = window.location.href) {
    return addParamsToUrl(objToUrlParams(obj), url);
}

// extract params from url
function extractParamsFromUrl(url = window.location.href) {
    return urlParamsToObj(url.split('?')[1]);
}
// test
let urlParams = objToUrlParams({
    a: 1,
    b: "zad",
    c: {
        d: 2,
        e: "f"
    },
    j: [1, 2, 3, 4]
});
console.log(urlParams); // a=1&b=zad&c-d=2&c-e=f&j=1&j=2&j=3&j=4

let obj = urlParamsToObj(urlParams);
console.log(obj); // { "a": "1", "b": "zad", "j": [ "1", "2", "3", "4" ], "c": { "d": "2", "e": "f" } }
Tombouctou answered 8/8, 2022 at 0:28 Comment(0)
B
0

Check the parse_string function http://php.net/manual/en/function.parse-str.php

It will return all the variables from a query string, including arrays.

Example from php.net:

<?php
$str = "first=value&arr[]=foo+bar&arr[]=baz";
parse_str($str);
echo $first;  // value
echo $arr[0]; // foo bar
echo $arr[1]; // baz

parse_str($str, $output);
echo $output['first'];  // value
echo $output['arr'][0]; // foo bar
echo $output['arr'][1]; // baz

?>
Broadfaced answered 3/3, 2012 at 16:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.