qs

package
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package qs parses and serializes URL query strings with support for nested objects and arrays via bracket notation. It is a Go port of a subset of the npm module "qs", the query-string library Express uses when its extended body/query parser is enabled.

Where the standard library's net/url and this repository's querystringlite treat a query string as a flat mapping of keys to values, qs understands the bracket syntax that lets a single query string describe arbitrarily nested structures. This is useful whenever an HTML form, an Express client, or an AJAX request encodes an object or array into a URL, and the server needs to reconstruct that shape rather than a flat list of strings.

The bracket notation works by reading each key as a path. A plain key such as "a" addresses a top-level scalar; "a[b]" nests a value under key "b" of the object at "a"; "a[b][c]" nests one level deeper; and an empty bracket pair "a[]" appends to an array. Parse walks these path segments and materializes them as Go values: scalars are stored as strings, nested objects as map[string]any, and repeated "[]" entries as []any, so the three canonical forms below round-trip through the natural Go types.

Supported forms:

a=1&b=2          -> {"a":"1","b":"2"}
a[b]=1&a[c]=2    -> {"a":{"b":"1","c":"2"}}
a[]=1&a[]=2      -> {"a":["1","2"]}

Keys and values are URL-decoded on parse and URL-encoded on stringify. A leading "?" is ignored by Parse, empty pairs are skipped, and a key with no "=" is treated as having an empty-string value. Stringify is the inverse of Parse for the documented cases: nested maps become bracketed keys, slices become repeated "[]" entries, and both the top-level keys and every nested object's keys are emitted in sorted order so output is deterministic and safe to compare in tests.

This is intentionally a subset of the full npm qs surface. It focuses on the nesting, array, and encoding behavior that Express relies on and does not implement the entire option matrix of the original, such as configurable array-index limits, custom delimiters, dotted-path notation, or charset sentinels. The real qs enforces a default nesting depth of five bracket levels to bound adversarial input; this port applies no such depth cap and will follow bracket segments as deeply as the key string describes, so callers that accept untrusted input should bound key length themselves.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Parse

func Parse(s string) map[string]any

Parse parses a URL query string into a nested map. Scalar values are stored as strings, nested objects as map[string]any, and repeated bracket ("[]") entries as []any. A leading "?" is ignored. Keys and values are URL-decoded.

Example

ExampleParse reconstructs a nested object from bracket notation. The key "a[b]" nests the value under key "b" of the object stored at "a", and "a[c]" adds a sibling, so the two pairs together build a single map. Parse stores scalar leaves as strings and nested objects as map[string]any, decoding each key and value along the way. The result is printed with fmt, which renders maps with their keys in sorted order for a stable rendering. This is the shape Express reconstructs when its extended query parser is enabled.

package main

import (
	"fmt"

	"github.com/malcolmston/express/qs"
)

func main() {
	fmt.Println(qs.Parse("a[b]=1&a[c]=2"))
}
Output:
map[a:map[b:1 c:2]]
Example (Array)

ExampleParse_array shows how repeated empty brackets build a slice. Each "a[]" entry appends to the array stored at "a" in the order the pairs appear, so "a[]=1&a[]=2" yields a two-element slice rather than a map. Array elements are stored as []any and printed by fmt in index order. This mirrors the way HTML forms and Express clients encode a list into a query string. The bracket notation is what lets a flat URL describe a structured value.

package main

import (
	"fmt"

	"github.com/malcolmston/express/qs"
)

func main() {
	fmt.Println(qs.Parse("a[]=1&a[]=2"))
}
Output:
map[a:[1 2]]

func Stringify

func Stringify(m map[string]any) string

Stringify serializes a nested map into a URL query string. Nested maps become bracketed keys, and slices become repeated "[]" entries. Output is sorted by key for deterministic results. Keys and values are URL-encoded.

Example

ExampleStringify serializes a nested map back into bracket notation, the inverse of Parse. Nested maps become bracketed keys such as "a[b]", and both the top-level keys and every nested object's keys are emitted in sorted order so the output is deterministic. Keys and values are URL-encoded, though the bracket characters themselves are written literally. The result here round- trips back to the same structure through Parse. Deterministic ordering makes Stringify output safe to compare directly in tests.

package main

import (
	"fmt"

	"github.com/malcolmston/express/qs"
)

func main() {
	m := map[string]any{"a": map[string]any{"b": "1", "c": "2"}}
	fmt.Println(qs.Stringify(m))
}
Output:
a[b]=1&a[c]=2

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL