4

I'm consuming an API that provides much more information that I need. Im using Retrofit and GSON to get the data and create a list of POJO with the needed values. I made a custom TypeAdapter, and inside of it, I get the response and return a list of objects. When using a local JSON object, it work fine with the custom TypeAdapter, but with Retrofit I go the error.

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

Any ideas why is that? What Im I doing wrong?

EDIT:

I changed the GsonBuilder, to pass the right type to the Retrofit Builder, like this:

Type collectionType = new TypeToken<List<Article>>(){}.getType();
    gsonBuilder.registerTypeAdapter(collectionType,new ArticleTypeAdapter());

JSON

{
"status": "OK",
"copyright": "Copyright (c) 2017 The New York Times Company.  All Rights Reserved.",
"num_results": 5,
"results": [
    {
        "url": "https://www.nytimes.com/2017/06/29/automobiles/autoreviews/video-review-a-family-friendly-ferrari-for-the-family-of-means.html",
        "adx_keywords": "Ferrari SpA;Ferrari GTC4Lusso;Automobiles",
        "column": "Driven",
        "section": "Automobiles",
        "byline": "By TOM VOELK",
        "type": "Article",
        "title": "Video Review: A Family-Friendly Ferrari, for the Family of Means",
        "abstract": "The all-wheel-drive V12 GTC4Lusso seats four comfortably. With its svelte silhouette, it may look like a station wagon. It is anything but.",
        "published_date": "2017-06-29",
        "source": "The New York Times",
        "id": 100000005178442,
        "asset_id": 100000005178442,
        "views": 1,
        "des_facet": [
            "AUTOMOBILES"
        ],
        "org_facet": [
            "FERRARI SPA"
        ],
        "per_facet": "",
        "geo_facet": "",
        "media": [
            {
                "type": "image",
                "subtype": "photo",
                "caption": "Ferrari GTC4Lusso",
                "copyright": "Martin Campbell",
                "approved_for_syndication": 1,
                "media-metadata": [
                    {
                        "url": "https://static01.nyt.com/images/2017/06/26/automobiles/autoreviews/27driven1/27driven1-thumbStandard.jpg",
                        "format": "Standard Thumbnail",
                        "height": 75,
                        "width": 75
                    },
                    {
                        "url": "https://static01.nyt.com/images/2017/06/26/automobiles/autoreviews/27driven1/27driven1-mediumThreeByTwo210.jpg",
                        "format": "mediumThreeByTwo210",
                        "height": 140,
                        "width": 210
                    },
                    {
                        "url": "https://static01.nyt.com/images/2017/06/26/automobiles/autoreviews/27driven1/27driven1-mediumThreeByTwo440.jpg",
                        "format": "mediumThreeByTwo440",
                        "height": 293,
                        "width": 440
                    }
                ]
            }
        ]
    },
    {
        "url": "https://www.nytimes.com/2017/07/06/automobiles/wheels/luxury-cars-dealers-sales.html",
        "adx_keywords": "Automobiles;Luxury Goods and Services;Customer Relations;United States;Lexus Division of Toyota Motor Corp;Cadillac Division of General Motors Corp;Alfa Romeo Automobiles;Lincoln Motor Co",
        "column": "Wheels",
        "section": "Automobiles",
        "byline": "By ERIC A. TAUB",
        "type": "Article",
        "title": "How to Soothe Luxury-Car Buyers: Add Perks and Subtract Haggling",
        "abstract": "Several automakers aim to change the shopping experience with fixed prices, after-sale care and even ways to avoid the dealership altogether.",
        "published_date": "2017-07-06",
        "source": "The New York Times",
        "id": 100000005135624,
        "asset_id": 100000005135624,
        "views": 2,
        "des_facet": [
            "AUTOMOBILES",
            "LUXURY GOODS AND SERVICES",
            "CUSTOMER RELATIONS"
        ],
        "org_facet": [
            "LEXUS DIVISION OF TOYOTA MOTOR CORP",
            "CADILLAC DIVISION OF GENERAL MOTORS CORP",
            "ALFA ROMEO AUTOMOBILES",
            "LINCOLN MOTOR CO"
        ],
        "per_facet": "",
        "geo_facet": [
            "UNITED STATES"
        ],
        "media": [
            {
                "type": "image",
                "subtype": "photo",
                "caption": "Lexus has introduced a fixed-price program, called Lexus Plus, in which the buyer deals with only one person from beginning to end.",
                "copyright": "Alex Wroblewski for The New York Times",
                "approved_for_syndication": 1,
                "media-metadata": [
                    {
                        "url": "https://static01.nyt.com/images/2017/07/06/business/07WHEELS1/07WHEELS1-thumbStandard.jpg",
                        "format": "Standard Thumbnail",
                        "height": 75,
                        "width": 75
                    },
                    {
                        "url": "https://static01.nyt.com/images/2017/07/06/business/07WHEELS1/07WHEELS1-mediumThreeByTwo210.jpg",
                        "format": "mediumThreeByTwo210",
                        "height": 140,
                        "width": 210
                    },
                    {
                        "url": "https://static01.nyt.com/images/2017/07/06/business/07WHEELS1/07WHEELS1-mediumThreeByTwo440.jpg",
                        "format": "mediumThreeByTwo440",
                        "height": 293,
                        "width": 440
                    }
                ]
            }
        ]
    },
    {
        "url": "https://www.nytimes.com/2017/06/29/automobiles/wheels/why-fog-lamps-are-starting-to-disappear.html",
        "adx_keywords": "Automobile Safety Features and Defects;AAA Foundation for Traffic Safety;Mercedes-Benz;Consumer Reports;Insurance Institute for Highway Safety",
        "column": "Wheels",
        "section": "Automobiles",
        "byline": "By JAMES G. COBB",
        "type": "Article",
        "title": "Why Fog Lamps Are Starting to Disappear",
        "abstract": "Several luxury automakers have moved away from fog lights, saying that new high-tech headlights render them obsolete.",
        "published_date": "2017-06-29",
        "source": "The New York Times",
        "id": 100000005172500,
        "asset_id": 100000005172500,
        "views": 3,
        "des_facet": [
            "AUTOMOBILE SAFETY FEATURES AND DEFECTS"
        ],
        "org_facet": [
            "AAA FOUNDATION FOR TRAFFIC SAFETY",
            "MERCEDES-BENZ",
            "CONSUMER REPORTS",
            "INSURANCE INSTITUTE FOR HIGHWAY SAFETY"
        ],
        "per_facet": "",
        "geo_facet": "",
        "media": [
            {
                "type": "image",
                "subtype": "photo",
                "caption": "General Motors’ headquarters and other buildings in the Detroit skyline, obscured by early morning fog. Some automakers have quietly omitted front fog lights from many new models, saying that high-tech headlights make them unnecessary.",
                "copyright": "Kevin Miyazaki for The New York Times",
                "approved_for_syndication": 1,
                "media-metadata": [
                    {
                        "url": "https://static01.nyt.com/images/2017/06/29/business/30WHEELS1/30WHEELS1-thumbStandard.jpg",
                        "format": "Standard Thumbnail",
                        "height": 75,
                        "width": 75
                    },
                    {
                        "url": "https://static01.nyt.com/images/2017/06/29/business/30WHEELS1/30WHEELS1-mediumThreeByTwo210.jpg",
                        "format": "mediumThreeByTwo210",
                        "height": 140,
                        "width": 210
                    },
                    {
                        "url": "https://static01.nyt.com/images/2017/06/29/business/30WHEELS1/30WHEELS1-mediumThreeByTwo440.jpg",
                        "format": "mediumThreeByTwo440",
                        "height": 293,
                        "width": 440
                    }
                ]
            }
        ]
    },
    {
        "url": "https://www.nytimes.com/2017/07/06/automobiles/autoreviews/video-review-volvo-v90-cross-country-rolls-into-suv-territory.html",
        "adx_keywords": "Automobiles;Volvo Car Corp;2017 Volvo V90 Cross Country",
        "column": "Driven",
        "section": "Automobiles",
        "byline": "By TOM VOELK",
        "type": "Article",
        "title": "Video Review: Volvo V90 Cross Country Rolls Into S.U.V. Territory",
        "abstract": "Americans are buying S.U.V.s and crossovers in record numbers, but it’s worth keeping an open mind about Volvo’s station wagon.",
        "published_date": "2017-07-06",
        "source": "The New York Times",
        "id": 100000005194798,
        "asset_id": 100000005194798,
        "views": 4,
        "des_facet": [
            "AUTOMOBILES"
        ],
        "org_facet": [
            "VOLVO CAR CORP"
        ],
        "per_facet": "",
        "geo_facet": "",
        "media": ""
    },
    {
        "url": "https://www.nytimes.com/2017/06/08/automobiles/saab-autos-cars.html",
        "adx_keywords": "Automobiles;Saab Automobile AB;Bankruptcies;Portland (Ore)",
        "column": "Wheels",
        "section": "Automobiles",
        "byline": "By NICK KURCZEWSKI",
        "type": "Article",
        "title": "In Portland, a Rare Outpost for Die-Hard Saab Fans",
        "abstract": "The Swedish automaker filed for bankruptcy in 2011, leaving a vanishingly small number of dealerships for fans of the company.",
        "published_date": "2017-06-08",
        "source": "The New York Times",
        "id": 100000005095241,
        "asset_id": 100000005095241,
        "views": 5,
        "des_facet": [
            "AUTOMOBILES",
            "BANKRUPTCIES"
        ],
        "org_facet": [
            "SAAB AUTOMOBILE AB"
        ],
        "per_facet": "",
        "geo_facet": [
            "PORTLAND (ORE)"
        ],
        "media": [
            {
                "type": "image",
                "subtype": "photo",
                "caption": "Saab filed for bankruptcy in 2011, but Garry Small Saab in Portland, Ore., has continued to service and sell the brand’s cars.",
                "copyright": "Amanda Lucier for The New York Times",
                "approved_for_syndication": 1,
                "media-metadata": [
                    {
                        "url": "https://static01.nyt.com/images/2017/06/08/business/09WHEELS1/09WHEELS1-thumbStandard.jpg",
                        "format": "Standard Thumbnail",
                        "height": 75,
                        "width": 75
                    },
                    {
                        "url": "https://static01.nyt.com/images/2017/06/08/business/09WHEELS1/09WHEELS1-mediumThreeByTwo210.jpg",
                        "format": "mediumThreeByTwo210",
                        "height": 140,
                        "width": 210
                    },
                    {
                        "url": "https://static01.nyt.com/images/2017/06/08/business/09WHEELS1/09WHEELS1-mediumThreeByTwo440.jpg",
                        "format": "mediumThreeByTwo440",
                        "height": 293,
                        "width": 440
                    }
                ]
            }
        ]
    }
]

}

