Nie można utworzyć konwertera dla mojej klasy w bibliotece Android Retrofit

136

Im migruję z używania Volley do Retrofit, mam już klasę gson, której użyłem wcześniej do konwersji odpowiedzi JSONObject na obiekt, który implementuje adnotacje gson. Kiedy próbuję wysłać żądanie HTTP przy użyciu funkcji retrofit, ale moja aplikacja ulega awarii z tym błędem:

 Unable to start activity ComponentInfo{com.lightbulb.pawesome/com.example.sample.retrofit.SampleActivity}: java.lang.IllegalArgumentException: Unable to create converter for class com.lightbulb.pawesome.model.Pet
    for method GitHubService.getResponse

Podążam za przewodnikiem w witrynie modernizacji i wymyślam te implementacje:

To jest moja czynność, w której próbuję wykonać żądanie retro http:

public class SampleActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sample);

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("**sample base url here**")
                .build();

        GitHubService service = retrofit.create(GitHubService.class);
        Call<Pet> callPet = service.getResponse("41", "40");
        callPet.enqueue(new Callback<Pet>() {
            @Override
            public void onResponse(Response<Pet> response) {
                Log.i("Response", response.toString());
            }

            @Override
            public void onFailure(Throwable t) {
                Log.i("Failure", t.toString());
            }
        });
        try{
            callPet.execute();
        } catch (IOException e){
            e.printStackTrace();
        }

    }
}

Mój interfejs, który okazał się moim API

public interface GitHubService {
    @GET("/ **sample here** /{petId}/{otherPet}")
    Call<Pet> getResponse(@Path("petId") String userId, @Path("otherPet") String otherPet);
}

I na koniec klasa Pet, która powinna być odpowiedzią:

public class Pet implements Parcelable {

    public static final String ACTIVE = "1";
    public static final String NOT_ACTIVE = "0";

    @SerializedName("is_active")
    @Expose
    private String isActive;
    @SerializedName("pet_id")
    @Expose
    private String petId;
    @Expose
    private String name;
    @Expose
    private String gender;
    @Expose
    private String age;
    @Expose
    private String breed;
    @SerializedName("profile_picture")
    @Expose
    private String profilePicture;
    @SerializedName("confirmation_status")
    @Expose
    private String confirmationStatus;

    /**
     *
     * @return
     * The confirmationStatus
     */
    public String getConfirmationStatus() {
        return confirmationStatus;
    }

    /**
     *
     * @param confirmationStatus
     * The confirmation_status
     */
    public void setConfirmationStatus(String confirmationStatus) {
        this.confirmationStatus = confirmationStatus;
    }

    /**
     *
     * @return
     * The isActive
     */
    public String getIsActive() {
        return isActive;
    }

    /**
     *
     * @param isActive
     * The is_active
     */
    public void setIsActive(String isActive) {
        this.isActive = isActive;
    }

    /**
     *
     * @return
     * The petId
     */
    public String getPetId() {
        return petId;
    }

    /**
     *
     * @param petId
     * The pet_id
     */
    public void setPetId(String petId) {
        this.petId = petId;
    }

    /**
     *
     * @return
     * The name
     */
    public String getName() {
        return name;
    }

    /**
     *
     * @param name
     * The name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     *
     * @return
     * The gender
     */
    public String getGender() {
        return gender;
    }

    /**
     *
     * @param gender
     * The gender
     */
    public void setGender(String gender) {
        this.gender = gender;
    }

    /**
     *
     * @return
     * The age
     */
    public String getAge() {
        return age;
    }

    /**
     *
     * @param age
     * The age
     */
    public void setAge(String age) {
        this.age = age;
    }

    /**
     *
     * @return
     * The breed
     */
    public String getBreed() {
        return breed;
    }

    /**
     *
     * @param breed
     * The breed
     */
    public void setBreed(String breed) {
        this.breed = breed;
    }

    /**
     *
     * @return
     * The profilePicture
     */
    public String getProfilePicture() {
        return profilePicture;
    }

