Simply trying to serialize it is really the only way from a
correctness perspective.
Objects can have .toJSON() methods (IOW, can execute arbitrary code
during serialization) so there is really no way to know what the
serialized object graph looks like until you try it.
By the way, all the negative cases you mention are in fact valid
inputs, they all produce an output when you pass them to
JSON.stringify().