title: Simplest way to call REST API and parse JSON data with Java
body: <p>I'm trying to do something that is trivial in JavaScript, but seems complicated with Java. I'm hoping someone can point out how to do it simply in Java as well.</p>
  
  <p>I want to call a REST JSON API, e.g. <code>https://images-api.nasa.gov/search?q=clouds</code></p>
  
  <p>I get back a data structure that, in a simplified form, looks something like this:</p>
  
  <pre><code>{
    "collection": {
      "items": [
        {
          "links": [
            {
              "href": "https://images-assets.nasa.gov/image/cloud-vortices_22531636120_o/cloud-vortices_22531636120_o~thumb.jpg",
              "rel": "preview"
            }
          ]
        }
      ]
    }
  }
  </code></pre>
  
  <p>In Java, I want to call the URL and get the <code>href</code> Strings as a List.</p>
  
  <p>In JavaScript, I would simply write</p>
  
  <pre><code>fetch("https://images-api.nasa.gov/search?q=moon")
    .then(data =&gt; data.json())
    .then(data =&gt; {
      const items = data
        .collection
        .items
        .map(item =&gt; item.links)
        .flat()
        .filter(link =&gt; link.rel === "preview")
        .map(link =&gt; link.href);
  
      // do something with "items"
  })
  </code></pre>
  
  <h2>1. My initial solution</h2>
  
  <p>With a little searching, I found this approach, which seems to be going in the right direction, but still very verbose.</p>
  
  <pre><code>String uri = "https://images-api.nasa.gov/search?q=clouds";
  List&lt;String&gt; hrefs = new ArrayList&lt;&gt;();
  
  try {
      // make the GET request
      URLConnection request = new URL(uri).openConnection();
      request.connect();
      InputStreamReader inputStreamReader = new InputStreamReader((InputStream) request.getContent());
  
      // map to GSON objects
      JsonElement root = new JsonParser().parse(inputStreamReader);
  
      // traverse the JSON data 
      JsonArray items = root
              .getAsJsonObject()
              .get("collection").getAsJsonObject()
              .get("items").getAsJsonArray();
  
      // flatten nested arrays
      JsonArray links = new JsonArray();
      items.forEach(item -&gt; links.addAll(item
              .getAsJsonObject()
              .get("links")
              .getAsJsonArray()));
  
      // filter links with "href" properties
      links.forEach(link -&gt; {
          JsonObject linkObject = link.getAsJsonObject();
          String relString = linkObject.get("rel").getAsString();
          if ("preview".equals(relString)) {
              hrefs.add(linkObject.get("href").getAsString());
          }
      });
  
  } catch (IOException e) {
      e.printStackTrace();
  }
  
  return hrefs;
  </code></pre>
  
  <p>My remaining questions are:</p>
  
  <ul>
  <li>Is there a way to
  use RestTemplate or some other library to make the GET Request less
  verbose and still keep the generic flexibility of GSON? </li>
  <li>Is there a way to flatten nested JsonArrays and/or filter JsonArrays with GSON so I don't need to
  create additional temporary JsonArrays?</li>
  <li>Are there any other ways to make the code less verbose ?</li>
  </ul>
  
  <hr>
  
  <h1>Edited</h1>
  
  <p>The following sections were added after reading the comments and answers below.</p>
  
  <hr>
  
  <h2>2. Less verbose solution</h2>
  
  <p>(as proposed in the answer by @diutsu)</p>
  
  <pre><code>List&lt;String&gt; hrefs = new ArrayList&lt;&gt;();
  String json = new RestTemplate().getForObject("https://images-api.nasa.gov/search?q=clouds", String.class);
  new JsonParser().parse(json).getAsJsonObject()
      .get("collection").getAsJsonObject()
      .get("items").getAsJsonArray()
      .forEach(item -&gt; item.getAsJsonObject()
          .get("links").getAsJsonArray()
          .forEach(link -&gt; {
              JsonObject linkObject = link.getAsJsonObject();
              String relString = linkObject.get("rel").getAsString();
              if ("preview".equals(relString)) {
                  hrefs.add(linkObject.get("href").getAsString());
              }
          })
      );
  return hrefs;
  </code></pre>
  
  <h2>3. Solution using Mapper POJOs</h2>
  
  <p>(inspired by @JBNizet and @diutsu)</p>
  
  <p>The actuall GET request and tranformation is now a one-liner and almost identical to the JavaScript code I posed above, ... </p>
  
  <pre><code>return new RestTemplate().getForObject("https://images-api.nasa.gov/search?q=clouds", CollectionWrapper.class)
      .getCollection()
      .getItems().stream()
      .map(Item::getLinks)
      .flatMap(List::stream)
      .filter(item -&gt; "preview".equals(item.getRel()))
      .map(Link::getHref)
      .collect(Collectors.toList());
  </code></pre>
  
  <p>... but for this to work, I had to create the following 4 classes:</p>
  
  <p><strong>CollectionWrapper</strong></p>
  
  <pre><code>public class CollectionWrapper {
  
      private Collection collection;
  
      public CollectionWrapper(){}
  
      public CollectionWrapper(Collection collection) {
          this.collection = collection;
      }
  
      public Collection getCollection() {
          return collection;
      }
  }
  </code></pre>
  
  <p><strong>Collection</strong></p>
  
  <pre><code>public class Collection {
  
      private List&lt;Item&gt; items;
  
      public Collection(){}
  
      public Collection(List&lt;Item&gt; items) {
          this.items = items;
      }
  
      public List&lt;Item&gt; getItems() {
          return items;
      }
  }
  </code></pre>
  
  <p><strong>Item</strong></p>
  
  <pre><code>public class Item {
  
      private List&lt;Link&gt; links;
  
      public Item(){}
  
      public Item(List&lt;Link&gt; links) {
          this.links = links;
      }
  
      public List&lt;Link&gt; getLinks() {
          return links;
      }
  }
  </code></pre>
  
  <p><strong>Link</strong></p>
  
  <pre><code>public class Link {
  
      private String href;
      private String rel;
  
      public Link() {}
  
      public Link(String href, String rel) {
          this.href = href;
          this.rel = rel;
      }
  
      public String getHref() {
          return href;
      }
  
      public String getRel() {
          return rel;
      }
  }
  </code></pre>
  
  <h2>4. Using Kotlin</h2>
  
  <p>(inspired by @NBNizet)</p>
  
  <pre><code>val collectionWrapper = RestTemplate().getForObject("https://images-api.nasa.gov/search?q=clouds", CollectionWrapper::class.java);
  return collectionWrapper
          ?.collection
          ?.items
          ?.map { item -&gt; item.links }
          ?.flatten()
          ?.filter { item -&gt; "preview".equals(item.rel) }
          ?.map { item -&gt; item.href }
          .orEmpty()
  </code></pre>
  
  <p>Using Kotlin makes the mapper classes simpler, even simpler than using Java with Lombok</p>
  
  <pre><code>data class CollectionWrapper(val collection: Collection)
  data class Collection(val items: List&lt;Item&gt;)
  data class Item(val links: List&lt;Link&gt;)
  data class Link(val rel: String, val href: String)
  </code></pre>
  
  <h2>5. Mapping directly to Map and List</h2>
  
  <p>I'm not convinced this is a good idea, but good to know it can be done:</p>
  
  <pre><code>return 
      ( (Map&lt;String, Map&lt;String, List&lt;Map&lt;String, List&lt;Map&lt;String, String&gt;&gt;&gt;&gt;&gt;&gt;) 
          new RestTemplate()
          .getForObject("https://images-api.nasa.gov/search?q=clouds", Map.class)
      )
      .get("collection")
      .get("items").stream()
      .map(item -&gt; item.get("links"))
      .flatMap(List::stream)
      .filter(link -&gt; "preview".equals(link.get("rel")))
      .map(link -&gt; link.get("href"))
      .collect(Collectors.toList());
  </code></pre>