Parsing and creating JSON packets For several of my pet projects, I have had to interpret and/or send JSON WebSocket or HTTP responses in Java. I did some research and picked the library GSON to use. In each project, I have implemented a custom deserializer in order to correctly interpret different packet types.

For example:


{"type": "set-name", "data":{"name":"daniel"}}
{"type": "set-timeperiod", "data":{"start":"2017-03-16", "end":"2017-03-17"}}



In this case I would use two classes, SetName and SetTimePeriod:


public class SetName implements Data {
    private String name;

    @Override
    public void handle(Foo foo) throws InvalidPacketException {
        if(name == null) throw new InvalidPacketException();
        foo.doThingWithName(name);
    }

    @Override
    public String getType() { return "set-name"; }
}



Foo represents any arbitrary resources, the packet might need to perform its function. The data interface is fairly straightforward:

public interface Data {
    void handle(Foo foo);
    String getType();
}


The Data objects are wrapped in a Packet representing the whole thing:

public void Packet {
    private String type;
    private Data data;

    public Packet(Data data) {
        this.data = data;
        type = data.getType();
    }

    public void handle(Foo foo) throws InvalidPacketException { data.handle(foo); }

    public Packet initData(Data data) {
        if(this.data != null) throw new IllegalStateException("Data already initialised.");
        this.data = data;
        return this;
    }
}


And finally, the packet would be converted from raw JSON to an instance of Packet using a custom Deserializer.

public class PacketDeserializer implements JsonDeserializer&lt;Packet&gt; {

    @Override
    public Packet deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();
        // Remove data object, GSON won't know what to turn it into.
        JsonObject dataObject = jsonObject.remove("data").getAsJsonObject();
        // Determine what type of data object we need.
        Class dataClass = typeToClass(jsonObject.get("type").getAsString());
        // Construct and return a Packet of the correct type.
        Data data = context.deserialize(dataObject, dataClass);
        return new Gson().fromJson(jsonObject, Packet.class).initData(data);
    }

    private Class&lt;? extends Data&gt; typeToClass(String type) throws JsonParseException {
        switch(type) {
            case("set-name"): return SetName.class;
            case("set-timeperiod"): return SetTimePeriod.class;
            default: throw new JsonParseException("Type " + type + " is invalid.");
        }
    }
}


This means GSON will now deserialize the object into a Packet and then call the handle() to do whatever needs to be done with that packet:

Packet packet = new GsonBuilder()
    .registerTypeAdapter(Packet.class, new PacketDeserializer())
    .create()
    .fromJson(message, Packet.class);
packet.handle(foo);


In addition, a Packet instance can be converted to JSON without the need for any custom serializer. Once this structure is built, it is very easy to add new packet types or to send/parse packets.

This seems like an elegant solution to me, but I have some concerns:


Is it actually efficient? Would it be much faster to parse the JSON directly?
I can end up with a lot of subclasses of Data which makes me feel like there could be a better way of doing it
If this is a good solution, would it be a good thing to use wherever I'm parsing JSON with some object with varying contents like data?


I came up with this solution years ago, when I was fairly new to programming and haven't changed it much at all, so I feel like I must be doing something wrong.
A quick braindump:


PacketDeserializer#deserialize  is overcommented. Most comments restate what the code says. Try commenting as little as possible, but as much as necessary.
PacketDeserializer could benefit from not hardcoding the mapping between the type strings and classes. Consider passing a Map&lt;String, Class&lt;? extends Data&gt;&gt; as constructor parameter and replacing typeToClass with a map.get
Lastly you should also avoid rawtypes. Class without generics specification is a rawtype.


Continuing that thought, you really want a different name than Foo for the "PacketHandler" you pass in to Packet#handle. You should consider going so far as to extract the responsibility of handling the package into a separate class. A Packet should not know how to handle itself. It's originally just a data-holder, nothing intelligent :)