Earwin delos Santos
Earwin delos Santos

Reputation: 3065

Unable to create converter for my class in Android Retrofit library

I'm migrating from using Volley to Retrofit, I already have gson class that I used before for converting JSONObject response to a object that implements gson annotations. When I'm trying to make a HTTP GET request using Retrofit but then my app crashes with this error:

 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

Im following the guide in retrofit site and I come up with these implementations:

This is my activity where I am trying to execute the retro http request:

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

    }
}

My interface which turned to be my API

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

And finally the Pet class which should be the response:

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];
        }
    };
}

Upvotes: 189

Views: 159457

Answers (21)

SanBarra
SanBarra

Reputation: 3

You might also get this error if you are using Gson to deserialize or serialize certain Java time classes such as LocalDate and Instant. This was the case for me and the solution was to create custom deserializers and serializers and registering them on the Gson object

private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_INSTANT;

JsonDeserializer<Instant> deserializerInstant = (json, typeOfT, context) -> json == null ? null : FORMATTER.parse(json.getAsString(), Instant::from);
JsonSerializer<Instant> serializerInstant = (instant, typeOfT, context) -> instant == null ? null : JsonParser.parseString(instant.toString());
    
gson = new GsonBuilder()
            .registerTypeAdapter(Instant.class, deserializerInstant)
            .registerTypeAdapter(Instant.class, serializerInstant)
            .create();

Upvotes: 0

PGMacDesign
PGMacDesign

Reputation: 6302

If anyone ever comes across this in the future because you are trying to define your own custom converter factory and are getting this error, it can also be caused by having multiple variables in a class with a misspelled or the same serialized name. IE:

Java

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

Kotlin

data class Foo(
  @SerializedName("name") 
  val firstName: String,
  @SerializedName("name")
  val lastName: String
)

Having serialized names defined twice (likely by mistake) will also throw this exact same error.

Update: Keep in mind that this logic also holds true via inheritance. If you extend to a parent class with an object that has the same Serialized name as you do in the sub-class, it will cause this same problem.

Upvotes: 323

Micka&#235;l Merlet
Micka&#235;l Merlet

Reputation: 21

For me, the problem was a incompatibility between Kotlin Android version (id("org.jetbrains.kotlin.android") version "1.9.0") and Kotlin plugin Serialization (org.jetbrains.kotlin.plugin.serialization)

My kotlin plugin was in version 1.8.10. After updated it to 1.9.0, it works.

So, make sure Android version is the same as plugin serialiser.

Upvotes: 2

C.F.G
C.F.G

Reputation: 1463

For better debuging, I've used extra option .asLenient() of MoshiConverterFactory.create(moshi) i.e. MoshiConverterFactory.create(moshi).asLenient() and in logcat it showed the cause.

In my project it showed me what is wrong:

Caused by: java.lang.IllegalArgumentException:
Platform class java.sql.Time (with no annotations)
requires explicit JsonAdapter to be registered
for class java.sql.Time finishTime

I've defined a class with Time finishTime property. I temporarily changed its type to "String" and the error went away.

Upvotes: 0

George Vortelinos
George Vortelinos

Reputation: 41

I use moshi and for my case it was missing @JsonClass annotation

@JsonClass(generateAdapter = true)
data class Response (
    val summary: String?
)

Upvotes: 0

Dmitry L.
Dmitry L.

Reputation: 1602

In my case it was @XNullable tag. It is not correctly handled.

Upvotes: 0

Mark S. Khalil
Mark S. Khalil

Reputation: 157

In my cas i had to change ConverterFactory from moshi to Gson and it woked will.

.addConverterFactory(GsonConverterFactory.create())

Upvotes: 1

Franklin84
Franklin84

Reputation: 573

In my case I was missing the Serialization attribute.
I had to add @kotlinx.serialization.Serializable before every data class:

@kotlinx.serialization.Serializable
data class RadioSearchPodcastDto(
    val playables: List<Playable>,
    val searchTag: SearchTag,
    val totalCount: Int
)

Retrofit interface:

interface PodcastRadioApi {
     @GET("/podcasts/search")
     suspend fun getPodcastBySearch(@Query("query") query: String,
                                    @Query("count") count: Int,
                                    @Query("offset") offset: Int,
                                    @Query("partner") partner: String): RadioSearchPodcastDto
}

With every, I mean the main class and all sub-classes (Playable, SearchTag, ...)

Upvotes: 3

All Іѕ Vаиітy
All Іѕ Vаиітy

Reputation: 26372

In my case using kotlinx.serialization, the same exception was raised by retrofit,

it was due to the missing @Serializable annotation.

@Serializable
data class MyClass(
    val id: String
)

Upvotes: 2

Hmerman6006
Hmerman6006

Reputation: 1913

In my case I was using the Moshi library with Retrofit 2.0, i.e

// Moshi
implementation 'com.squareup.moshi:moshi-kotlin:1.9.3'
// Retrofit with Moshi Converter
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'

I forgot to pass in the custom Moshi json converter adapter factory object to the moshi converter factory constructor.

private val moshi = Moshi.Builder() // adapter
    .add(KotlinJsonAdapterFactory())
    .build()

private val retrofit = Retrofit.Builder()
    .addConverterFactory(MoshiConverterFactory.create()) // <- missing moshi json adapter insance
    .baseUrl(BASE_URL)
    .build()

Fix: .addConverterFactory(MoshiConverterFactory.create(moshi))

Upvotes: 10

Sana Ebadi
Sana Ebadi

Reputation: 7210

in my case, I am using Moshi with Retrofit and my mistake was :

I did not define a body for object that include in Response class service.

for example:

@JsonSerializable
data class Balance(
    @field:Json(name = "balance") var balance: Double,
    @field:Json(name = "currency") var currency: Currency

and the Currency class was emty. so I complete it and the problem fixed!

Upvotes: -1

oguzata
oguzata

Reputation: 53

In build.gradle changing

minifyEnabled true

to

minifyEnabled false

has solved my problem.

Upvotes: 2

Mohamed Farahat
Mohamed Farahat

Reputation: 761

just make sure that you are not using the same serialize name twice

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

just remove one of them

Upvotes: 55

SkyNet801
SkyNet801

Reputation: 41

In my case, the problem was that my SUPERCLASS model had this field defined in it. Very stupid, I know....

Upvotes: 2

Riot Goes Woof
Riot Goes Woof

Reputation: 3514

In my case, it was due to trying to take a List being returned by my service into an ArrayList. So what I had was:

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

when I should've had

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

Hope this helps someone!

Upvotes: 4

squirrel
squirrel

Reputation: 154

Hey i was going through the same issue today took me a whole day to find a solution but this is the solution i found finally. Am using Dagger in my code and i needed to implement the Gson converter in my retrofit instance.

so this was my code before

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

this was what i ended up with

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

notice how there is no converter in the first example and the addition if you haven't instantiated Gson you add it like this

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

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

and ensure you have included it in the method call for retrofit.

once again hope this helps some one like me.

Upvotes: 0

Juan Mendez Escobar
Juan Mendez Escobar

Reputation: 2757

Based on top comment I updated my imports

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

I've used http://www.jsonschema2pojo.org/ in order to create pojo's from Spotify json results and making sure to specify Gson format.

These days there are Android Studio plugins which can create the pojo's or Kotlin data models for you. One great option for mac is Quicktype. https://itunes.apple.com/us/app/paste-json-as-code-quicktype/id1330801220

Upvotes: 13

Glenncito
Glenncito

Reputation: 930

@Silmarilos's post helped me solve this. In my case, it was that I used "id" as a serialized name, like this:

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

and I changed it to

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

All working now. I forgot that 'id' is a default attribute.

Upvotes: 5

RAJESH KUMAR ARUMUGAM
RAJESH KUMAR ARUMUGAM

Reputation: 1570

This may help someone

In my case mistakenly I wrote SerializedName like this

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

It should be

@SerializedName("name")
String name;

@SerializedName("time")
String time;

Upvotes: 2

Saksham Dhawan
Saksham Dhawan

Reputation: 51

In my case, I had a TextView object inside my modal class and GSON did not know how to serialize it. Marking it as 'transient' solved the issue.

Upvotes: 5

iagreen
iagreen

Reputation: 31996

Prior to 2.0.0, the default converter was a gson converter, but in 2.0.0 and later the default converter is ResponseBody. From the docs:

By default, Retrofit can only deserialize HTTP bodies into OkHttp's ResponseBody type and it can only accept its RequestBody type for @Body.

In 2.0.0+, you need to explicitly specify you want a Gson converter:

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

You will also need to add the following dependency to your gradle file:

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

Use the same version for the converter as you do for your retrofit. The above matches this retrofit dependency:

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

Also, note as of writing this, the retrofit docs are not completely updated, which is why that example got you into trouble. From the docs:

Note: This site is still in the process of being expanded for the new 2.0 APIs.

Upvotes: 256

Related Questions