Sqlite's JSONB keeps numbers as strings, which is slower but preserves your weird JSON (since there's no such thing as standard JSON). I'm not sure about duplicate keys.
I-JSON is the most sensible JSON profile I know of: https://datatracker.ietf.org/doc/html/rfc7493. It says: UTF-8 only, prefer not to use numbers beyond IEEE 754-2008 binary64 precision, no duplicate keys, and a couple more things.
You would probably be string quoting your numbers in your data/model if this matters to you so sounds like the right call from PG implementation.
If key order has to be preserved then a blob type would be a better fit, then you're guaranteed to get back what you wrote.
For example, SQLite says it stores JSON as regular text but MySQL converts it to an internal representation[2], so if you migrate you might be in trouble.
[1]: https://ecma-international.org/publications-and-standards/st...
>An object is an unordered set of name/value pairs.
# select '{ "id": "\u0000" }'::json;
-> { "id": "\u0000" }
# select '{ "id": "\u0000" }'::jsonb;
-> ERROR: unsupported Unicode escape sequence
Once you accept this then it stops being a problem.
EDIT: https://sqlite.org/draft/jsonb.html does say that they are different.