I know that I sound like a broken record, but this is
exactly the issue which canonical S-expressions were designed for, and which SPKI wrestled with & solved twenty years ago.
The SPKI version of a message would look something like (I've removed the hash property, because I don't think it makes sense for an object to specify the hash to be used to refer to it, but one could add it back in if one wished):
(message
(previous (hash sha256 |XphMUkWQtomKjXQvFGfsGYpt69sgEY7Y4Vou9cEuJho=|))
(author (public-key (ed25519 |FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY=|)))
(sequence 216)
(timestamp "2018-04-19T17:53:26Z")
(content (type vote)
(link (hash sha256 |DlBH/hCmXfVzks2uY+WIll4aTzxrfBA8m/3GIdX3Vew=|))
(value 1)))
The transport version would be this (you can Base64-decode it to see the canonical version):
{KDc6bWVzc2FnZSg4OnByZXZpb3VzKDQ6aGFzaDY6c2hhMjU2MzI6XphMUkWQtomKjXQvFGfsGYpt
69sgEY7Y4Vou9cEuJhopKSg2OmF1dGhvcigxMDpwdWJsaWMta2V5KDc6ZWQyNTUxOTMyOhQl/7bA
y6bmwjyinyK8OIHPkkJB3Gg9e7OxiOov84lmKSkpKDg6c2VxdWVuY2UzOjIxNikoOTp0aW1lc3Rh
bXAyMDoyMDE4LTA0LTE5VDE3OjUzOjI2WikoNzpjb250ZW50KDQ6dHlwZTQ6dm90ZSkoNDpsaW5r
KDQ6aGFzaDY6c2hhMjU2MzI6DlBH/hCmXfVzks2uY+WIll4aTzxrfBA8m/3GIdX3VewpKSg1OnZh
bHVlMToxKSkp}
And a signed message might look something like:
(sequence
(message
(previous (hash sha256 |XphMUkWQtomKjXQvFGfsGYpt69sgEY7Y4Vou9cEuJho=|))
(author (public-key (ed25519 |FCX/tsDLpubCPKKfIrw4gc+SQkHcaD17s7GI6i/ziWY=|)))
(sequence 216)
(timestamp "2018-04-19T17:53:26Z")
(content (type vote)
(link (hash sha256 |DlBH/hCmXfVzks2uY+WIll4aTzxrfBA8m/3GIdX3Vew=|))
(value 1)))
(signature (hash sha256 |XphMUkWQtomKjXQvFGfsGYpt69sgEY7Y4Vou9cEuJho=|)
(hash sha256 |5hHMWc1PqfrwFVfALci5JXCWqW7VC4I4iS4+Utvr44w=|)
|z7W1ERg9UYZjNfE72ZwEuJF79khG+eOHWFp6iF+KLuSrw8Lqa6IousK4cCn9T5qFa8E14GVek4cAMmMbjqDnAg==|))
Canonical S-expressions already buy you bit-for-bit identity when hashing, and SPKI (as an example) wraps signatures rather than injecting them — the only sane choice.
Why do I bring this up? Obviously it's possible to make JSON be a cryptographically-sound format (either by foregoing objects for arrays, or by rules around object-field ordering, along with other rules about encoding), but using it instead of an already-sound format indicates an unfamiliarity with prior work in the field.