Type Adapter's read method

public List<Article> read(JsonReader in) throws IOException {

    final List<Article> article_list = new ArrayList<>();
    in.beginObject();
    while(in.hasNext()){
        switch (in.nextName()){
            case "results":
                in.beginArray();
                while(in.hasNext()){
                    final Article article = new Article();
                    in.beginObject();
                    while (in.hasNext()){
                        switch (in.nextName()){

                            case "id":
                                article.setId(Long.valueOf(in.nextString()));
                                break;
                            case "title":
                                article.setTitle(in.nextString());
                                break;
                            case "url":
                                article.setUrl(in.nextString());
                                break;
                            case "adx_keywords":
                                article.setAdxKeywords(in.nextString());
                                break;
                            case "section":
                                article.setSection(in.nextString());
                                break;
                            case "byline":
                                article.setByline(in.nextString());
                                break;
                            case "abstract":
                                article.set_abstract(in.nextString());
                                break;
                            case "published_date":
                                article.setPublishedDate(in.nextString());
                                break;
                            case "media":
                                if(!in.peek().equals(JsonToken.BEGIN_ARRAY)){
                                    in.skipValue();
                                    break;
                                }
                                in.beginArray();
                                final ArrayList<Media> media_list = new ArrayList<>();
                                while(in.hasNext()){
                                    in.beginObject();
                                    while(in.hasNext()){
                                        switch (in.nextName()){
                                            case "media-metadata":
                                                in.beginArray();
                                                while(in.hasNext()){
                                                    in.beginObject();
                                                    final Media media = new Media();
                                                    while(in.hasNext()){
                                                        switch (in.nextName()){
                                                            case "url":
                                                                media.setUrl(in.nextString());
                                                                break;
                                                            case "format":
                                                                media.setFormat(in.nextString());
                                                                break;
                                                            default:
                                                                in.skipValue();
                                                                break;
                                                        }
                                                    }
                                                    in.endObject();
                                                    media_list.add(media);
                                                }
                                                in.endArray();
                                                break;
                                            default:
                                                in.skipValue();
                                                break;
                                        }
                                    }
                                    in.endObject();
                                }
                                in.endArray();
                                for(Media m : media_list){
                                    if(m.getFormat().equals("mediumThreeByTwo440")){
                                        article.setMedia(m);
                                        break;
                                    }
                                }
                                break;

                            default:
                                in.skipValue();
                                break;
                        }/**/
                    }
                    in.endObject();
                    article_list.add(article);
                }
                in.endArray();
                break;
            default:
                in.skipValue();
                break;

        }

    }
    in.endObject();

    Timber.d(article_list.toString());
    return article_list;
}

Article and Media classes (no methods)

public class Article {
private Long id;
private String url;
private String adxKeywords;
private String section;
private String byline;
private String title;
private String _abstract;
private String publishedDate;
private Media media;
}

public class Media {
private String url;
private String format;
}

Retrofit Builder

final GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Article.class,new ArticleTypeAdapter());
    final Gson gson = gsonBuilder.create();
    retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();
David Pena
  • 163
  • 3
  • 9

1 Answers1

-2

You need do something like this:-

public class Response{
    public String status;
    public String copyright;
    public int num_results;
    public Article[] results;
}

You have to pass a response class with the same structure of response you are receiving from the server. Please follow the steps:-

You don't required a different type adapter for each request you can simply create the retrofit with default Gson converter factory like this:-

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addConverterFactory(GsonConverterFactory.create())
    .build();

Now you need to define a Service for API request like this:-

public class ArticleService{
   @GET("endpoint")//What ever is your request type
   Call<Response> getArticals(/** body query param or other required data**// )
}

After that you request creation will be something like this:-

ArticleService service = retrofit.create(ArticleService.class);
VikasGoyal
  • 3,308
  • 1
  • 22
  • 42
  • That could work, yes. But I would have it would be ugly to have a class with a lot of attributes unused. I solved the problem, and edit the post. Thank you – David Pena Jul 07 '17 at 04:39
  • If your API is sending some attributes then they shouldn't be ugly if you feel ugly then it's time to change the structure of API but instead of creating multiple GSON adapters don't you think it's better to have a class which can work with default adapter. – VikasGoyal Jul 07 '17 at 04:45
  • 1
    Im using the NewYorkTimesAPI for this project. But Im not going to use all the atributes. And Im only using this one GSON Adapter to fetch news. For me the code looks more clean without all of the data that it comes with it. – David Pena Jul 07 '17 at 05:21
  • I gave a downvote for not answering the question and instead reposting the retrofit docs. OP was already aware of this possibility which is why he asked for how to do it another way (which is certainly more clean and you often can't change public APIs) – Thomas Feb 04 '18 at 16:49