androidpaginationretrofit2android-livedataandroid-paging-library

Searchview using keyword with Unsplash API


I'm calling in my app Unsplash API. I want to get photos based on the keyword.

But I can't something is wrong in my Model or URL and I get this error :

Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

I'm using Paging library with LiveData for this but I don't know how to do it properly.

Here is my PhotoDataSource:

  override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Photo>) {
    networkState.postValue(NetworkState.LOADING)
    initialLoad.postValue(NetworkState.LOADING)

    photoService.search("nature", FIRST_PAGE_NUMBER, params.requestedLoadSize).enqueue(object : Callback<List<Photo>>{
        override fun onFailure(call: Call<List<Photo>>, t: Throwable) {
            // keep a Completable for future retry
            setRetry(Action { loadInitial(params, callback) })
            val error = NetworkState.error(t.message)
            // publish the error
            networkState.postValue(error)
            initialLoad.postValue(error)
        }

        override fun onResponse(call: Call<List<Photo>>, response: Response<List<Photo>>) {
            if (response.body() != null) {
                setRetry(null)
                val data = response.body()
                val items = data?.map { it } ?: emptyList()
                networkState.postValue(NetworkState.LOADED)
                initialLoad.postValue(NetworkState.LOADED)
                callback.onResult(items, null, FIRST_PAGE_NUMBER + INCREMENT_PAGE_VALUE)
            }
        }
    })
}

override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Photo>) {
    networkState.postValue(NetworkState.LOADING)

    Timber.d("Fetching next page: ${params.key}")
    photoService.search("nature", params.key, params.requestedLoadSize).enqueue(object : Callback<List<Photo>> {
        override fun onFailure(call: Call<List<Photo>>, t: Throwable) {
            // keep a Completable for future retry
            setRetry(Action { loadAfter(params, callback) })
            // publish the error
            networkState.postValue(NetworkState.error(t.message))
        }

        override fun onResponse(call: Call<List<Photo>>, response: Response<List<Photo>>) {
            if (response.body() != null) {
                val data = response.body()
                val items = data?.map { it } ?: emptyList()
                setRetry(null)
                networkState.postValue(NetworkState.LOADED)
                callback.onResult(items, params.key + INCREMENT_PAGE_VALUE)
            }
        }

    })
}

Here, I have Photo Object:

   @Parcelize
data class Photo (val urls: @RawValue Urls? = null,
                  val color: String? = null,
                  val width: Int? = null,
                  val height: Int? = null,
                  val created_at: String? = null,
                  val id: String? = null,
                  val user: @RawValue User? = null,
                  val links: @RawValue Links? = null,
                  val likes: Int? = null,
                  val downloads: Int? = null,
                  val description: String? = null,
                  val location: @RawValue Location? = null,
                  val exif: @RawValue Exif? = null
):Parcelable

And here I'm calling the API, PhotoService:

  @GET("search/photos")
fun search(@Query("query") query: String, @Query("per_page") perPage: Int, @Query("page") page: Int): Call<List<Photo>>

Here is the full code from the JSON Response:

   {
      "total": 133,
      "total_pages": 7,
      "results": [
        {
          "id": "eOLpJytrbsQ",
          "created_at": "2014-11-18T14:35:36-05:00",
          "width": 4000,
          "height": 3000,
          "color": "#A7A2A1",
          "likes": 286,
          "liked_by_user": false,
          "description": "A man drinking a coffee.",
          "user": {
            "id": "Ul0QVz12Goo",
            "username": "ugmonk",
            "name": "Jeff Sheldon",
            "first_name": "Jeff",
            "last_name": "Sheldon",
            "instagram_username": "instantgrammer",
            "twitter_username": "ugmonk",
            "portfolio_url": "http://ugmonk.com/",
            "profile_image": {
              "small": "https://images.unsplash.com/profile-1441298803695-accd94000cac?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=32&w=32&s=7cfe3b93750cb0c93e2f7caec08b5a41",
              "medium": "https://images.unsplash.com/profile-1441298803695-accd94000cac?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=64&w=64&s=5a9dc749c43ce5bd60870b129a40902f",
              "large": "https://images.unsplash.com/profile-1441298803695-accd94000cac?ixlib=rb-0.3.5&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=128&w=128&s=32085a077889586df88bfbe406692202"
            },
            "links": {
              "self": "https://api.unsplash.com/users/ugmonk",
              "html": "http://unsplash.com/@ugmonk",
              "photos": "https://api.unsplash.com/users/ugmonk/photos",
              "likes": "https://api.unsplash.com/users/ugmonk/likes"
            }
          },
          "current_user_collections": [],
          "urls": {
            "raw": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f",
            "full": "https://hd.unsplash.com/photo-1416339306562-f3d12fefd36f",
            "regular": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=92f3e02f63678acc8416d044e189f515",
            "small": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=400&fit=max&s=263af33585f9d32af39d165b000845eb",
            "thumb": "https://images.unsplash.com/photo-1416339306562-f3d12fefd36f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=200&fit=max&s=8aae34cf35df31a592f0bef16e6342ef"
          },
          "links": {
            "self": "https://api.unsplash.com/photos/eOLpJytrbsQ",
            "html": "http://unsplash.com/photos/eOLpJytrbsQ",
            "download": "http://unsplash.com/photos/eOLpJytrbsQ/download"
          }
        },
        // more photos ...
      ]
    }

I have no idea what to do, I saw a lot of tutorials on the internet but I couldn't do anything. I hope you could help me, please.


Solution

  • Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

    This error specify that in your response one of keywords have object but you are accessing it as a array object.

    Solution:-

    Check that keywords which is mention as object.

    Try to make as object in your response Object

     @GET("search/photos")
    fun search(@Query("query") query: String, @Query("per_page") perPage: Int, @Query("page") page: Int): Call<Example>
    

    EDIT:-

    Use these classes in your project and Use Example for retrofit result.

    Example.class

    public class Example {
    
    @SerializedName("total")
    @Expose
    private Integer total;
    @SerializedName("total_pages")
    @Expose
    private Integer totalPages;
    @SerializedName("results")
    @Expose
    private List<Result> results = null;
    
    public Integer getTotal() {
    return total;
    }
    
    public void setTotal(Integer total) {
    this.total = total;
    }
    
    public Integer getTotalPages() {
    return totalPages;
    }
    
    public void setTotalPages(Integer totalPages) {
    this.totalPages = totalPages;
    }
    
    public List<Result> getResults() {
    return results;
    }
    
    public void setResults(List<Result> results) {
    this.results = results;
    }
    
    }
    

    Links.class

    public class Links {
    
    @SerializedName("self")
    @Expose
    private String self;
    @SerializedName("html")
    @Expose
    private String html;
    @SerializedName("photos")
    @Expose
    private String photos;
    @SerializedName("likes")
    @Expose
    private String likes;
    
    public String getSelf() {
    return self;
    }
    
    public void setSelf(String self) {
    this.self = self;
    }
    
    public String getHtml() {
    return html;
    }
    
    public void setHtml(String html) {
    this.html = html;
    }
    
    public String getPhotos() {
    return photos;
    }
    
    public void setPhotos(String photos) {
    this.photos = photos;
    }
    
    public String getLikes() {
    return likes;
    }
    
    public void setLikes(String likes) {
    this.likes = likes;
    }
    
    }
    

    Links_.class

    public class Links_ {
    
    @SerializedName("self")
    @Expose
    private String self;
    @SerializedName("html")
    @Expose
    private String html;
    @SerializedName("download")
    @Expose
    private String download;
    
    public String getSelf() {
    return self;
    }
    
    public void setSelf(String self) {
    this.self = self;
    }
    
    public String getHtml() {
    return html;
    }
    
    public void setHtml(String html) {
    this.html = html;
    }
    
    public String getDownload() {
    return download;
    }
    
    public void setDownload(String download) {
    this.download = download;
    }
    
    }
    

    ProfileImage.class

    public class ProfileImage {
    
    @SerializedName("small")
    @Expose
    private String small;
    @SerializedName("medium")
    @Expose
    private String medium;
    @SerializedName("large")
    @Expose
    private String large;
    
    public String getSmall() {
    return small;
    }
    
    public void setSmall(String small) {
    this.small = small;
    }
    
    public String getMedium() {
    return medium;
    }
    
    public void setMedium(String medium) {
    this.medium = medium;
    }
    
    public String getLarge() {
    return large;
    }
    
    public void setLarge(String large) {
    this.large = large;
    }
    
    }
    

    Result.class

    public class Result {
    
    @SerializedName("id")
    @Expose
    private String id;
    @SerializedName("created_at")
    @Expose
    private String createdAt;
    @SerializedName("width")
    @Expose
    private Integer width;
    @SerializedName("height")
    @Expose
    private Integer height;
    @SerializedName("color")
    @Expose
    private String color;
    @SerializedName("likes")
    @Expose
    private Integer likes;
    @SerializedName("liked_by_user")
    @Expose
    private Boolean likedByUser;
    @SerializedName("description")
    @Expose
    private String description;
    @SerializedName("user")
    @Expose
    private User user;
    @SerializedName("current_user_collections")
    @Expose
    private List<Object> currentUserCollections = null;
    @SerializedName("urls")
    @Expose
    private Urls urls;
    @SerializedName("links")
    @Expose
    private Links_ links;
    
    public String getId() {
    return id;
    }
    
    public void setId(String id) {
    this.id = id;
    }
    
    public String getCreatedAt() {
    return createdAt;
    }
    
    public void setCreatedAt(String createdAt) {
    this.createdAt = createdAt;
    }
    
    public Integer getWidth() {
    return width;
    }
    
    public void setWidth(Integer width) {
    this.width = width;
    }
    
    public Integer getHeight() {
    return height;
    }
    
    public void setHeight(Integer height) {
    this.height = height;
    }
    
    public String getColor() {
    return color;
    }
    
    public void setColor(String color) {
    this.color = color;
    }
    
    public Integer getLikes() {
    return likes;
    }
    
    public void setLikes(Integer likes) {
    this.likes = likes;
    }
    
    public Boolean getLikedByUser() {
    return likedByUser;
    }
    
    public void setLikedByUser(Boolean likedByUser) {
    this.likedByUser = likedByUser;
    }
    
    public String getDescription() {
    return description;
    }
    
    public void setDescription(String description) {
    this.description = description;
    }
    
    public User getUser() {
    return user;
    }
    
    public void setUser(User user) {
    this.user = user;
    }
    
    public List<Object> getCurrentUserCollections() {
    return currentUserCollections;
    }
    
    public void setCurrentUserCollections(List<Object> currentUserCollections) {
    this.currentUserCollections = currentUserCollections;
    }
    
    public Urls getUrls() {
    return urls;
    }
    
    public void setUrls(Urls urls) {
    this.urls = urls;
    }
    
    public Links_ getLinks() {
    return links;
    }
    
    public void setLinks(Links_ links) {
    this.links = links;
    }
    
    }
    

    Urls.class

    public class Urls {
    
    @SerializedName("raw")
    @Expose
    private String raw;
    @SerializedName("full")
    @Expose
    private String full;
    @SerializedName("regular")
    @Expose
    private String regular;
    @SerializedName("small")
    @Expose
    private String small;
    @SerializedName("thumb")
    @Expose
    private String thumb;
    
    public String getRaw() {
    return raw;
    }
    
    public void setRaw(String raw) {
    this.raw = raw;
    }
    
    public String getFull() {
    return full;
    }
    
    public void setFull(String full) {
    this.full = full;
    }
    
    public String getRegular() {
    return regular;
    }
    
    public void setRegular(String regular) {
    this.regular = regular;
    }
    
    public String getSmall() {
    return small;
    }
    
    public void setSmall(String small) {
    this.small = small;
    }
    
    public String getThumb() {
    return thumb;
    }
    
    public void setThumb(String thumb) {
    this.thumb = thumb;
    }
    
    }
    

    User.class

    public class User {
    
    @SerializedName("id")
    @Expose
    private String id;
    @SerializedName("username")
    @Expose
    private String username;
    @SerializedName("name")
    @Expose
    private String name;
    @SerializedName("first_name")
    @Expose
    private String firstName;
    @SerializedName("last_name")
    @Expose
    private String lastName;
    @SerializedName("instagram_username")
    @Expose
    private String instagramUsername;
    @SerializedName("twitter_username")
    @Expose
    private String twitterUsername;
    @SerializedName("portfolio_url")
    @Expose
    private String portfolioUrl;
    @SerializedName("profile_image")
    @Expose
    private ProfileImage profileImage;
    @SerializedName("links")
    @Expose
    private Links links;
    
    public String getId() {
    return id;
    }
    
    public void setId(String id) {
    this.id = id;
    }
    
    public String getUsername() {
    return username;
    }
    
    public void setUsername(String username) {
    this.username = username;
    }
    
    public String getName() {
    return name;
    }
    
    public void setName(String name) {
    this.name = name;
    }
    
    public String getFirstName() {
    return firstName;
    }
    
    public void setFirstName(String firstName) {
    this.firstName = firstName;
    }
    
    public String getLastName() {
    return lastName;
    }
    
    public void setLastName(String lastName) {
    this.lastName = lastName;
    }
    
    public String getInstagramUsername() {
    return instagramUsername;
    }
    
    public void setInstagramUsername(String instagramUsername) {
    this.instagramUsername = instagramUsername;
    }
    
    public String getTwitterUsername() {
    return twitterUsername;
    }
    
    public void setTwitterUsername(String twitterUsername) {
    this.twitterUsername = twitterUsername;
    }
    
    public String getPortfolioUrl() {
    return portfolioUrl;
    }
    
    public void setPortfolioUrl(String portfolioUrl) {
    this.portfolioUrl = portfolioUrl;
    }
    
    public ProfileImage getProfileImage() {
    return profileImage;
    }
    
    public void setProfileImage(ProfileImage profileImage) {
    this.profileImage = profileImage;
    }
    
    public Links getLinks() {
    return links;
    }
    
    public void setLinks(Links links) {
    this.links = links;
    }
    
    }
    

    Next time try to convert your JSON into POJO from this Link.