Stefano Sanfilippo
Stefano Sanfilippo

Reputation: 33056

How to deserialize and return objects from JSON in Java with Gson without knowing the .class a priori?

I am currently working on a thin-client application, where the communication happens through JSON-serialized message objects. Server serializes the message, sends it through a socket, client receives and deserializes. Answers happen in the same way.

First, let's assume the message class are defined both on server and client.

Problem is that Gson::fromJson function needs a .class/type object for deserializing via introspection (understandably), but in my application, multiple type of objects can be received without knowing the .class in advance.

My idea was to create a message wrapper like this:

class MessageWrapper {
    public class MessageWrapper(Object message, MessageType type) {
        this.message = message;
        this.type = type;
    }

    // getters...

    public enum MesssageType {
        PLACEMENT,
        UPDATE,
        // ...
    }

    private final Object message;
    private final MessageType type;
}

Or even go further by determining type param with introspection. This solution is great for serializing (I repeat, that is not a problem), but while deserializing I would get the message type and loose the message itself, at least if I don't parse it twice. Specializing MessageWrapper through Java "templating" mechanism brings us back to the original problem (I would have multiple classes to choose from).

Another idea, was to send a token to identify the message, before the JSON string, like:

Placement={"foo": 2, "bar": "baz"}

Then read the token to determine the .class type. This could work, but there is still a problem: how would I return the value from my receive function? Of course I could do:

public Object receive(Reader stream) {}

And force the user to do a downcast, but I'd rather avoid it.

EDIT: this is because the client has a reactor-like structure: it runs in a loop and dispatches messages to appropriate handlers.

Upvotes: 2

Views: 1351

Answers (2)

MikO
MikO

Reputation: 18751

Using Gson, if you have a reasonable number of possible messages, you could create a class containing all of them (something similar to which @Enrichman wrote, but you don't need to check for nulls...). For example if you have the class

public class Response {
    private Placement placement;
    private Update update;
    //more message types
    //getters & setters
}

Then you can deserialize your response with:

Gson gson = new Gson();
Response response = gson.fromJson(jsonString, Response.class);

And it would deserialize this JSON:

{"placement": {...} }

and this:

{"update": {...} }

and this:

{"placement": {...}, "update": {...} }

Gson will ignore all the fields in the JSON response that don't correspond to any attribute in your class, so you can use a single class to deserialize multiple responses...

Upvotes: 3

Enrichman
Enrichman

Reputation: 11337

EDIT:

Ok, now it's clearer.

What about having a Wrapper with a series of defined Objects? How many handlers do you have?

I mean something like:

class HandlerWrapper {
    private final Placement placement;
    private final Update update;
}

then the client will deserialize this and check for the not-null property:

{"placement":{"foo": 2, "bar": "baz"}, "update":null, ...}

I knwo it's not very nice but I cannot think about something else.. : /


OLD

I don't think this is actually a problem, because you can say in your specification what kind of object you have to expect from that specific case.

For example, if you're calling an API to get a User, you will expect a User.class in the Object. The same for an API to get a Book (or whatelse).

(I hope I understood the question)

Upvotes: 3

Related Questions