    /**
     *
     * @param profilePicture
     * The profile_picture
     */
    public void setProfilePicture(String profilePicture) {
        this.profilePicture = profilePicture;
    }


    protected Pet(Parcel in) {
        isActive = in.readString();
        petId = in.readString();
        name = in.readString();
        gender = in.readString();
        age = in.readString();
        breed = in.readString();
        profilePicture = in.readString();
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(isActive);
        dest.writeString(petId);
        dest.writeString(name);
        dest.writeString(gender);
        dest.writeString(age);
        dest.writeString(breed);
        dest.writeString(profilePicture);
    }

    @SuppressWarnings("unused")
    public static final Parcelable.Creator<Pet> CREATOR = new Parcelable.Creator<Pet>() {
        @Override
        public Pet createFromParcel(Parcel in) {
            return new Pet(in);
        }

        @Override
        public Pet[] newArray(int size) {
            return new Pet[size];
        }
    };
}
Earwin delos Santos
źródło
proszę dodać ciąg odpowiedzi dla tego linku mysample.com/development/cuteness
koutuk
@koutuk to tylko próbka,
zresztą
w której linii
pojawia
powinieneś przejść przez youtube.com/watch?v=gGuUBlzmtPQ ten film
koutuk
och, to jest ten stary. spróbuj odwiedzić plac
Earwin delos Santos

Odpowiedzi:

220

Wcześniej 2.0.0domyślnym konwerterem był konwerter gson, ale w wersjach 2.0.0i później domyślnym konwerterem jest ResponseBody. Z dokumentów:

Domyślnie Retrofit może deserializować tylko treści HTTP do typu OkHttp ResponseBodyi może akceptować tylko jego RequestBodytyp dla @Body.

W programie 2.0.0+musisz wyraźnie określić, że chcesz mieć konwerter Gson:

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("**sample base url here**")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

Będziesz także musiał dodać następującą zależność do swojego pliku gradle:

compile 'com.squareup.retrofit2:converter-gson:2.1.0'

Użyj tej samej wersji dla konwertera, co do modernizacji. Powyższe pasuje do tej zależności modernizacji:

compile ('com.squareup.retrofit2:retrofit:2.1.0')

Należy również zauważyć, że w chwili pisania tego dokumentu dokumenty dotyczące modernizacji nie są całkowicie zaktualizowane, dlatego ten przykład wpędził Cię w kłopoty. Z dokumentów:

Uwaga: ta witryna jest nadal w trakcie rozbudowy o nowe interfejsy API 2.0.

iagreen
źródło
nadal mam problem
user3475052
232

Jeśli ktoś kiedykolwiek spotka się z tym w przyszłości, ponieważ próbujesz zdefiniować własną niestandardową fabrykę konwerterów i otrzymujesz ten błąd, może to być również spowodowane posiadaniem wielu zmiennych w klasie z błędnie wpisaną lub taką samą nazwą serializowaną. TO ZNACZY:

public class foo {
  @SerializedName("name")
  String firstName;
  @SerializedName("name")
  String lastName;
}

Po dwukrotnym zdefiniowaniu nazw serializowanych (prawdopodobnie przez pomyłkę) również wystąpi ten sam błąd.

Aktualizacja : pamiętaj, że ta logika obowiązuje również w przypadku dziedziczenia. Jeśli rozszerzysz do klasy nadrzędnej z obiektem, który ma taką samą nazwę serializowaną, jak w przypadku podklasy, spowoduje to ten sam problem.

PGMacDesign
źródło
2
To był problem dla mnie, zapomniałem usunąć pole w klasie podrzędnej, którą przeniosłem do klasy nadrzędnej. Dzięki stary!
Vucko,
1
Dzięki uratowałeś mnie od 2 dni zmagań. Popełniłem ten sam błąd, deklarując 2 zmienne o takich samych nazwach serializowanych.
Valynk
To zadziałało dla mnie
odich daniel
thx, rzeczywiście to był problem, +1
Ravi Vaniya
10

