I'll offer an alternative. Sometimes it's easier to use a completely different encoding, especially if you're dealing with a variety of systems that don't all handle the details of URL encoding the same way.
Rather than URL-encoding the data, you can base64-encode it. The benefit of this is the encoded data is very generic, consisting only of alpha characters and sometimes trailing =
's. Example:
JSON array-of-strings:
["option", "Fred's dog", "Bill & Trudy", "param=3"]
That data, URL-encoded as the data
param:
"data=%5B%27option%27%2C+%22Fred%27s+dog%22%2C+%27Bill+%26+Trudy%27%2C+%27param%3D3%27%5D"
Same, base64-encoded:
"data=WyJvcHRpb24iLCAiRnJlZCdzIGRvZyIsICJCaWxsICYgVHJ1ZHkiLCAicGFyYW09MyJd"
The base64 approach can be a bit shorter, but more importantly it's simpler. I often have problems moving URL-encoded data between cURL, web browsers and other clients, usually due to quotes, embedded %
signs, shell expansion of wildcards, and so on. Base64 is very neutral and very portable because there are no special characters in its output.
Many systems use this approach. For example in the Kubernetes ecosystem, kubectl edit secret
represents all secrets in base64 to avoid encoding issues and to make non-printable binary data easily copy-and-pastable.