2

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.

Stefano Sanfilippo
  • 32,265
  • 7
  • 79
  • 80
  • can you change the structure sent from the server? I mean by using another library and adding type information in the stream. – eugen Jun 05 '13 at 10:22
  • I do have type information in the generated JSON, either as a field (first case), or type token (latter case). – Stefano Sanfilippo Jun 05 '13 at 10:32

2 Answers2

3

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)

Enrichman
  • 11,157
  • 11
  • 67
  • 101
3

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...

MikO
  • 18,243
  • 12
  • 77
  • 109
  • Good answers. I'm accepting this one as it outlines the key point that "null fields are not serialized", and upvoting the other as it provided the key intuition. It's a pity that I could not achieve the same result in a simpler way, maybe some CGlib magic could do it. – Stefano Sanfilippo Jun 06 '13 at 09:18
  • Btw you can choose to serialize or not null fields (at least you can do this with Jackson: http://stackoverflow.com/questions/11757487/how-to-tell-jackson-to-ignore-a-field-only-if-its-value-is-null ). :) – Enrichman Jun 07 '13 at 08:17