@@ -49,6 +49,96 @@ A simple usage example:
4949 # Decoding
5050 assert qs.decode(' a=b' ) == {' a' : ' b' }
5151
52+ Compared with ``urllib.parse ``
53+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54+
55+ The standard library's
56+ `urlencode <https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlencode >`__,
57+ `parse_qs <https://docs.python.org/3/library/urllib.parse.html#urllib.parse.parse_qs >`__,
58+ and
59+ `parse_qsl <https://docs.python.org/3/library/urllib.parse.html#urllib.parse.parse_qsl >`__
60+ are designed for conventional flat ``application/x-www-form-urlencoded ``
61+ data. Use ``qs_codec `` when the query represents nested dictionaries or lists,
62+ must interoperate with Node ``qs ``, or needs configurable list, duplicate, null,
63+ or resource-limit behavior.
64+
65+ ``urlencode `` can expand a flat sequence into repeated keys with
66+ ``doseq=True ``, which corresponds to ``ListFormat.REPEAT ``. It does not
67+ recursively encode nested mappings; ``qs.encode `` uses bracket or dot paths
68+ instead:
69+
70+ .. code :: python
71+
72+ from urllib.parse import urlencode
73+
74+ import qs_codec as qs
75+
76+ assert urlencode({' tags' : [' a' , ' b' ]}, doseq = True ) == ' tags=a&tags=b'
77+ assert qs.encode(
78+ {' tags' : [' a' , ' b' ]},
79+ qs.EncodeOptions(list_format = qs.ListFormat.REPEAT ),
80+ ) == ' tags=a&tags=b'
81+ assert qs.encode(
82+ {' filter' : {' name' : ' Jane' }},
83+ ) == ' filter%5Bname%5D=Jane'
84+
85+ The encoding defaults also differ: ``urlencode `` emits spaces as ``+ `` and
86+ uses Python scalar spellings such as ``True `` and ``None ``; ``qs.encode `` uses
87+ ``%20 ``, lowercase booleans, and an empty value for ``None `` by default.
88+
89+ On decode, ``parse_qs `` returns a dictionary whose values are always lists,
90+ while ``parse_qsl `` returns an ordered list of name/value pairs and preserves
91+ interleaved duplicate keys. Both treat bracket expressions as literal key
92+ names, drop blank values unless ``keep_blank_values=True ``, and collapse a
93+ name-only token and an explicit empty value to the same empty string.
94+ ``qs.decode `` normally returns a scalar for one value, reconstructs bracket
95+ paths, and can preserve that null distinction:
96+
97+ .. code :: python
98+
99+ from urllib.parse import parse_qs, parse_qsl
100+
101+ import qs_codec as qs
102+
103+ query = ' a=1&b=2&a=3&filter%5Bname%5D=Jane&flag&empty='
104+
105+ assert parse_qs(query, keep_blank_values = True ) == {
106+ ' a' : [' 1' , ' 3' ],
107+ ' b' : [' 2' ],
108+ ' filter[name]' : [' Jane' ],
109+ ' flag' : [' ' ],
110+ ' empty' : [' ' ],
111+ }
112+ assert parse_qsl(query, keep_blank_values = True ) == [
113+ (' a' , ' 1' ),
114+ (' b' , ' 2' ),
115+ (' a' , ' 3' ),
116+ (' filter[name]' , ' Jane' ),
117+ (' flag' , ' ' ),
118+ (' empty' , ' ' ),
119+ ]
120+ assert qs.decode(
121+ query,
122+ qs.DecodeOptions(strict_null_handling = True ),
123+ ) == {
124+ ' a' : [' 1' , ' 3' ],
125+ ' b' : ' 2' ,
126+ ' filter' : {' name' : ' Jane' },
127+ ' flag' : None ,
128+ ' empty' : ' ' ,
129+ }
130+
131+ All three decoders leave primitive values as strings. ``parse_qs `` and
132+ ``qs.decode `` combine repeated flat keys under their default behavior;
133+ ``parse_qsl `` instead retains each pair in input order. The standard-library
134+ parsers offer ``max_num_fields ``; ``qs.decode `` additionally provides default
135+ parameter, nesting-depth, and list limits plus configurable duplicate handling.
136+
137+ Use ``parse_qsl `` when flat pair order or duplicate interleaving matters, but
138+ not as a raw-query round-trip format: it percent-decodes names and values,
139+ normalizes ``+ `` and ``%20 `` to the same space, and cannot retain the distinction
140+ between a name-only token and an explicit empty value.
141+
52142Working with URLs
53143~~~~~~~~~~~~~~~~~
54144
0 commit comments