web-dev-qa-db-ja.com

RetrofitとGSONを使用して、[]で囲まれたJSONオブジェクトのリストを解析する方法は?

簡単なRESTエンドポイントを作成しました:

http://<server_address>:3000/sizes

このURLは、次のようにjson arrayを含む非常に単純な応答を返します。

[
  { "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.IllegalStateExceptionBEGIN_OBJECTが予期されていたが、STRINGでした行1列18のパス$ [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を作成する方法はわかりません。

前もって感謝します。

25
Tomasz Dzieniak

おもしろい事実は...私のコードはまったく問題ありません。少なくとも上記の質問で提示されたもの。

Sizeモデルから1行削除しました。

コード自体(特にRetrofitの構成)に焦点を当てたため、インポートを完全に無視しました。

それは判明しました-モデルのフィールドにSizeクラスを入力し始めたときにStringモデルを実装しているとき:

  • name
  • createdAt
  • updatedAt

IntelliJ IDEAのコード補完が私に提案した

  • Java.lang.Stringではありません
  • しかしcom.Sun.org.Apache.xpath.internal.operations.String

Gsonの逆シリアル化を完全に台無しにしたもの

報酬に関しては...

私は自分自身の答えを有効としてマークすることにしました。どうして?

  • それを確実にするために、私とまったく同じ問題を抱える開発者全員-有効なインポートがあることを確認してください

素晴らしいサービスに感謝します。

賞金は1つしかないので、報酬を決定しました xiaoyaoworm 彼のコードは私のニーズに合っているので(質問には書いていませんが、このようなシンプルなサービスを書くというアイデアは-提示したとおりです)私の質問では-エンドユーザーの実装の詳細から非表示にし、JsonArrayなどを使用しないでください [〜#〜] bnk [〜#〜] response)。

更新1:

xiaoyaoworm の答えの唯一の問題は、Sizeモデルは注釈を必要としないことを示唆したことです引用されたJSONの例の完全な誤り。

上記の場合、Sizeモデルの正確な2つのフィールドには注釈が必要です-created_atおよびupdated_at

converter-gsonライブラリのいくつかのバージョンもテストしました( xiaoyaoworm は私以外のものを使用しています)-何も変更していません。注釈が必要でした。

それ以外の場合-再び、多くの感謝!

5
Tomasz Dzieniak

最近、レトロフィット2に関連する1つのプロジェクトを終えました。私のソースに基づいて、私はあなたのすべてのものを私のプロジェクトにコピーして試してみて、いくつかの小さな変更を加えます。それは私の側でうまくいきます。

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'

モデル:(UPDATE:tommusの場合、createdAtおよびupdatedAtが彼のjson応答サンプルに表示されるようになりました。モデル内の名前がjson responeと異なるため、これら2つの値には注釈が必要です))

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

これを実行すると、印刷したい情報が表示されます: enter image description here

Git POST PUT DELETEが機能する。retrofit2.0を使用して、これを参照として使用できます。 My Github retrofit2.0 repo

19
xiaoyaoworm

以下を使用してください:

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

デバッグのスクリーンショットは次のとおりです。

enter image description here


更新:次のオプションも使用できます。

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