1
votes

I'm writing some JSON parsing code in java, and I've got a couple methods where the only difference between them is whether they return JSONObject or JSONArray. I'm trying to go from this:

  private JSONArray getJsonArray(String path) throws IOException {
    HttpGet httpget = new HttpGet(path);
    httpget.setConfig(requestConfig);

    try (CloseableHttpClient httpClient = httpClientBuilder.build()) {
      try (CloseableHttpResponse result = httpClient.execute(apiHost, httpget)) {
        return new JSONArray(new JSONTokener(result.getEntity().getContent()));
      }
    }
  }

  private JSONObject getJsonObject(String path) throws IOException {
    HttpGet httpget = new HttpGet(path);
    httpget.setConfig(requestConfig);

    try (CloseableHttpClient httpClient = httpClientBuilder.build()) {
      try (CloseableHttpResponse result = httpClient.execute(apiHost, httpget)) {
        return new JSONObject(new JSONTokener(result.getEntity().getContent()));
      }
    }
  }

to this (not valid code):

  private <T> get(String path, Class<T> type) throws IOException {
    HttpGet httpget = new HttpGet(path);
    httpget.setConfig(requestConfig);

    try (CloseableHttpClient httpClient = httpClientBuilder.build()) {
      try (CloseableHttpResponse result = httpClient.execute(apiHost, httpget)) {
        return new T(new JSONTokener(result.getEntity().getContent()));
      }
    }
  }

How do I properly initialize a new object of type T with parameters? Can I somehow limit the possible values of T to JSONObject / JSONArray? I know of <T extends Something> form, but those two seem to inherit straight from Object with no common interfaces :(

1
org.json (which I'm assuming is what you are using) does not support generic deserialization. Use something more sophisticated like Jackson or Gson. - Sotirios Delimanolis
@SotiriosDelimanolis I don't think this is a dupe of that question. The other question is basically "how to parse JSON", while this is "how to create an instance of a class corresponding to a generic parameter", which is basically independent of JSON entirely. - tobias_k
@SotiriosDelimanolis the code was provided only to better illustrate my point. tobias_k got the essence of my question right. - Toms Mikoss
I see now. Re-opened. - Sotirios Delimanolis

1 Answers

1
votes

You could use reflection to get and invoke a matching constructor, if any, and raise an exception if no such constructor exists.

private <T> T get(String path, Class<T> type) throws IOException {
    HttpGet httpget = new HttpGet(path);
    httpget.setConfig(requestConfig);

    try (CloseableHttpClient httpClient = httpClientBuilder.build()) {
        try (CloseableHttpResponse result = httpClient.execute(apiHost, httpget)) {
            Constructor<T> constructor = type.getConstructor(JSONTokener.class);
            return constructor.newInstance(new JSONTokener(result.getEntity().getContent()));
        } catch (ReflectiveOperationException e) {
            throw new IllegalArgumentException("Provided result class does not accept JSONTokener parameter.");
        }
    }
}

Note that this is sort of duck typing, i.e. you do not really limit the type to JSONObject or JSONArray but instead everything that provides the respective constructor is okay.