Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Gson with Interface Types

Tags:

java

json

gson

I am working on some server code, where the client sends requests in form of JSON. My problem is, there are a number of possible requests, all varying in small implementation details. I therefore thought to use a Request interface, defined as:

public interface Request {     Response process ( ); } 

From there, I implemented the interface in a class named LoginRequest as shown:

public class LoginRequest implements Request {     private String type = "LOGIN";     private String username;     private String password;      public LoginRequest(String username, String password) {         this.username = username;         this.password = password;     }      public String getType() {         return type;     }     public void setType(String type) {         this.type = type;     }     public String getUsername() {         return username;     }     public void setUsername(String username) {         this.username = username;     }     public String getPassword() {         return password;     }     public void setPassword(String password) {         this.password = password;     }      /**      * This method is what actually runs the login process, returning an      * appropriate response depending on the outcome of the process.      */     @Override     public Response process() {         // TODO: Authenticate the user - Does username/password combo exist         // TODO: If the user details are ok, create the Player and add to list of available players         // TODO: Return a response indicating success or failure of the authentication         return null;     }      @Override     public String toString() {         return "LoginRequest [type=" + type + ", username=" + username             + ", password=" + password + "]";     } } 

To work with JSON, I created a GsonBuilder instance and registered an InstanceCreator as shown:

public class LoginRequestCreator implements InstanceCreator<LoginRequest> {     @Override     public LoginRequest createInstance(Type arg0) {         return new LoginRequest("username", "password");     } } 

which I then used as shown in the snippet below:

GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(LoginRequest.class, new LoginRequestCreator()); Gson parser = builder.create(); Request request = parser.fromJson(completeInput, LoginRequest.class); System.out.println(request); 

and I get the expected output.

The thing I wish to do is replace the line Request request = parser.fromJson(completeInput, LoginRequest.class); with something similar to Request request = parser.fromJson(completeInput, Request.class); but doing that will not work, since Request is an interface.

I want my Gson to return the appropriate type of request depending on the received JSON.

An example of the JSON I passed to the server is shown below:

{     "type":"LOGIN",     "username":"someuser",     "password":"somepass" } 

To reiterate, I am looking for a way to parse requests (In JSON) from clients and return objects of classes implementing the Request interface

like image 630
fredmanglis Avatar asked May 06 '13 10:05

fredmanglis


People also ask

Is GSON better than JSON?

GSON can use the Object definition to directly create an object of the desired type. While JSONObject needs to be parsed manually.

Does GSON offer parse () function?

The GSON JsonParser class can parse a JSON string or stream into a tree structure of Java objects. GSON also has two other parsers. The Gson JSON parser which can parse JSON into Java objects, and the JsonReader which can parse a JSON string or stream into tokens (a pull parser).

Is GSON toJson thread safe?

Gson is typically used by first constructing a Gson instance and then invoking toJson(Object) or fromJson(String, Class) methods on it. Gson instances are Thread-safe so you can reuse them freely across multiple threads.

What does GSON toJson do?

Gson is the main actor class of Google Gson library. It provides functionalities to convert Java objects to matching JSON constructs and vice versa. Gson is first constructed using GsonBuilder and then toJson(Object) or fromJson(String, Class) methods are used to read/write JSON constructs.


1 Answers

Polymorphic mapping of the type described is not available in Gson without some level of custom coding. There is an extension type adapter available as an extra that provides a bulk of the functionality you are looking for, with the caveat that the polymorphic sub-types need to be declared to the adapter ahead of time. Here is an example of its use:

public interface Response {}  public interface Request {     public Response process(); }  public class LoginRequest implements Request {     private String userName;     private String password;      // Constructors, getters/setters, overrides }  public class PingRequest implements Request {     private String host;     private Integer attempts;      // Constructors, getters/setters, overrides }  public class RequestTest {      @Test     public void testPolymorphicSerializeDeserializeWithGSON() throws Exception {         final TypeToken<List<Request>> requestListTypeToken = new TypeToken<List<Request>>() {         };          final RuntimeTypeAdapterFactory<Request> typeFactory = RuntimeTypeAdapterFactory                 .of(Request.class, "type")                 .registerSubtype(LoginRequest.class)                 .registerSubtype(PingRequest.class);          final Gson gson = new GsonBuilder().registerTypeAdapterFactory(                 typeFactory).create();          final List<Request> requestList = Arrays.asList(new LoginRequest(                 "bob.villa", "passw0rd"), new LoginRequest("nantucket.jones",                 "crabdip"), new PingRequest("example.com", 5));          final String serialized = gson.toJson(requestList,                 requestListTypeToken.getType());         System.out.println("Original List: " + requestList);         System.out.println("Serialized JSON: " + serialized);          final List<Request> deserializedRequestList = gson.fromJson(serialized,                 requestListTypeToken.getType());          System.out.println("Deserialized list: " + deserializedRequestList);     } } 

Note that you don't actually need to define the type property on the individual Java objects - it exists only in the JSON.

like image 174
Perception Avatar answered Sep 27 '22 18:09

Perception