This costs a little in performance, since the compiler can't inline trivial functions like tns_get_null(). But, it will make it much easier to provide slightly different parsing/rending strategies e.g. automatically turning unicode strings into utf8 bytes.
This is what the stdlib json module uses, and it seems appropriate. Custom error classes don't really buy us anything.
dump() is just a simpler wrapper around dumps(), while load() does some extra work to ensure it only reads as much data as it needs.