Как разбирать список объектов JSON, окруженных [], с помощью Retrofit и GSON?
Я создал простую конечную точку REST:
http://<server_address>:3000/sizes
Этот URL-адрес возвращает очень простой ответ, содержащий массив json следующим образом:
[
{ "id": 1, "name": "Small", "active": true },
{ "id": 2, "name": "Medium", "active": true },
{ "id": 3, "name": "Large", "active": true }
]
Теперь я пытаюсь использовать этот ответ, используя Retrofit 2 с GSON.
Я добавил модель:
@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;
}
И сервис:
public interface Service {
@GET("sizes")
Call<List<Size>> loadSizes();
}
Я создал экземпляр "Дооснащения":
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://<server_address>:3000")
.addConverterFactory(GsonConverterFactory.create())
.build();
И мое обслуживание:
Service service = retrofit.create(Service.class);
Теперь, пытаясь вызвать данные:
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());
}
});
В результате получается исключение:
java.lang.IllegalStateException: Ожидаемый BEGIN_OBJECT, но был STRING в строке 1 column 18 path $[0].name
Я предполагаю, что ошибка вызвана тем, что REST API возвращает ответ, который представляет собой массив или объект.
- Правильно ли я?
- Каков самый простой способ заставить этот код работать?
Служба REST не может быть изменена, поэтому ответ должен оставаться как есть.
Кроме того, десериализация вышеупомянутого json с использованием чистого GSON может быть выполнена:
Type sizesType = new TypeToken<List<Size>>(){}.getType();
List<Size> size = new Gson().fromJson(json, sizesType);
Но я не знаю, как сделать Retrofit 2. чтобы использовать это.
Спасибо заранее.
Ответы
Ответ 1
Забавный факт: мой код в порядке. По крайней мере, тот, который представлен в вопросе выше.
Я закончил удаление одной строки из моей модели Size
.
Поскольку я сосредоточился на самом коде (особенно в настройке Retrofit), я полностью проигнорировал импорт.
Оказалось, что при реализации модели Size
, когда я начал набирать класс String
для полей модели:
IntelliJ IDEA завершение кода предложило мне
- not
java.lang.String
- но
com.sun.org.apache.xpath.internal.operations.String
что полностью перепутано Gson
десериализация.
Когда дело доходит до вознаграждения...
Я решил пометить как правильный мой собственный ответ. Зачем?
- Чтобы гарантировать, что каждый из тех разработчиков, у кого будет такая же проблема, как и я, , убедитесь, что у вас есть действующие импортные.
Большое спасибо господам выше за их прекрасные услуги.
Поскольку у меня есть только одна награда, я решил вознаградить xiaoyaoworm, поскольку его код лучше соответствует моим потребностям (я не написал это в своем вопросе, но идея написания такого простого сервиса, как я уже представил в моем вопросе, заключается в том, чтобы скрыться от деталей реализации конечного пользователя и не использовать JsonArray
и таких, как в BNK).
Обновление 1:
Единственная проблема с ответом xiaoyaoworm заключается в том, что он предложил модели Size
не нужны аннотации, что полностью неправильно для цитируемого примера JSON.
В приведенном выше случае точные два поля модели Size
нужны аннотации - created_at
и updated_at
.
Я даже тестировал несколько версий библиотеки converter-gson
(я видел, что xiaoyaoworm использовал другие, чем я) - он ничего не изменил. Аннотации были необходимы.
В противном случае - снова большое спасибо!
Ответ 2
Недавно я только закончил один проект, связанный с retrofit2. Основываясь на моем источнике, я копирую все свои материалы в свой проект, чтобы попробовать, внести некоторые незначительные изменения, он хорошо работает на моей стороне.
В вашем файле build.gradle добавьте те:
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'
Модель: (ОБНОВЛЕНИЕ: Следуйте примеру tommus, createdAt и updatedAt теперь показаны в его примере ответа json, эти два значения требуют аннотации из-за имени в модели, отличного от 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;
}
Сервис: (Точно так же, как и у вас)
public interface service {
@GET("sizes")
Call<List<Size>> loadSizes();
}
RestClient: (Я добавляю журнал здесь, чтобы вы могли видеть всю информацию о запросах и ответах, будьте осторожны, не используя Localhost, но ваш IP-адрес сервера в 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;
}
}
В своей деятельности добавьте эту функцию для запуска своего кода: (точно так же, как и вы сделали. Ответ будет вашим списком размера)
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());
}
});
}
Запустив это, вы увидите информацию, которую хотите распечатать:
![введите описание изображения здесь]()
Вот мой github repo, который я использую retrofit2.0, чтобы упростить работу GET POST PUT DELETE. Вы можете использовать это как ссылку. My Github retrofit2.0 repo
Ответ 3
Пожалуйста, используйте следующее:
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();
}
Size.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());
}
});
}
Вот скриншот отладки:
![введите описание изображения здесь]()
UPDATE:
Вы также можете использовать следующую опцию:
@GET("/json.txt")
Call<List<Size>> readList();
и
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());
}
});