使用Gson和接口类型

我正在研究一些服务器代码,客户端以JSON的forms发送请求。 我的问题是,有许多可能的请求,所有请求都在小的实现细节中有所不同。 因此我想使用Request接口,定义如下:

public interface Request { Response process ( ); } 

从那里,我在名为LoginRequest的类中实现了接口,如下所示:

 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 + "]"; } } 

为了使用JSON,我创建了一个GsonBuilder实例并注册了一个InstanceCreator ,如下所示:

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

然后我使用,如下面的代码段所示:

 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); 

我得到了预期的输出。

我想要做的是替换Request request = parser.fromJson(completeInput, LoginRequest.class); 类似于Request request = parser.fromJson(completeInput, Request.class); 但这样做是行不通的,因为Request是一个接口。

我希望我的Gson根据收到的JSON返回适当类型的请求。

我传递给服务器的JSON示例如下所示:

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

重申一下,我正在寻找一种方法来解析客户端的请求(In JSON)并返回实现Request接口的类的对象

假设您可能拥有的不同可能的JSON请求彼此之间没有太大的不同,我建议采用不同的方法,在我看来更简单。

假设您有以下3种不同的JSON请求:

 { "type":"LOGIN", "username":"someuser", "password":"somepass" } //////////////////////////////// { "type":"SOMEREQUEST", "param1":"someValue", "param2":"someValue" } //////////////////////////////// { "type":"OTHERREQUEST", "param3":"someValue" } 

Gson允许您使用单个类来包装所有可能的响应,如下所示:

 public class Request { @SerializedName("type") private String type; @SerializedName("username") private String username; @SerializedName("password") private String password; @SerializedName("param1") private String param1; @SerializedName("param2") private String param2; @SerializedName("param3") private String param3; //getters & setters } 

通过使用注释@SerializedName ,当Gson尝试解析JSON请求时,它只是查看类中每个命名属性,如果JSON请求中有一个具有相同名称的字段。 如果没有这样的字段,则类中的属性仅设置为null

这样,您只需使用Request类就可以解析许多不同的JSON响应,如下所示:

 Gson gson = new Gson(); Request request = gson.fromJson(jsonString, Request.class); 

一旦将JSON请求解析到您的类中,您就可以将数据从wrap类传输到具体的XxxxRequest对象,例如:

 switch (request.getType()) { case "LOGIN": LoginRequest req = new LoginRequest(request.getUsername(), request.getPassword()); break; case "SOMEREQUEST": SomeRequest req = new SomeRequest(request.getParam1(), request.getParam2()); break; case "OTHERREQUEST": OtherRequest req = new OtherRequest(request.getParam3()); break; } 

请注意,如果您有许多不同的JSON请求并且这些请求彼此非常不同,这种方法会变得有点繁琐,但即便如此,我认为这是一种很好且非常简单的方法……

如果没有某种程度的自定义编码,Gson中没有所描述类型的多态映射。 有一个扩展类型适配器作为额外提供,提供了您正在寻找的大部分function,需要注意的是多态子类型需要提前向适配器声明。 以下是其使用示例:

 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> requestListTypeToken = new TypeToken>() { }; final RuntimeTypeAdapterFactory typeFactory = RuntimeTypeAdapterFactory .of(Request.class, "type") .registerSubtype(LoginRequest.class) .registerSubtype(PingRequest.class); final Gson gson = new GsonBuilder().registerTypeAdapterFactory( typeFactory).create(); final List 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 deserializedRequestList = gson.fromJson(serialized, requestListTypeToken.getType()); System.out.println("Deserialized list: " + deserializedRequestList); } } 

请注意,您实际上不需要在单个Java对象上定义type属性 – 它仅存在于JSON中。

Genson库默认提供对多态类型的支持。 以下是它的工作原理:

 // tell genson to enable polymorphic types support Genson genson = new Genson.Builder().setWithClassMetadata(true).create(); // json value will be {"@class":"mypackage.LoginRequest", ... other properties ...} String json = genson.serialize(someRequest); // the value of @class property will be used to detect that the concrete type is LoginRequest Request request = genson.deserialize(json, Request.class); 

您还可以为类型使用别名。

 // a better way to achieve the same thing would be to use an alias // no need to use setWithClassMetadata(true) as when you add an alias Genson // will automatically enable the class metadata mechanism genson = new Genson.Builder().addAlias("loginRequest", LoginRequest.class).create(); // output is {"@class":"loginRequest", ... other properties ...} genson.serialize(someRequest); 

默认情况下,GSON无法区分序列化为JSON的类; 换句话说,您需要明确告诉解析器您期望的是哪个类。

解决方案可以是自定义反序列化或使用类型适配器 ,如此处所述。