Expected BEGIN_OBJECT, but was STRING with custom TypeAdapter

So I am trying to solve the problem I have with my custom TypeAdapter with Gson and Retrofit. I keep getting the error Expected BEGIN_OBJECT but was STRING

, but I'm not sure how to solve it. I was getting an error before Expected BEGIN_ARRAY but was STRING

and I resolved this. I'm not sure if it should be the same as with this new error. I have listed my classes below and any help is appreciated. This is how my json looks like here: http://pastie.org/private/bfo86iznldacbz10rtsdsg The main problem is the multimedia box in the json. It is an empty string if there is no value, but if there is a value, it returns a jsonarray containing jsonobjects.

ArrayAdapter.java

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;


public class ArrayAdapter<T> extends TypeAdapter<List<T>> {
    private Class<T> adapterclass;

    public ArrayAdapter(Class<T> adapterclass) {
        this.adapterclass = adapterclass;
    }

    public List<T> read(JsonReader reader) throws IOException {

        List<T> list = new ArrayList<T>();

        Gson gson = new GsonBuilder()
                .registerTypeAdapterFactory(new ArrayAdapterFactory())
                .create();

        if (reader.peek() == JsonToken.STRING) {
            T inning = gson.fromJson(reader, adapterclass);
            list.add(inning);
        } else if (reader.peek() == JsonToken.BEGIN_ARRAY) {

            reader.beginArray();
            while (reader.hasNext()) {
                T inning = gson.fromJson(reader, adapterclass);
                list.add(inning);
            }
            reader.endArray();

        } else if (reader.peek() == JsonToken.BEGIN_OBJECT) {
            reader.beginObject();
            while(reader.hasNext()) {
            }
        }

        return list;
    }

    public void write(JsonWriter writer, List<T> value) throws IOException {

    }

}

      

ArraryAdapterFactory

import java.lang.reflect.ParameterizedType;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;

public class ArrayAdapterFactory implements TypeAdapterFactory {

    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {

        TypeAdapter<T> typeAdapter = null;

        try {
            if (type.getRawType() == List.class)
                typeAdapter = new ArrayAdapter(
                        (Class) ((ParameterizedType) type.getType())
                                .getActualTypeArguments()[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return typeAdapter;


    }

}

      

Times.java

public class NYTimes {

    // uses the new york times api
    // gets the top stores on the nytimes homepage
    private static final String API_URL = "http://api.nytimes.com/svc/news/v3/content/all/all";

    static Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ArrayAdapterFactory()).create();

    private static final RestAdapter REST_ADAPTER = new RestAdapter.Builder()
            .setConverter(new GsonConverter(gson))
            .setEndpoint(API_URL)
            .build();

    private static final NYTimesService SERVICE = REST_ADAPTER.create(NYTimesService.class);

    public static NYTimesService getService() {
        return SERVICE;
    }
}

      

POJO classes

News.java

public class News {

    @Expose
    private String status;
    @Expose
    private String copyright;
    @SerializedName("num_results")
    @Expose
    private Integer numResults;
    @Expose
    private List<Result> results = new ArrayList<Result>();

    /**
     *
     * @return
     * The status
     */
    public String getStatus() {
        return status;
    }

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

    /**
     *
     * @return
     * The copyright
     */
    public String getCopyright() {
        return copyright;
    }

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

    /**
     *
     * @return
     * The numResults
     */
    public Integer getNumResults() {
        return numResults;
    }

    /**
     *
     * @param numResults
     * The num_results
     */
    public void setNumResults(Integer numResults) {
        this.numResults = numResults;
    }

    /**
     *
     * @return
     * The results
     */
    public List<Result> getResults() {
        return results;
    }

    /**
     *
     * @param results
     * The results
     */
    public void setResults(List<Result> results) {
        this.results = results;
    }

}

      

Result.java

public class Result {
    @Expose
    private String section;
    @Expose
    private String subsection;
    @Expose
    private String title;
    @SerializedName("abstract")
    @Expose
    private String _abstract;
    @Expose
    private String url;
    @Expose
    private String byline;
    @SerializedName("thumbnail_standard")
    @Expose
    private String thumbnailStandard;
    @SerializedName("item_type")
    @Expose
    private String itemType;
    @Expose
    private String source;
    @SerializedName("updated_date")
    @Expose
    private String updatedDate;
    @SerializedName("created_date")
    @Expose
    private String createdDate;
    @SerializedName("published_date")
    @Expose
    private String publishedDate;
    @SerializedName("material_type_facet")
    @Expose
    private String materialTypeFacet;
    @Expose
    private String kicker;
    @Expose
    private String subheadline;
    @SerializedName("des_facet")
    @Expose
    private List<String> desFacet = new ArrayList<>();
    @SerializedName("org_facet")
    @Expose
    private List<String> orgFacet = new ArrayList<>();
    @SerializedName("per_facet")
    @Expose
    private List<String> perFacet = new ArrayList<>();
    @SerializedName("geo_facet")
    @Expose
    private List<String> geoFacet = new ArrayList<>();
    @SerializedName("related_urls")
    @Expose
    private Object relatedUrls;
    @Expose
    private List<Multimedium> multimedia;

    /**
     *
     * @return
     * The section
     */
    public String getSection() {
        return section;
    }

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

    /**
     *
     * @return
     * The subsection
     */
    public String getSubsection() {
        return subsection;
    }

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

    /**
     *
     * @return
     * The title
     */
    public String getTitle() {
        return title;
    }

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

    /**
     *
     * @return
     * The _abstract
     */
    public String getAbstract() {
        return _abstract;
    }

    /**
     *
     * @param _abstract
     * The abstract
     */
    public void setAbstract(String _abstract) {
        this._abstract = _abstract;
    }

    /**
     *
     * @return
     * The url
     */
    public String getUrl() {
        return url;
    }

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

    /**
     *
     * @return
     * The byline
     */
    public String getByline() {
        return byline;
    }

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

    /**
     *
     * @return
     * The thumbnailStandard
     */
    public String getThumbnailStandard() {
        return thumbnailStandard;
    }

    /**
     *
     * @param thumbnailStandard
     * The thumbnail_standard
     */
    public void setThumbnailStandard(String thumbnailStandard) {
        this.thumbnailStandard = thumbnailStandard;
    }

    /**
     *
     * @return
     * The itemType
     */
    public String getItemType() {
        return itemType;
    }

    /**
     *
     * @param itemType
     * The item_type
     */
    public void setItemType(String itemType) {
        this.itemType = itemType;
    }

    /**
     *
     * @return
     * The source
     */
    public String getSource() {
        return source;
    }

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

    /**
     *
     * @return
     * The updatedDate
     */
    public String getUpdatedDate() {
        return updatedDate;
    }

    /**
     *
     * @param updatedDate
     * The updated_date
     */
    public void setUpdatedDate(String updatedDate) {
        this.updatedDate = updatedDate;
    }

    /**
     *
     * @return
     * The createdDate
     */
    public String getCreatedDate() {
        return createdDate;
    }

    /**
     *
     * @param createdDate
     * The created_date
     */
    public void setCreatedDate(String createdDate) {
        this.createdDate = createdDate;
    }

    /**
     *
     * @return
     * The publishedDate
     */
    public String getPublishedDate() {
        return publishedDate;
    }

    /**
     *
     * @param publishedDate
     * The published_date
     */
    public void setPublishedDate(String publishedDate) {
        this.publishedDate = publishedDate;
    }

    /**
     *
     * @return
     * The materialTypeFacet
     */
    public String getMaterialTypeFacet() {
        return materialTypeFacet;
    }

    /**
     *
     * @param materialTypeFacet
     * The material_type_facet
     */
    public void setMaterialTypeFacet(String materialTypeFacet) {
        this.materialTypeFacet = materialTypeFacet;
    }

    /**
     *
     * @return
     * The kicker
     */
    public String getKicker() {
        return kicker;
    }

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

    /**
     *
     * @return
     * The subheadline
     */
    public String getSubheadline() {
        return subheadline;
    }

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

    /**
     *
     * @return
     * The desFacet
     */
    public List<String> getDesFacet() {
        return desFacet;
    }

    /**
     *
     * @param desFacet
     * The des_facet
     */
    public void setDesFacet(List<String> desFacet) {
        this.desFacet = desFacet;
    }

    /**
     *
     * @return
     * The orgFacet
     */
    public List<String> getOrgFacet() {
        return orgFacet;
    }

    /**
     *
     * @param orgFacet
     * The org_facet
     */
    public void setOrgFacet(List<String> orgFacet) {
        this.orgFacet = orgFacet;
    }

    /**
     *
     * @return
     * The perFacet
     */
    public List<String> getPerFacet() {
        return perFacet;
    }

    /**
     *
     * @param perFacet
     * The per_facet
     */
    public void setPerFacet(List<String> perFacet) {
        this.perFacet = perFacet;
    }

    /**
     *
     * @return
     * The geoFacet
     */
    public List<String> getGeoFacet() {
        return geoFacet;
    }

    /**
     *
     * @param geoFacet
     * The geo_facet
     */
    public void setGeoFacet(List<String> geoFacet) {
        this.geoFacet = geoFacet;
    }

    /**
     *
     * @return
     * The relatedUrls
     */
    public Object getRelatedUrls() {
        return relatedUrls;
    }

    /**
     *
     * @param relatedUrls
     * The related_urls
     */
    public void setRelatedUrls(Object relatedUrls) {
        this.relatedUrls = relatedUrls;
    }

    /**
     *
     * @return
     * The multimedia
     */
    public List<Multimedium> getMultimedia() {
        return multimedia;
    }

    /**
     *
     * @param multimedia
     * The multimedia
     */
    public void setMultimedia(List<Multimedium> multimedia) {
        this.multimedia = multimedia;
    }
}

      

Multimedium.java

public class Multimedium {
    @Expose
    private String url;
    @Expose
    private String format;
    @Expose
    private Integer height;
    @Expose
    private Integer width;
    @Expose
    private String type;
    @Expose
    private String subtype;
    @Expose
    private Object caption;
    @Expose
    private Object copyright;

    /**
     *
     * @return
     * The url
     */
    public String getUrl() {
        return url;
    }

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

    /**
     *
     * @return
     * The format
     */
    public String getFormat() {
        return format;
    }

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

    /**
     *
     * @return
     * The height
     */
    public Integer getHeight() {
        return height;
    }

    /**
     *
     * @param height
     * The height
     */
    public void setHeight(Integer height) {
        this.height = height;
    }

    /**
     *
     * @return
     * The width
     */
    public Integer getWidth() {
        return width;
    }

    /**
     *
     * @param width
     * The width
     */
    public void setWidth(Integer width) {
        this.width = width;
    }

    /**
     *
     * @return
     * The type
     */
    public String getType() {
        return type;
    }

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

    /**
     *
     * @return
     * The subtype
     */
    public String getSubtype() {
        return subtype;
    }

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

    /**
     *
     * @return
     * The caption
     */
    public Object getCaption() {
        return caption;
    }

    /**
     *
     * @param caption
     * The caption
     */
    public void setCaption(Object caption) {
        this.caption = caption;
    }

    /**
     *
     * @return
     * The copyright
     */
    public Object getCopyright() {
        return copyright;
    }

    /**
     *
     * @param copyright
     * The copyright
     */
    public void setCopyright(Object copyright) {
        this.copyright = copyright;
    }
}

      

I know there is a lot of code to go through, but I am looking for any help to solve this problem, so I try to be as clear as I can.

+3


source to share


1 answer


I managed to get a solution by doing a custom JsonDeserializer

one and then adding this to RestAdapter

like so:

public class ResultsDeserializerJson implements JsonDeserializer<Result> {

@Override
    public Result deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        JsonElement titleElement = json.getAsJsonObject().get("title");
        JsonElement multimediaElement = json.getAsJsonObject().get("multimedia");
        if (multimediaElement.isJsonArray()) {
            return new Result(
                titleElement.toString(),
                (Multimedia[]) context.deserialize(multimediaElement.getAsJsonArray(), Multimedia[].class));
        } else if (multimediaElement.getAsString().equals("")) {
            Multimedia multimedia = new Multimedia();
            multimedia.setFormat("");
            multimedia.setUrl("");
            return new Result(titleElement.toString(), multimedia);
        } else {
            Log.d("ResultsDeserializerJson", multimediaElement.toString());
            throw new JsonParseException("Unsupported type of multimedia element");
        }
    }
}

      

Internally, Result.java

I added the following constructor (note that you can add additional parameters for things like section, subsection, etc.):

public Result(String title, Multimedia ... multimedia) {
    this.mTitle = title.replace("\"", "");;
    mMultimedia = Arrays.asList(multimedia);
}

      

Then for mine RestAdapter

I'll do the following:



private NYTimesService() {

    Gson gson = new GsonBuilder()
            .registerTypeAdapter(Result.class, new ResultsDeserializerJson()).create();

    mAsyncRestAdapter = new RestAdapter.Builder()
            .setEndpoint(API_URL)
            .setConverter(new GsonConverter(gson))
            .setRequestInterceptor(new RequestInterceptor() {
                @Override
                public void intercept(RequestFacade request) {
                    request.addEncodedQueryParam("api-key", API_KEY);
                }
            })
            .setLogLevel(RestAdapter.LogLevel.FULL)
            .build();
}

      

I created a dedicated app to get this working and here's an open repository open:

SampleNYTimesApp Repo

This is still a somewhat hackish solution and I feel it is not the most optimal, but I was able to get the following in the time I figured out the solution:

enter image description here

+1


source







All Articles