i ran into this issue ages back and forgot about it and anyway just went ahead and created my own custom marshal/unmarshal API that was designed to append output to provided slices, so they could be spliced together such as a simple array encoder that spews its results onto a buffer with multiple other encoders with the same API.
you can't do that with the standard json.Marshal/Unmarshal anyway. in fact the tag codec i wrote, each tag must be constructed using this method to mesh with the custom parser state machine i made (this mainly affects unmarshaling) but underneath the level of implementing the UnmarshalJSON (and MarshalJSON) it already bypasses these things, and i can trivially implement these anyhow except of course using them on tags that contain these characters will not work with json.Marshal anyway.
oof. the main encoder available without further dependencies in Go has a serious flaw that impedes usage for nostr. this is why you will notice that all of the #golang #nostr relays use some kind of custom json encoder.
ah well, it's not so bad. this is just one of the unfortunate issues that preventing user generated XSS attacks in json sent to a Go server that renders the json into a browser due to FUCKING RETARDED BROWSERS AND THEIR ABSURD COMPLEXITY) so whatever. you can't use json.Marshal with nostr. the end. and they all lived happily ever after.