Skip to main content

Turn a Query String to JSON: Parse ?a=1&b=2 Into an Object

Parse a URL query string into JSON, decode percent-encoding, fold repeated params into arrays, and debug broken links — with a worked ?name=tom&age=20 example.

Published By Li Lei
#query string #json #url #web development #debugging

Turn a Query String to JSON: Parse ?a=1&b=2 Into an Object

A query string is the part of a URL after the ?. It looks simple — ?a=1&b=2 — but the moment you start reading a real one by eye, the simplicity falls apart. Repeated keys, percent-encoded characters, empty values, bracketed nesting: a query string from a live request is rarely the tidy two-pair example you see in tutorials. Converting it into JSON gives you a structured object you can actually read, edit, and compare.

This post walks through how that conversion works, why the edge cases matter, and how I use it to debug links that misbehave.

The Core Algorithm: Split on &, Then on the First =

The whole thing rests on two splits. Strip the leading ? if it is there, then split the string on & to get a list of pairs. For each pair, split on the first = only — not every = — because a value can legitimately contain an equals sign. A base64 token, for instance, often ends in = padding, and splitting greedily would mangle it.

So the rule is:

  1. Drop a leading ?.
  2. Split the remaining string on & → individual key=value chunks.
  3. For each chunk, split on the first =key and value.
  4. URL-decode both halves.
  5. Assemble the decoded pairs into an object.

That "first = only" detail is the one most hand-rolled parsers get wrong. redirect=https://app.example.com/?next=/home has three = characters, and only the first one separates the key from the value.

Worked Example: ?name=tom&age=20

Take the query string ?name=tom&age=20 and run it through the steps.

Strip the ?, giving name=tom&age=20. Split on &:

["name=tom", "age=20"]

Split each on the first =:

["name", "tom"]
["age", "20"]

Decode (nothing to decode here) and assemble:

{
  "name": "tom",
  "age": "20"
}

Notice that age is the string "20", not the number 20. A query string has no type information — everything arrives as text. Guessing types is dangerous: a value like 007 or a 20-digit order ID would silently change if you coerced it to a number. If your backend needs a real integer, cast it yourself after parsing. The Query String ⇄ JSON Converter keeps every value as a string for exactly this reason.

Decoding: Percent-Encoding and the + Shorthand

Browsers and backends escape characters that would otherwise break a URL. A space becomes %20, a slash becomes %2F, and non-ASCII text is encoded byte by byte — turns into %E4%BD%A0. There is also the legacy +-for-space convention inherited from HTML form posts, where name=Ada+Lovelace means Ada Lovelace.

A correct parser decodes both on the way in: + becomes a space, and %XX sequences are resolved back to their characters. So q=Ada+Lovelace&city=%E5%8C%97%E4%BA%AC reads back as { "q": "Ada Lovelace", "city": "北京" }. Going the other direction, when you rebuild a query string, every key and value should pass through encodeURIComponent, which produces %20 for spaces — the form that every server accepts. If a percent sequence is malformed (%E4%BD with a byte missing), keep the raw text rather than throwing, so a single bad character does not nuke the whole parse.

Decoding is also where a lot of bugs hide. If you ever see a value like WELCOME%2F survive into the output, you are looking at double-encoding — the / was escaped to %2F, then the whole thing escaped again so the % became %25. Seeing the decoded JSON next to the raw string makes that obvious.

Repeated Parameters Become Arrays

Here is the case that trips up naive parsers and even some standard libraries. What happens when a key appears more than once?

?tag=red&tag=blue&tag=green

A lazy parser keeps the last value and drops the first two, so you lose red and blue without warning. The right behavior is to fold repeats into an array:

{
  "tag": ["red", "blue", "green"]
}

A key that appears exactly once stays a plain string; a key that repeats becomes a list. This is the convention PHP, Rails, and the popular qs library all use, so the JSON drops straight back into most backends. Many tools also support an explicit tag[]=red&tag[]=blue bracket syntax and a nested form like user[name]=ada, which expands into { "user": { "name": "ada" } }. Turn that option off when a parameter name legitimately contains square brackets, and they get treated as literal characters in the key instead.

How I Use This to Debug Links

I reach for this conversion most often when a redirect quietly eats a parameter. Last month a login flow was supposed to carry ?return_to=/dashboard&plan=pro&promo=WELCOME, but support kept reporting that the promo never applied. Reading the two URLs character by character in a terminal got me nowhere — they looked identical at a glance.

So I parsed both: the URL my code generated, and the URL the server actually returned after the redirect. Side by side as JSON, the difference jumped out. The server's version had promo present but its value was WELCOME%2F — a stray trailing slash had been double-encoded somewhere in the redirect chain. The parsed JSON showed "promo": "WELCOME/" against my expected "promo": "WELCOME", and the encoding mismatch told me exactly which layer to fix. Five minutes, no guessing. Doing that by eye on two 200-character URLs would have taken half an hour and probably missed it.

The same trick works for auditing marketing links stuffed with UTM parameters, reshaping form data between a flat backend and a nested one, and pulling a captured API request out of the DevTools Network tab into something editable.

When to Use a Different Tool

A single query string converted to JSON is one job. A few neighbors handle adjacent ones:

  • Comparing dozens of campaign links at once, with a flat table and CSV export, is what the URL Query Params Extractor is built for — it is the bulk-audit counterpart to this single-string converter.
  • If you only need to escape or unescape a raw value rather than restructure a whole string, the URL Encoder / Decoder is the smaller, sharper tool.
  • Once you have the JSON, the JSON Formatter will pretty-print, validate, and minify it for you.

A query string is just a flat list of key=value pairs separated by &. Split it on the right characters, decode it properly, and respect repeats and nesting, and you get JSON you can trust — and links you can actually debug.


Made by Toolora · Updated 2026-06-13