2013-05-06 11 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 = parser.fromJson(completeInput, Request.class);と似たものに置き換えることですが、それは動作しません。Requestはインターフェイスなので、これは動作しません。

受信したJSONに応じて、Gsonに適切なタイプのリクエストを返すようにします。私は、サーバーに渡されたJSONの例を以下に示し

:繰り返しに

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

、私はクライアントから(JSONで)要求を解析し、実装するクラスのオブジェクトを返す方法を探していますRequestインターフェイス

+0

サーバーから取得できるさまざまなJSONレスポンスの他の例を教えてください。なぜなら、あなたは多くの非常に異なる可能性を持っていないと、あなたが簡単にできることがあるからです。 – MikO

+0

あなたの入力に感謝@MiKO。他の可能性のあるリクエストは 'PlayRequest'、' LogoutRequest'、 'GetPlayersRequest'、' JoinGameRequest'、 'StartGameRequest'などです... – fredmanglis

+0

あなたは、他のタイプのリクエストの少なくとも1つに対して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); 
    } 
} 

注意あなたが実際に個々のJavaオブジェクトにtypeプロパティを定義する必要はありません - それだけでJSONに存在します。

+3

'RuntimeTypeAdapterFactory'が不足している人には、maven-centralで利用可能なこの[gson-extras](https://github.com/DanySK/gson-extras)を使うことができます(このプロジェクトの目的は、 Maven-Centralで利用可能)。 – 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); 
関連する問題