J'ai créé un simple point de terminaison REST:
http://<server_address>:3000/sizes
Cette URL renvoie une réponse très simple contenant un tableau json comme suit:
[
{ "id": 1, "name": "Small", "active": true },
{ "id": 2, "name": "Medium", "active": true },
{ "id": 3, "name": "Large", "active": true }
]
Maintenant, j'essaie de consommer cette réponse en utilisant Retrofit 2 avec GSON.
J'ai ajouté un modèle:
@lombok.AllArgsConstructor
@lombok.EqualsAndHashCode
@lombok.ToString
public class Size {
private int id;
private String name;
private boolean active;
@SerializedName("created_at")
private String createdAt;
@SerializedName("updated_at")
private String updatedAt;
}
Et service:
public interface Service {
@GET("sizes")
Call<List<Size>> loadSizes();
}
J'ai instancié une rénovation:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://<server_address>:3000")
.addConverterFactory(GsonConverterFactory.create())
.build();
Et mon service:
Service service = retrofit.create(Service.class);
Maintenant, essayons d'appeler les données:
service.loadSizes().enqueue(new Callback<List<Size>>() {
@Override
public void onResponse(Call<List<Size>> call, Response<List<Size>> response) {
for(Size size: response.body()) {
System.out.println(size.toString());
}
}
@Override
public void onFailure(Call<List<Size>> call, Throwable t) {
System.out.println(t.getMessage());
}
});
Ce qui se termine par une exception:
Java.lang.IllegalStateException: BEGIN_OBJECT attendu mais était STRING à la ligne 1 colonne 18 chemin $ [0] .name
Je suppose que l'erreur est causée par cela, le l'API REST renvoie une réponse qui est un tableau ni un objet.
le service REST ne peut pas être modifié, donc la réponse doit rester telle quelle.
De plus, la désérialisation du json ci-dessus à l'aide de GSON pur peut être effectuée par:
Type sizesType = new TypeToken<List<Size>>(){}.getType();
List<Size> size = new Gson().fromJson(json, sizesType);
Mais je n'ai aucune idée de comment faire Retrofit 2 pour l'utiliser.
Merci d'avance.
Le fait drôle est ... Mon code est parfaitement bien. Au moins celui présenté dans la question ci-dessus.
J'ai fini par supprimer une ligne de mon modèle Size
.
Comme je me suis concentré sur le code lui-même (en particulier la configuration de Retrofit), j'ai totalement ignoré les importations.
Il s'est avéré - lors de l'implémentation du modèle Size
lorsque j'ai commencé à taper la classe String
pour les champs du modèle:
name
createdAt
updatedAt
La complétion de code IntelliJ IDEA m'a suggéré
Java.lang.String
com.Sun.org.Apache.xpath.internal.operations.String
ce qui a totalement gâché la désérialisation de Gson
.
En ce qui concerne les récompenses ...
J'ai décidé de marquer comme valide ma propre réponse. Pourquoi?
Un grand merci aux messieurs ci-dessus pour leurs excellents services.
Comme je n'ai qu'une seule prime, j'ai décidé de récompenser xiaoyaoworm car son code correspond mieux à mes besoins (je ne l'ai pas écrit dans ma question mais l'idée d'écrire un service aussi simple - comme je l'ai présenté dans ma question - est de se cacher des détails d'implémentation de l'utilisateur final et de ne pas utiliser JsonArray
et autres comme dans BNK réponse).
Le seul problème avec xiaoyaoworm est que, il a suggéré que le modèle Size
ne nécessite aucune annotation ce qui est totalement faux pour l'exemple JSON cité.
Pour le cas ci-dessus, exactement deux champs du modèle Size
nécessitent des annotations - created_at
et updated_at
.
J'ai même testé quelques versions du converter-gson
bibliothèque (j'ai vu xiaoyaoworm avoir utilisé autre chose que moi) - cela n'a rien changé. Des annotations étaient nécessaires.
Sinon - encore une fois, merci beaucoup!
Récemment, je viens de terminer un projet lié à retrofit2. Sur la base de ma source, je copie tous vos trucs dans mon projet pour essayer, en faisant quelques changements mineurs, cela fonctionne bien de mon côté.
Dans votre build.gradle, ajoutez ceux-ci:
compile 'com.squareup.retrofit2:retrofit:2.0.1'
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.okhttp3:okhttp:3.1.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'
Modèle: ( MISE À JOUR: Suivez le cas de tommus, createdAt et updatedAt est maintenant affiché dans son exemple de réponse json, ces deux valeurs doivent être annotées car le nom dans le modèle est différent de json respone )
public class Size {
private int id;
private String name;
private boolean active;
@SerializedName("created_at")
private String createdAt;
@SerializedName("updated_at")
private String updatedAt;
}
Service: (Exactement le même que ce que vous avez)
public interface service {
@GET("sizes")
Call<List<Size>> loadSizes();
}
RestClient: ( J'ajoute un journal ici, afin que vous puissiez voir toutes les informations sur les demandes et les réponses, faites attention de ne pas utiliser Localhost mais l'adresse IP de votre serveur dans l'URL )
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.xiaoyaoworm.prolificlibrary.test.Service;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RestClient {
private static Service service;
public static Service getClient() {
if (service == null) {
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();
// Add logging into retrofit 2.0
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.interceptors().add(logging);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://YOURSERVERIPNOTLOCALHOST:3000/")
.addConverterFactory(GsonConverterFactory.create(gson))
.client(httpClient.build()).build();
service = retrofit.create(Service.class);
}
return service;
}
}
Sur votre activité, ajoutez cette fonction pour exécuter votre code: ( exactement comme ce que vous avez fait. La réponse sera votre liste de taille )
private void loadSize() {
Service serviceAPI = RestClient.getClient();
Call<List<Size>> loadSizeCall = serviceAPI.loadSizes();
loadSizeCall.enqueue(new Callback<List<Size>>() {
@Override
public void onResponse(Call<List<Size>> call, Response<List<Size>> response) {
for(Size size: response.body()) {
System.out.println(size.toString());
}
}
@Override
public void onFailure(Call<List<Size>> call, Throwable t) {
System.out.println(t.getMessage());
}
});
}
En exécutant cela, vous verrez les informations que vous souhaitez imprimer:
Voici mon dépôt github que j'utilise retrofit2.0 pour faire simple GET POST PUT DELETE fonctionne. Vous pouvez l'utiliser comme référence. Mon repo Github retrofit2.
Veuillez utiliser ce qui suit:
fichier build.gradle:
dependencies {
...
compile 'com.squareup.retrofit2:retrofit:2.0.1'
compile 'com.squareup.retrofit2:converter-gson:2.0.1'
compile 'com.google.code.gson:gson:2.6.2'
}
WebAPIService.Java:
public interface WebAPIService {
@GET("/json.txt") // I use a simple json file to get the JSON Array as yours
Call<JsonArray> readJsonArray();
}
Taille.Java:
public class Size {
@SerializedName("id")
private int id;
@SerializedName("name")
private String name;
@SerializedName("active")
private boolean active;
@SerializedName("created_At")
private String createdAt;
@SerializedName("updated_at")
private String updatedAt;
}
MainActivity.Java:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://...")
.addConverterFactory(GsonConverterFactory.create())
.build();
WebAPIService service = retrofit.create(WebAPIService.class);
Call<JsonArray> jsonCall = service.readJsonArray();
jsonCall.enqueue(new Callback<JsonArray>() {
@Override
public void onResponse(Call<JsonArray> call, Response<JsonArray> response) {
String jsonString = response.body().toString();
Log.i("onResponse", jsonString);
Type listType = new TypeToken<List<Size>>() {}.getType();
List<Size> yourList = new Gson().fromJson(jsonString, listType);
Log.i("onResponse", yourList.toString());
}
@Override
public void onFailure(Call<JsonArray> call, Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
Voici la capture d'écran de débogage:
MISE À JOUR: Vous pouvez également utiliser l'option suivante:
@GET("/json.txt")
Call<List<Size>> readList();
et
Call<List<Size>> listCall1 = service.readList();
listCall1.enqueue(new Callback<List<Size>>() {
@Override
public void onResponse(Call<List<Size>> call, Response<List<Size>> response) {
for (Size size : response.body()){
Log.i("onResponse", size.toString());
}
}
@Override
public void onFailure(Call<List<Size>> call, Throwable t) {
Log.e("onFailure", t.toString());
}
});