1

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 :(

Toms Mikoss
  • 9,097
  • 10
  • 29
  • 41
  • `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 Jul 20 '15 at 15:18
  • @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 Jul 20 '15 at 15:34
  • @SotiriosDelimanolis the code was provided only to better illustrate my point. tobias_k got the essence of my question right. – Toms Mikoss Jul 20 '15 at 16:26
  • I see now. Re-opened. – Sotirios Delimanolis Jul 20 '15 at 16:28

1 Answers1

1

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.

tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • Thank you, this does exactly what I was looking for. And given that most of my programming experience is with duck-typed languages, I'm fine with this. My train of thought with limiting to `JSONObject`/`JSONArray` was to allow compiler to infer that T will always have the proper constructor, rather than limit how the function can get called. – Toms Mikoss Jul 20 '15 at 16:49