Na podstawie górnego komentarza zaktualizowałem import

implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'

Użyłem http://www.jsonschema2pojo.org/ , aby utworzyć pojo z wyników Spotify json i upewnić się, że określono format Gson.

Obecnie dostępne są wtyczki Android Studio, które mogą tworzyć dla Ciebie modele danych pojo lub Kotlin. Jedną z doskonałych opcji dla komputerów Mac jest Quicktype. https://itunes.apple.com/us/app/paste-json-as-code-quicktype/id1330801220

Juan Mendez
źródło
10

po prostu upewnij się, że nie używasz dwukrotnie tej samej nazwy serializacji

 @SerializedName("name") val name: String
 @SerializedName("name") val firstName: String

po prostu usuń jedną z nich

Mohamed Farahat
źródło
Dziękuję, uratowałeś mi dzień :)
Hemendra Gangwar
4

W moim przypadku miałem obiekt TextView w mojej klasie modalnej i GSON nie wiedział, jak go serializować. Oznaczenie tego jako „przejściowego” rozwiązało problem.

Saksham Dhawan
źródło
Chociaż spowoduje to, że będzie on widoczny, pamiętaj, że nie zadziała, jeśli zaciemnisz kod (w IE, za pomocą programu proguard) i opublikujesz go; lepiej mieć zamiast tego adnotacje SerializedName lub Exposed
PGMacDesign
4

Post @ Silmarilos pomógł mi rozwiązać ten problem. W moim przypadku użyłem „id” jako serializowanej nazwy, na przykład:

 @SerializedName("id")
var node_id: String? = null

i zmieniłem to na

 @SerializedName("node_id")
var node_id: String? = null

Teraz wszystko działa. Zapomniałem, że „id” jest atrybutem domyślnym.

Glenncito
źródło
2

To może komuś pomóc

W moim przypadku omyłkowo napisałem SerializedName w ten sposób

@SerializedName("name","time")
String name,time; 

Powinno być

@SerializedName("name")
String name;

@SerializedName("time")
String time;
RAJESH KUMAR ARUMUGAM
źródło
1

W moim przypadku było to spowodowane próbą przeniesienia listy zwracanej przez moją usługę do ArrayList. Tak więc miałem:

@Json(name = "items")
private ArrayList<ItemModel> items;

kiedy powinienem był

@Json(name = "items")
private List<ItemModel> items;

Mam nadzieję, że to komuś pomoże!

Riot Goes Woof
źródło
0

Hej, dzisiaj przechodziłem przez ten sam problem, szukając rozwiązania, zajęło mi cały dzień, ale to jest rozwiązanie, które ostatecznie znalazłem. Używam Daggera w moim kodzie i musiałem zaimplementować konwerter Gson w mojej instancji modernizacji.

więc to był mój kod wcześniej

@Provides
    @Singleton
    Retrofit providesRetrofit(Application application,OkHttpClient client) {
        String SERVER_URL=URL;
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.baseUrl(SERVER_URL);
        return builder
                .client(client)
                .build();
    }

to właśnie z tym skończyłem

@Provides
    @Singleton
    Retrofit providesRetrofit(Application application,OkHttpClient client, Gson gson) {
        String SERVER_URL=URL;
        Retrofit.Builder builder = new Retrofit.Builder();
        builder.baseUrl(SERVER_URL);
        return builder
                .client(client)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();
    }

zwróć uwagę, że w pierwszym przykładzie nie ma konwertera, a dodatek, jeśli nie utworzyłeś instancji Gson, dodajesz go w ten sposób

    @Provides
    @Singleton
    Gson provideGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();

   gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
        return gsonBuilder.create();
    }

i upewnij się, że uwzględniono go w wywołaniu metody dla modernizacji.

jeszcze raz mam nadzieję, że to pomoże komuś takiemu jak ja.

wiewiórka
źródło
0

W moim przypadku problem polegał na tym, że mój model SUPERCLASS miał zdefiniowane w sobie to pole. Bardzo głupie, wiem ...

SkyNet801
źródło