I have a netty decoder which uses GSon to convert JSon coming from web client to appropriate java objects.
The requirement is:
Client could be sending unrelated classes, Class A, Class B, Class C etc but I would like to use the same singleton decoder instance in the pipeline to do conversion(since I use spring for configuring it). The issue I am facing is I need to know the class
object before hand.
public Object decode()
{
gson.fromJson(jsonString, A.class);
}
This cannot decode B or C. The users of my library, now need to write separate decoders for each class instead of a cast later down the line. The only way I can see for doing this is to pass the String name of the class say "org.example.C" in the JSon string from web client, parse it out in the decoder and then use Class.forName
to get the class. Is there a better way to do this?
GSon MUST know the class matching the json string. If you don't wan't to provide it with fromJson(), you can actually specify it in the Json. A way is to define an interface and bind an adapter on it.
Like :
class A implements MyInterface {
// ...
}
public Object decode()
{
Gson gson = builder.registerTypeAdapter(MyInterface.class, new MyInterfaceAdapter());
MyInterface a = gson.fromJson(jsonString, MyInterface.class);
}
Adapter can be like :
public final class MYInterfaceAdapter implements JsonDeserializer<MyInterface>, JsonSerializer<MyInterface> {
private static final String PROP_NAME = "myClass";
@Override
public MyInterface deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
String classPath = json.getAsJsonObject().getAsJsonPrimitive(PROP_NAME).getAsString();
Class<MyInterface> cls = (Class<MyInterface>) Class.forName(classPath);
return (MyInterface) context.deserialize(json, cls);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
@Override
public JsonElement serialize(MyInterface src, Type typeOfSrc, JsonSerializationContext context) {
// note : won't work, you must delegate this
JsonObject jo = context.serialize(src).getAsJsonObject();
String classPath = src.getClass().getName();
jo.add(PROP_NAME, new JsonPrimitive(classPath));
return jo;
}
}