2013-05-06 3 views
18

클라이언트가 JSON 형태로 요청을 보내는 일부 서버 코드에서 작업하고 있습니다. 내 문제는 작은 구현 세부 사항에 따라 다양한 요청이있을 수 있다는 것입니다.인터페이스 유형이있는 Gson 사용

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<LoginRequest> { 
    @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 request = parser.fromJson(completeInput, LoginRequest.class);Request request = parser.fromJson(completeInput, Request.class);과 비슷한 것으로 바꾸는 것이 효과가 없습니다.

Gson에 수신 된 JSON에 따라 적절한 유형의 요청을 반환하고 싶습니다.

내가 서버에 전달 된 JSON의 예는 다음과 같습니다

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

가 반복하기 위해, 나는를 구현하는 클래스의 객체를 클라이언트에서 (JSON에서) 요청을 구문 분석하고 반환 할 수있는 방법을 찾고 있어요 Request 인터페이스

+0

서버에서 얻을 수있는 다양한 JSON 응답의 다른 예를 제공해 주실 수 있습니까? 왜냐하면 당신이 많은 가능성과 아주 다른 가능성을 가지고 있지 않다면, 당신이 쉽게 할 수있는 일이 있기 때문입니다 ... – MikO

+0

당신의 의견을 위해 @MiKO를 고맙게 생각합니다. 다른 가능성있는 요청은'PlayRequest','LogoutRequest','GetPlayersRequest','JoinGameRequest','StartGameRequest' 등입니다 ... – fredmanglis

+0

적어도 다른 유형의 요청에 대한 JSON 요청의 예제를 제공 할 수 있다면 . 내'LoginRequest'에 대해서'type','username','password' 등 다른 요청은 어떻게 되나요? 어떻게 보이나요? – MikO

답변

7

, 내 의견에 간단하게 다른 접근 방식을 제안한다.

{ 
    "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 
} 

으로

의 당신이이 3 개 가지 JSON 요청을한다고 가정 해 봅시다 주석 @SerializedName을 사용하여 Gson이 JSON 요청을 구문 분석하려고 할 때 클래스의 각 명명 된 속성에 대해 동일한 이름의 JSON 요청에 필드가 있는지 살펴 봅니다. 해당 입력란이없는 경우 클래스의 속성은 null으로 설정됩니다.

이처럼 만 Request 클래스를 사용하여 다양한 JSON 응답을 구문 분석 할 수있는이 방법 : 당신이 당신의 JSON 요청 클래스로 파싱 한 후

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

, 당신은 에서 데이터를 전송할 수 있습니다 이 방법은 당신이 많은 다른 JSON 요청과 그 requ에있는 경우 좀 더 지루한 얻을 수 있다는

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

참고 : 콘크리트와 같은 XxxxRequest 객체 클래스, 뭔가 에스트리스는 서로 매우 다르지만 그렇다고하더라도 나는 매우 간단하고 좋은 방법이라고 생각합니다.

+0

감사합니다. @MikO. 그러면 'switch-case'구조가 요청 팩토리에 들어갈 수 있다고 생각합니다. 감사. 도움이되었습니다. 저를 살펴 봅시다. – fredmanglis

+0

예, 스위치를'RequestFactory' 클래스에 넣는 것은 확실히 의미가 있습니다. – MikO

0

기본적으로 GSON은 JSON으로 직렬화 된 클래스를 구별 할 수 없습니다. 다시 말해 파서에게 기대하는 클래스를 명시 적으로 알려야합니다.

용액 here 바와 같이 맞춤, 역 직렬화 또는 형 어댑터를 이용 될 수있다.

23

설명 된 유형의 다형 매핑은 Gson에서 특정 수준의 사용자 지정 코딩 없이는 사용할 수 없습니다. 다형성 하위 유형을 어댑터에 미리 지정해야한다는 경고와 함께 찾고있는 기능의 대부분을 제공하는 확장 유형 어댑터 as an extra이 있습니다. 다음은 그 사용의 예입니다

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

참고 실제로 개별 자바 객체에 type 속성을 정의 할 필요가 없습니다 - 그것은 단지 JSON 존재합니다.

+3

'RuntimeTypeAdapterFactory'가 누락 된 사람들은 maven-central에서 사용할 수있는이 [gson-extras] (https://github.com/DanySK/gson-extras)를 사용할 수 있습니다 (프로젝트의 목적은 중앙에서 사용 가능). – Tomask

4

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

유형에 별명을 사용할 수도 있습니다.당신이 가질 수있는 다른 가능한 JSON 요청이 서로 극단적으로 차이가 있다고 가정

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