Query String to JSON
Turn any query string into structured JSON and back — bracket nesting, array conventions and + handling made explicit — entirely on this device.
How nested keys are written: user[name] (brackets), user.name (dots), or flat keys only (none). How lists are written: a=1&a=2 (repeat), a[]=1 (brackets), a[0]=1 (indices), or a=1,2 (comma). How a space is encoded: %20 (RFC 3986) or + (form encoding). With %20, a literal + stays +. Maximum key nesting depth (1–32); deeper input refuses with an error instead of guessing. How a key without = reads (?flag) — JSON null writes back through the same policy.
Press ? for shortcuts
Do it offline:$ node -e 'console.log(JSON.stringify(Object.fromEntries(new URLSearchParams(process.argv[1]))))' 'a=1&b=2'
How to use it
- Paste a query string, a full URL or JSON — the badge shows the detected direction, and one click flips it.
- The banner names the conventions it found; the nesting, array and space radios stay on auto until you pin one.
- Read the result beside its pair count — in reverse the encoded query is the artifact, its decoded form previewed underneath.
- Copy or download the output, or share the state as a link; sharing asks first when a parameter looks credential-shaped.
A query string is flat text with unwritten rules: nothing in the standards says how ?user[name]=ada or tags=a,b,c should become structure. Paste one — or a whole URL — and read it as JSON with the nesting, array and space conventions detected, announced and yours to override; paste JSON instead to write the query string back. Everything runs on this device.
How it works
RFC 3986 defines the query as everything between ? and # — and stops there: no arrays, no nesting, no rule for +. The structure people expect is folklore: PHP and Rails read user[name]=ada as an object, Java servlets repeat a key to carry a list, some APIs join values with commas, and HTML forms write a space as +.
So the engine runs two layers: a flat pass with the application/x-www-form-urlencoded reading browsers use — split on &, then on the first =, percent-decode — and the qs convention layer applying the dialect the radios show: bracket or dot nesting to the depth limit, one of four array styles, + as space or as itself.
Examples
?user[name]=ada&user[langs][]=js&user[langs][]=ts{"user":{"name":"ada","langs":["js","ts"]}}The bracket dialect PHP and Rails made common practice: user[name] opens an object, an empty [] appends to an array, and the leading ? is optional.
tags=a,b,c{"tags":["a","b","c"]}One pair carries the whole list under the comma convention. Detection proposes it when a value is shaped like a,b,c and no stronger array evidence appears.
tags=red,green,blue{"tags":"red,green,blue"}The same shape under repeat stays one plain string — array conventions are a choice, not a fact. Only the radio differs from the previous example.
{"page":2,"sort":"-date"}page=2&sort=-dateReverse mode: numbers serialize plainly and the encoded query is the artifact. Query strings carry no types — read back, this gives the string "2", not the number 2.
?flag&debug=1{"flag":true,"debug":"1"}flag has no = at all; a labeled policy decides its reading — empty string by default, true here, or null — and a chip names the choice.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Full URLs work — only the query is read
The text between ? and # is extracted, with a note; path and fragment never gain query semantics, so a + in the path stays literal. A leading ? is optional.
+ and %20 claim the same space
%20 is the RFC 3986 spelling; + means space only in the form dialect. Percent mode keeps + literal, plus mode decodes it — and detection proposes plus whenever one appears.
Broken percent escapes are kept, not repaired
%GG, a truncated %A and bytes that are not UTF-8 stay as pasted; each bad pair earns a warning naming its 1-based position and the fault. Past twenty, an honest count covers the rest.
Depth stops at 5 until you raise it
Deeper keys refuse with the key named — raise the limit (to 32) or flatten. Sparse indices like a[3]=x compact with a note; indices past 999 become object keys.
__proto__ is data here, never a prototype
A key named __proto__ or constructor lands as an ordinary property — never silently dropped, and Object.prototype is never touched. The classic pollution trick becomes plain data.
Common errors
`%GG` at position 6 is not a valid percent escape — left undecoded- A warning, not a failure: the pair lands in the JSON with its escape untouched. Positions count characters from 1 in what you pasted — here the % in q=100%GG&lang=en. A literal percent sign belongs in a query as %25.
`filter[a][b][c][d][e][f]` nests deeper than the depth limit of 5 — raise the limit or flatten the key- This key walks six levels under a default limit of five. Raise the ceiling (to 32) when the structure is intentional — the refusal names the key, so a runaway bracket is easy to spot.
A query string needs named top-level keys — this JSON's root is an array, not an object. Wrap it in an object such as `{"items": …}` first.- Pairs need names left of the =, so a bare array or scalar has nowhere to hang. Wrapped as {"items":[1,2,3]}, the same data becomes items=1&items=2&items=3 under repeat.
Unexpected '}' at line 1, column 12 — probable cause: trailing comma after the last property.- Reverse-mode JSON failures arrive from the shared validator with line, column and a caret frame, word for word. A trailing comma is legal in JavaScript source but never in JSON.
Input is larger than the 1 MB limit for this tool. For bigger payloads use the CLI: node -e 'console.log(JSON.stringify(Object.fromEntries(new URLSearchParams(process.argv[1]))))' 'a=1&b=2'- A refusal, never a truncation — cut input would put warning positions on the wrong characters. The one-liner reads flat pairs at any size; bracket and dot conventions stay this page's job.
FAQ
How are arrays encoded in query strings?
Four ways, none official: repeat the key (tag=js&tag=ts), append brackets (tag[]=js&tag[]=ts), number them (tag[0]=js&tag[1]=ts), or join with commas (tag=js,ts). This page reads and writes all four — set the array radio to whatever your framework expects.
+ or %20 — which encodes a space?
Both: RFC 3986 spells a space %20; the HTML form encoding inherited + instead. So q=blue+socks means blue socks to a form parser and a literal plus to a strict URI reader — the space radio settles it.
How do nested objects get encoded?
By convention only — brackets (user[name]=ada) or dots (user.name=ada); this page speaks both. In reverse the nesting radio picks the spelling; nesting none refuses nested JSON with a suggestion, and a key whose brackets or dots would read back as structure is warned.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Re-run the conversion at any input size.
- ⌘/Ctrl + S
- Download the output instead of saving the page.
- ⌘/Ctrl + C
- Copy the whole output when nothing is selected.
- ⌘/Ctrl + Shift + C
- Copy the whole output even over a selection.
- s
- Swap direction, query ↔ JSON (idle inside text fields).
- t
- Advance to the next theme palette.
- ? or ⌘/Ctrl + /
- Open the shortcut help overlay.
- Esc
- Dismiss the topmost overlay; input survives.