簡體   English   中英

如何使用Retrofit和GSON解析[]包圍的JSON對象列表?

[英]How to parse list of JSON objects surrounded by [] using Retrofit and 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 }
]

現在,我正在嘗試使用GSON的Retrofit 2使用此響應

我添加了一個模型:

@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但在第1行第18 列為STRING路徑$ [0] .name

我認為錯誤是由此引起的, REST API返回一個數組或對象的響應

  1. 我對么?
  2. 使這段代碼有效的最簡單方法是什么?

無法修改REST服務 ,因此響應必須保持原樣。

此外,使用純GSON對上述json進行反序列化可以通過以下方式完成:

Type sizesType = new TypeToken<List<Size>>(){}.getType();
List<Size> size = new Gson().fromJson(json, sizesType);

但我不知道如何讓Retrofit 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,而是使用URL中的服務器IP地址

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工作。 您可以將其用作參考。 我的Github改造2.0回購

請使用以下內容:

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

這是調試截圖:

在此輸入圖像描述


更新:您還可以使用以下選項:

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

有趣的是......我的代碼非常好。 至少在上面的問題中提出的那個。

我最終從我的Size模型中刪除了一行。

當我專注於代碼本身(特別是Retrofit的配置)時,我完全忽略了導入。

事實證明 - 當我開始為模型的字段鍵入String類時實現Size模型:

  • name
  • createdAt
  • updatedAt

IntelliJ IDEA的代碼完成建議我

  • 不是java.lang.String
  • 但是com.sun.org.apache.xpath.internal.operations.String

什么完全搞砸了Gson的反序列化

談到獎勵......

我決定將自己的答案標記為有效。 為什么?

  • 為了確保每個開發人員都會遇到與我完全相同的問題 - 確保您擁有有效的導入

非常感謝上面的gentlmen提供的優質服務。

因為我只有一個賞金,所以我決定獎勵xiaoyaoworm,因為他的代碼更能滿足我的需求(我沒有在我的問題中寫出來,但編寫這樣簡單的服務的想法 - 就像我在我的問題中提出的那樣 - 是隱藏從最終用戶實現細節而不是在BNK響應中使用JsonArray等。

更新1:

xiaoyaoworm回答的唯一問題是,他建議Size模型不需要任何注釋 ,引用的JSON示例完全錯誤。

對於上述情況, Size模型的確切兩個字段 需要注釋 - created_atupdated_at

我甚至測試了幾個版本的converter-gson庫(我看到xiaoyaoworm已經使用了除了我之外) - 它沒有改變任何東西。 注釋是必要的。

否則 - 再次,非常感謝!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM