简体   繁体   中英

Retrofit2 Specify root element and convert datatype

I've just started working with Retrofit2 and the API I'm consuming wraps all valid responses in a "response" object as shown below. I need to tell Retrofit to parse only the values within response without actually nesting them inside another object. For the login code, I'm also faced with the issue of getting a String which I want to convert to an actual time stamp.

This is a sample response from a login request:

{  
   "status":"success",
   "response":{  
      "token":"test_token",
      "expires":"1485217863"
   }
}

In the above the only two actual values are:

token
expires 

I'm hoping to end up with something like what is shown below.

public class Token {

  @SerializedName("token")
  String token;

  @SerializedName("expires")
  Timestamp expires;

  public User(String token, String expires ) {
    this.token
    this.expires = //conversion code omitted. 
  }
}

You have a couple of options here. You can either use a custom serialiser/deserialiser, type adapters, or you can simply use pojos and unwrap the result yourself.

Let me start with the easiest solution I can think of. Picture you have these classes:

public class ResponseData<T> {
    @SerializedName("status")
    @Expose
    String status;
    @SerializedName("response")
    @Expose
    T response;

    public T getResponse() {
      return response;
    }
    // getters and setters and friends
}

public class Token {
    @SerializedName("token")
    @Expose
    String token;

    @SerializedName("expires")
    @Expose
    Timestamp expires;

    public Token(String token, String expires) {
        this.token = token;
        this.expires = expires;
    }
}

So one first thing to notice is the use of @Expose . This is a nice to have, but not extremely necessary. It helps you out when you have custom serialisers.

I assumed that you can have multiple api endpoints that return the same kind of body, where the json is:

{  
 "status":"success",
 "response":{  
  // Can be anything
 }
}

And as you can see the response can be anything.

You can then make your retrofit calls return ResponseData<Token> and in your callbacks you can check the value of status and see if you can do getResponse to unpack the result. The advantage of this approach is that you can reuse ResponseData fairly easily.

Another approach is to use custom serialisers or type adapters. This is in my opinion more laborious, but still a valid approach. I think the answer here is quite extensive and explains how you can do this to get the nested object inside response .

To prepare retrofit to use the type adapters, you'll need to inject a configured Gson instance into it. Here's how:

Gson gson = new GsonBuilder()
    .registerTypeAdapter(Token.class, new YourTypeAdapter())
    .create();

Retrofit retrofit = new Retrofit.Builder()
   .addConverterFactory(GsonConverterFactory.create(gson))
   // ....

As you can see, we pass the created gson with your type adapter to the GsonConverterFactory used by retrofit. This prepares retrofit to serialise and deserialise Token objects using the given type adapter.

I think the main disadvantage with this approach is that if you want to write a generic deserialiser/serialiser/typeadapter it can become complicated quite fast (assuming you won't have only a Token object).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM