skip to Main Content

All the while, I’m using retrofit and JSON to POJO tool, in order to have Android app talk with API server.

The API server is using JSON as response.

Recently, I encounter stock related response, which looks like this

{
    "APPL": {
            "quote": {
                "symbol": "AAPL",
                "lastPrice": 1.23
            },
            "stats": {
                "dividendRate":2.52
            }
    },

    "GOOGL": {
            "quote": {
                "symbol": "GOOGL",
                "lastPrice": 4.56
            },
            "stats": {
                "dividendRate":7.89
            }
    }
}

APPL and GOOGL, are company stock codes. I view them as value. Hence, I don’t expect them to be placed at JSON’s name field (Left hand side)

I can hardly generate a suitable set of classes for the above response.

If I plug the JSON output directly to http://www.jsonschema2pojo.org/ , the result I’m getting are

public class BatchResponse {

    @SerializedName("APPL")
    @Expose
    private APPL aPPL;
    @SerializedName("GOOGL")
    @Expose
    private GOOGL gOOGL;

    public APPL getAPPL() {
        return aPPL;
    }

    public void setAPPL(APPL aPPL) {
        this.aPPL = aPPL;
    }

    public GOOGL getGOOGL() {
        return gOOGL;
    }

    public void setGOOGL(GOOGL gOOGL) {
        this.gOOGL = gOOGL;
    }

}

public class APPL {

    @SerializedName("quote")
    @Expose
    private Quote quote;
    @SerializedName("stats")
    @Expose
    private Stats stats;

    public Quote getQuote() {
        return quote;
    }

    public void setQuote(Quote quote) {
        this.quote = quote;
    }

    public Stats getStats() {
        return stats;
    }

    public void setStats(Stats stats) {
        this.stats = stats;
    }

}

public class GOOGL {

    @SerializedName("quote")
    @Expose
    private Quote quote;
    @SerializedName("stats")
    @Expose
    private Stats stats;

    public Quote getQuote() {
        return quote;
    }

    public void setQuote(Quote_ quote) {
        this.quote = quote;
    }

    public Stats getStats() {
        return stats;
    }

    public void setStats(Stats stats) {
        this.stats = stats;
    }

}

public class Quote {

    @SerializedName("symbol")
    @Expose
    private String symbol;
    @SerializedName("lastPrice")
    @Expose
    private double lastPrice;

    public String getSymbol() {
        return symbol;
    }

    public void setSymbol(String symbol) {
        this.symbol = symbol;
    }

    public double getLastPrice() {
        return lastPrice;
    }

    public void setLastPrice(double lastPrice) {
        this.lastPrice = lastPrice;
    }

}

public class Stats {

    @SerializedName("dividendRate")
    @Expose
    private double dividendRate;

    public double getDividendRate() {
        return dividendRate;
    }

    public void setDividendRate(double dividendRate) {
        this.dividendRate = dividendRate;
    }

}

Hence, if I have Tesla, Facebook, … stocks, I have to create new classes for each stock.

I was wondering, how I suppose to handle the above response using retrofit? Is there any workaround I can do on the above API response, so that I can handle them gracefully using retrofit?

Does it make sense, if I can propose the following response format to the API designer, so that application developer can handle such response easier?

{
    "batch" : [
        {
            "quote" : {
                "symbol": "AAPL",
                "lastPrice": 1.23
            },
            "stats" : {
                "dividendRate":2.52
            }
        },
        {
            "quote" : {
                "symbol": "GOOGL",
                "lastPrice": 4.56
            },
            "stats" : {
                "dividendRate":7.89
            }
        }
    ]
}

If using the above proposed API response, I can generate a better POJO classes.

public class BatchResponse {

    @SerializedName("batch")
    @Expose
    private List<Batch> batch = null;

    public List<Batch> getBatch() {
        return batch;
    }

    public void setBatch(List<Batch> batch) {
        this.batch = batch;
    }

}

public class Quote {

    @SerializedName("symbol")
    @Expose
    private String symbol;
    @SerializedName("lastPrice")
    @Expose
    private double lastPrice;

    public String getSymbol() {
        return symbol;
    }

    public void setSymbol(String symbol) {
        this.symbol = symbol;
    }

    public double getLastPrice() {
        return lastPrice;
    }

    public void setLastPrice(double lastPrice) {
        this.lastPrice = lastPrice;
    }

}

public class Stats {

    @SerializedName("dividendRate")
    @Expose
    private double dividendRate;

    public double getDividendRate() {
        return dividendRate;
    }

    public void setDividendRate(double dividendRate) {
        this.dividendRate = dividendRate;
    }

}

3

Answers


  1. Chosen as BEST ANSWER

    I manage to find a workaround.

    It is using Map of String to BatchResponse

    It looks like

    Call<Map<String, BatchResponse>> batchMultipleQuoteStats(@Query("symbols") String symbols);
    

    The BatchResponse class is

    public class BatchResponse {
    
        @SerializedName("quote")
        @Expose
        private Quote quote;
        @SerializedName("stats")
        @Expose
        private Stats stats;
    
        public Quote getQuote() {
            return quote;
        }
    
        public void setQuote(Quote_ quote) {
            this.quote = quote;
        }
    
        public Stats getStats() {
            return stats;
        }
    
        public void setStats(Stats stats) {
            this.stats = stats;
        }
    
    }
    

  2. how I suppose to handle the above response using retrofit?

    – You just need to use the same class. just change the SerializedName(“APPL”) eg for Tesla SerializedName(“TESLA”) as we are getting the same response inside. so we dont need to create the whole class again and again.

    @SerializedName("APPL")
    @Expose
    private APPL aPPL;
    @SerializedName("GOOGL")
    @Expose
    private APPL aPPL;
    

    Does it make sense, if I can propose the following response format to the API designer, so that application developer can handle such response easier?

    • Yes, you can use this format. It is perfect solution to make the json dynamic.
    Login or Signup to reply.
  3. More than an answer this might come across as a suggestion.

    The developer must have some reason behind the way the code was developed for creating the JSON response. Questions you could ask are if some other application is also latching on to the response in this format, or if there are applications under development which require data in this format etc. If there are other applications that consume the data, then the answer by @Harminder Singh seems the right way. If this response is consumed just by the application being developed by you, I do not see any reason in having it the way you want it and you can ask the developer to change it to your specification.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search