This is pretty hard to do in a language that supports both inheritance and generic types. Suppose you have a field of type List<Foo>. What values could the server possibly send the client?
- All subtypes of List: ArrayList, LinkedList. Also, ImmutableList and a bunch of other subtypes if Guava is in the classpath.
- All the fields in Foo, its serializable supertypes, and all its subtypes. And then do the same calculation again for those types, recursively.
Or if Foo is a widely used marker interface, anything implementing that marker interface in any library could possibly be sent. At this point, probably nobody working on the app has a good idea on why a particular type is included in the app.
Of course you could tell people to tighten up their type declarations. For example, never use List in a DTO; instead use ArrayList. This doesn't work because it just takes one field declaration in one library to screw it up, so it's hardly worth doing.
And I haven't even gotten into the complexities of things like:
class Foo<T extends Something> extends Bar<T>.
I heard a rumor once that someone proved that doing the calculation "properly" was NP-complete. In practice, we use approximations and there are bugs, though mostly people don't run into them too often.
I think it makes more sense to generate serialization code from an IDL such as a protobuf declaration. Since there's no inheritance and no generics, it's clearer to everyone which fields can possibly be transmitted on the wire and what the semantics are for forwards and backwards compatibility. Or just write out the JSON serialization for each class; it's not that hard, just repetitive.