Java 2

Week 8

  • JSON file input
  • Operations with open source APIs

Course Objectives

  • Students will demonstrate an understanding multitier software design

  • Students will understand the use of standard collections and generics in the Java programming language

  • Students will understand elementary Thread programming in Java

  • Students will create software that performs standard data operations with Java and a relational database

  • Students will use streams to communicate with sockets and files

  • Students will apply elementary XML functionality in Java

  • Students will understand the importance of using standard Object Oriented Programming (OOP) design tools such as the Unified Modeling Language (UML)

  • Students will demonstrate a preference for messages and Exceptions for communicating between components

  • Students will systematize Test Driven Development

What is JSON?

  • JavaScript Object Notation (JSON) is a text file format for storing and transmitting data objects.
  • JSON filenames use the extension .json
  • JSON can be used to get data for Java objects
  • A JavaScript object starts with { and ends with } 
  • Between the curly brackets are key:value pairs
    • keys must be strings
    • values can be strings, arrays, or objects
    • each key value pair must be separated by a comma.
  • Below is an example of JSON representing a Message object.
  • Here is a more complex example representing a Person object.
{role: "test", content: "example"}

What is an API?

  • An application programming interface (API) is a way for two or more programs to communicate with each other.
  • APIs offer data and services to software you create.
  • An API dictates how we need to write code to take advantage of that system's capabilities.

JSON API

application.properties

  • Change application.properties datasource.type to json
  • Add new properties for your API token and URL.

    • You will need the read access token, not the api key

datasource.type=json
json.apiReadAccessToken=<your-token>
json.apiURL=https://api.themoviedb.org/3/search/movie?

JSON Dependency

  • To get JSON from a web server, you can add this dependency to the pom.xml file
  • An alternative to Square OkHttp is Apache HttpClient

 

JsonMovieDAO

  • In the doa.impl package, create a new class called "JsonMovieDAO". Have this class implement the MovieDAO interface and the search method.

  • Set this class up similar to the XMLMovieDAO, but with two attributes, one for the URL and one for the key.

package edu.kirkwood.dao.impl;

import edu.kirkwood.dao.MovieDAO;
import edu.kirkwood.model.Movie;

import java.util.List;

public class JsonMovieDAO implements MovieDAO {
    private String apiURL;
    private String apiToken;

    public JsonMovieDAO(String apiURL, String apiToken) {
        this.apiURL = apiURL;
        this.apiToken = apiToken;
    }

    /**
     * Retrieves all movies from the data source that matches the title
     * @param title The movie title a user is searching for
     * @return A List<Movie> movies that matches the search title
     */
    @Override
    public List<Movie> search(String title) {
        return List.of();
    }
}

fetchRawData()

  • Create a getResponseBody method that takes in the title search String and returns a String output.

  • The responseBody will be the actual HTTP response from the web server.
  • Use an OkHttpClient object to make the GET request to the API.

  • The Request object must follow the API's documentation.

package edu.kirkwood.dao.impl;

import edu.kirkwood.dao.MovieDAO;
import edu.kirkwood.model.Movie;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

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

public class JsonMovieDAO implements MovieDAO {
    private String apiURL;
    private String apiToken;

    public JsonMovieDAO(String apiURL, String apiToken) {
        this.apiURL = apiURL;
        this.apiToken = apiToken;
    }

    /**
     * Retrieves all movies from the data source that matches the title
     * @param title The movie title a user is searching for
     * @return A List<Movie> movies that matches the search title
     */
    @Override
    public List<Movie> search(String title) {
        String responseBody = fetchRawData(title);
        System.out.println("Response Body: " + responseBody);
        return List.of();
    }

    /**
     * Retrieves all movies from the data source that matches the title
     * @param title The movie title a user is searching for
     * @return A Json String of movies that matches the search title
     */
    public String fetchRawData(String title) {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url(apiURL + "&query=" + title)
                .get()
                .addHeader("accept", "application/json")
                .addHeader("Authorization", "Bearer " + apiToken)
                .build();

        Response response = null;
        try {
            response = client.newCall(request).execute();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String responseBody = "";
        try {
            responseBody = response.body().string();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return responseBody;
    }
}

MovieDAOFactory

  • Update the getDAO() method to handle JSON.

  • Get the two json application properties.

  • Pass those values to the JsonMovieDAO constructor.

package edu.kirkwood.dao;

import edu.kirkwood.dao.impl.JsonMovieDAO;
import edu.kirkwood.dao.impl.MySQLMovieDAO;
import edu.kirkwood.dao.impl.XmlMovieDAO;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class MovieDAOFactory {
    private static Properties properties = new Properties();
    static {
        try(InputStream is = MovieDAOFactory.class.getClassLoader()
                .getResourceAsStream("application.properties")) {
            if(is == null) {
                throw new FileNotFoundException("application.properties file not found");
            }
            properties.load(is);
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * To retrieve a MovieDAO based on application.properties settings
     * @return A MovieDAO (XMLMovieDAO, MySQLMovieDAO, JSONMovieDAO, etc.)
     */
    public static MovieDAO getMovieDAO() {
        String dataSourceType = properties.getProperty("datasource.type");
        if(dataSourceType == null || dataSourceType.isEmpty()) {
            throw new IllegalArgumentException("datasource.type is required");
        }
        switch (dataSourceType.toUpperCase()) {
            case "XML":
                String apiURL = properties.getProperty("xml.apiURL");
                if(apiURL == null || apiURL.isEmpty()) {
                    throw new IllegalArgumentException("xml.apiURL is required");
                }
                return new XmlMovieDAO(apiURL);
            case "JSON":
                String jsonApiURL = properties.getProperty("json.apiURL");
                if(jsonApiURL == null || jsonApiURL.isEmpty()) {
                    throw new IllegalArgumentException("json.apiURL is required");
                }
                String jsonReadAccessToken = properties.getProperty("json.apiReadAccessToken");
                if(jsonReadAccessToken == null || jsonReadAccessToken.isEmpty()) {
                    throw new IllegalArgumentException("json.apiReadAccessToken");
                }
                return new JsonMovieDAO(jsonApiURL, jsonReadAccessToken);
            case "MYSQL":
                return new MySQLMovieDAO();
//            case "MONGODB":
//                break;
            default:
                throw new RuntimeException("Unsupported data source type: " + dataSourceType);
        }
    }
}

MyDataApp

  • Update the main method to call the JsonMovieDAO's search method.

  • Run the program. Raw movie data will display.

package edu.kirkwood;

import edu.kirkwood.dao.MovieDAO;
import edu.kirkwood.dao.MovieDAOFactory;
import edu.kirkwood.dao.impl.JsonMovieDAO;
import edu.kirkwood.dao.impl.MySQLMovieDAO;
import edu.kirkwood.dao.impl.XmlMovieDAO;
import edu.kirkwood.model.Movie;
import edu.kirkwood.model.xml.MovieSearchResult;

import java.util.ArrayList;
import java.util.List;

public class MyDataApp {
    public static void main(String[] args) {
        MovieDAO movieDAO = MovieDAOFactory.getMovieDAO();
        // Prompt the user for a movie title
        String title = "Short Circuit";
        List<Movie> results = new ArrayList<>();
        if(movieDAO instanceof XmlMovieDAO) {
            XmlMovieDAO xmlMovieDAO = (XmlMovieDAO) movieDAO;
            results = xmlMovieDAO.search(title);
        } else if(movieDAO instanceof MySQLMovieDAO) {
            MySQLMovieDAO mySQLMovieDAO = (MySQLMovieDAO) movieDAO;
            results = mySQLMovieDAO.search(title);
        } else if(movieDAO instanceof JsonMovieDAO) {
            JsonMovieDAO mySQLMovieDAO = (JsonMovieDAO) movieDAO;
            results = mySQLMovieDAO.search(title);
        }
        results.forEach(System.out::println);
    }
}

prettyPrintResponse

  • Raw JSON data is very hard to read.

  • In the JsonMovieDAO class, create a method called prettyPrintResponse().

  • Gson is used to read and format JSON.

package edu.kirkwood.dao.impl;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import edu.kirkwood.dao.MovieDAO;
import edu.kirkwood.model.Movie;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;
import java.time.LocalDate;
import java.util.List;

public class JsonMovieDAO implements MovieDAO {
    private String apiURL;
    private String apiToken;

    public JsonMovieDAO(String apiURL, String apiToken) {
        this.apiURL = apiURL;
        this.apiToken = apiToken;
    }

    /**
     * Retrieves all movies from the data source that matches the title
     * @param title The movie title a user is searching for
     * @return A List<Movie> movies that matches the search title
     */
    @Override
    public List<Movie> search(String title) {
        String responseBody = getResponseBody(title);
        prettyPrintResponse(responseBody);
        return List.of();
    }

    public void prettyPrintResponse(String responseBody) {
        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .create();
        JsonElement jsonElement = new JsonParser().parse(responseBody);
        String json = gson.toJson(jsonElement);
        System.out.print(json);
    }

    public String getResponseBody(String title) {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url(apiURL + "&query=" + title)
                .get()
                .addHeader("accept", "application/json")
                .addHeader("Authorization", "Bearer " + apiToken)
                .build();

        Response response = null;
        try {
            response = client.newCall(request).execute();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String responseBody = "";
        try {
            responseBody = response.body().string();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return responseBody;
    }
}

Models

  • Create a new models package called "json".

  • Create a new class called MovieSearchResult.

  • Add attributes representing a single Movie Json result.

  • Note that this API uses "overview" instead of "plot".

  • The LocalDate release_date is currently commented out. We'll handle that data type later.

package edu.kirkwood.model.json;

import java.time.LocalDate;

public class MovieSearchResult {
    private String id;
    private String title;
    private String overview;
//    private LocalDate release_date;

    public String getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public String getOverview() {
        return overview;
    }

//    public LocalDate getRelease_date() {
//        return release_date;
//    }

    @Override
    public String toString() {
        return "MovieSearchResult{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", overview='" + overview + '\'' +
//                ", release_date=" + release_date +
                '}';
    }

}

Models

  • Inside the "models.json" package, create a new class called TmdbMovieResponse.

  • Add an attribute representing a List of MovieSearchResult objects.

  • The word "results" is the name of the array returned from the API.

package edu.kirkwood.model.json;

import java.util.List;

public class TmdbMovieResponse {
    private List<MovieSearchResult> results;

    public List<MovieSearchResult> getResults() {
        return results;
    }
}

JsonMovieDAO

  • Update the search method to convert the raw data into a TmdbMovieResponse object.

  • Gson will take the value of the JSON's "results" array and set it to the TmdbMovieResponse's "results" List.

  • Gson will take the values of each movie id, title, and overview to MovieSearchResult object and automatically add it to the list.

  • We can loop through the List returned from getResults().

/**
 * Retrieves all movies from the data source that matches the title
 * @param title The movie title a user is searching for
 * @return A List<Movie> movies that matches the search title
 */
@Override
public List<Movie> search(String title) {
    String rawData = getRawData(title);
//        System.out.println(prettyFormatter(rawData));
    Gson gson = new GsonBuilder().create();
    TmdbMovieResponse movieResponse = null;
    try {
        movieResponse = gson.fromJson(rawData, TmdbMovieResponse.class);
    } catch (JsonSyntaxException | JsonIOException e) {
        // Will display an error if the json is not a valid 
        // representation for a TmdbMovieResponse object
        throw new RuntimeException(e);
    }
    movieResponse.getResults().forEach(result -> {
        System.out.println(result);
    });
    return List.of();
}

Handling LocalDate

  • In the json.MovieSearchResult class, uncomment the LocalDate release_date code.

  • If you run the program, you will get an error saying "com.google.gson.JsonIOException: Failed making field 'java.time.LocalDate#year' accessible; either increase its visibility or write a custom TypeAdapter for its declaring type."

package edu.kirkwood.model.json;

import java.time.LocalDate;

public class MovieSearchResult {
    private String id;
    private String title;
    private String overview;
    private LocalDate release_date;

    public String getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public String getOverview() {
        return overview;
    }

    public LocalDate getRelease_date() {
        return release_date;
    }

    @Override
    public String toString() {
        return "MovieSearchResult{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", overview='" + overview + '\'' +
                ", release_date=" + release_date +
                '}';
    }

}

custom TypeAdapter

  • In the DAO package, create a new class called JsonTypeAdapter.

  • Add a custom TypeAdapter class for any class in the java.time package, like LocalDate, LocalDateTime, Instant.

  • To put a class inside a class, we need to make them static.

  • The try-catch statements are needed in case a JSON object doesn't have a valid date, such as an empty string.

package edu.kirkwood.dao;

import com.google.gson.*;

import java.lang.reflect.Type;
import java.time.*;
import java.time.format.DateTimeParseException;

public class JsonTypeAdapter {
    /**
     * Used by Gson when date is formatted like "1967-07-23"
     */
    public static class LocalDateDeserializer implements JsonDeserializer<LocalDate> {
        @Override
        public LocalDate deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
            try {
                return LocalDate.parse(json.getAsString());
            } catch (DateTimeParseException e) {
                return null;
            }
        }
    }

    /**
     * Used by Gson when date is formatted like "1967-07-23T09:18:33"
     */
    public static class LocalDateTimeDeserializer implements JsonDeserializer<LocalDateTime> {
        @Override
        public LocalDateTime deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
            try {
                return LocalDateTime.parse(json.getAsString());
            } catch (DateTimeParseException e) {
                return null;
            }
        }
    }

    /**
     * Used by Gson when date is formatted like "1967-07-23T09:18:33.666Z"
     */
    public static class InstantDeserializer implements JsonDeserializer<Instant> {
        @Override
        public Instant deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
            try {
                return Instant.parse(json.getAsString());
            } catch (DateTimeParseException e) {
                return null;
            }
        }
    }
}

custom TypeAdapter

  • In the JsonMovieDAO class's search method, edit the Gson object by chaining the .registerTypeAdapter() method.

  • Because the JSON release_date is something like "1967-07-23", we will pass LocalDate.class as the first argument and a type Adapter class as the second argument.

/**
 * Retrieves all movies from the data source that matches the title
 * @param title The movie title a user is searching for
 * @return A List<Movie> movies that matches the search title
 */
@Override
public List<Movie> search(String title) {
    String rawData = getRawData(title);
//        System.out.println(prettyFormatter(rawData));
    Gson gson = new GsonBuilder()
            .registerTypeAdapter(LocalDate.class, new JsonTypeAdapter.LocalDateDeserializer())
            .create();
    TmdbMovieResponse movieResponse = null;
    try {
        movieResponse = gson.fromJson(rawData, TmdbMovieResponse.class);
    } catch (JsonSyntaxException | JsonIOException e) {
        // Will display an error if the json is not a valid
        // representation for a TmdbMovieResponse object
        throw new RuntimeException(e);
    }
    movieResponse.getResults().forEach(result -> {
        System.out.println(result);
    });
    return List.of();
}

MovieSearchResult to Movie

  • In the search method, instantiate a List<Movie> object. 

  • Inside the forEach loop, instantiate Movie objects and add them to the list.

  • Since the MovieSearchResult's release_date might be null, we have to write an if statement so the getYear() method is only called when the LocalDate object exists.

/**
 * Retrieves all movies from the data source that matches the title
 * @param title The movie title a user is searching for
 * @return A List<Movie> movies that matches the search title
 */
@Override
public List<Movie> search(String title) {
    List<Movie> movies = new ArrayList<>();
    String rawData = getRawData(title);
    // System.out.println(prettyFormatter(rawData));
    Gson gson = new GsonBuilder()
            .registerTypeAdapter(LocalDate.class, new JsonTypeAdapter.LocalDateDeserializer())
            .create();
    TmdbMovieResponse movieResponse = null;
    try {
        movieResponse = gson.fromJson(rawData, TmdbMovieResponse.class);
    } catch (JsonSyntaxException | JsonIOException e) {
        // Will display an error if the json is not a valid
        // representation for a TmdbMovieResponse object
        throw new RuntimeException(e);
    }
    movieResponse.getResults().forEach(result -> {
        Movie movie = new Movie();
        movie.setId(result.getId());
        movie.setTitle(result.getTitle());
        if(result.getRelease_date() != null) {
            movie.setYear(result.getRelease_date().getYear());
        }
        movie.setPlot(result.getOverview());
        movies.add(movie);
    });
    return movies;
}

Handling Multiple Pages

  • The Movie DB API return's a "total_pages" and "total_results" properties as part of the response.

  • Add total_pages as an attribute to the TmdbMovieResponse class

  • Add a getter method.

package edu.kirkwood.model.json;

import java.util.List;

public class TmdbMovieResponse {
    private List<MovieSearchResult> results;
    private int total_pages;

    public List<MovieSearchResult> getResults() {
        return results;
    }

    public int getTotal_pages() {
        return total_pages;
    }
}

Handle Multiple Pages

  • Update the JsonMovieDAO's getRawData method to accept a page number as input.

  • Concatenate that value to the apiURL.

/**
 * Get raw data from themoviedb.org search API
 * @param title The movie title a user is searching for
 * @param page The page number to retrieve
 * @return A JSON string returned from the API
 */
public String getRawData(String title, int page) {
    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder()
            .url(apiURL + "query=" + title + "&page=" + page)
            .get()
            .addHeader("accept", "application/json")
            .addHeader("Authorization", "Bearer " + apiToken)
            .build();
    Response response = null;
    String responseBody = "";
    try {
        response = client.newCall(request).execute();
        responseBody = response.body().string();
    } catch(IOException e) {
        throw new RuntimeException(e);
    }
    return responseBody;
}

Update search

  • Update the JsonMovieDAO's search method to run a while loop, passing the current page number to the getRawData method.

  • Run the loop continually until all pages have been retrieved.

/**
 * Retrieves all movies from the data source that matches the title
 * @param title The movie title a user is searching for
 * @return A List<Movie> movies that matches the search title
 */
@Override
public List<Movie> search(String title) {
    List<Movie> movies = new ArrayList<>();
    int currentPage = 1;
    while(true) {
        String rawData = getRawData(title, currentPage);
        // System.out.println(prettyFormatter(rawData));
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(LocalDate.class, new JsonTypeAdapter.LocalDateDeserializer())
                .create();
        TmdbMovieResponse movieResponse = null;
        try {
            movieResponse = gson.fromJson(rawData, TmdbMovieResponse.class);
        } catch (JsonSyntaxException | JsonIOException e) {
            // Will display an error if the json is not a valid
            // representation for a TmdbMovieResponse object
            throw new RuntimeException(e);
        }
        movieResponse.getResults().forEach(result -> {
            Movie movie = new Movie();
            movie.setId(result.getId());
            movie.setTitle(result.getTitle());
            if (result.getRelease_date() != null) {
                movie.setYear(result.getRelease_date().getYear());
            }
            movie.setPlot(result.getOverview());
            movies.add(movie);
        });
        if(movieResponse.getTotal_pages() > currentPage) {
            currentPage++;
        } else {
            break;
        }
    }
    return movies;
}

Movie API

import com.google.gson.*;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;
import java.lang.reflect.Type;
import java.time.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .registerTypeAdapter(LocalDate.class, new LocalDateDeserializer())
                .create();
//        JsonElement jsonElement = new JsonParser().parse(responseBody);
//        String json = gson.toJson(jsonElement);
//        System.out.print(json);
        MovieDBAPI movieDBAPI = null;
        try {
            movieDBAPI = gson.fromJson(responseBody, MovieDBAPI.class);
        } catch (JsonSyntaxException | JsonIOException e) {
            System.out.println(e.getMessage());
            System.exit(0);
        }

        for(Movie movie : movieDBAPI.results) {
            System.out.println("ID: " + movie.id);
            System.out.println("Title: " + movie.title);
            System.out.println("Overview: " + movie.overview);
            System.out.println("Genres: " + Arrays.toString(movie.genre_ids));
            System.out.println("Vote Average: " + movie.vote_average);
            System.out.println("Video: " + (movie.video ? "Yes" : "No"));
            System.out.println("Release Date: " + movie.release_date);
            System.out.println();
        }
    }

    

    private static class MovieDBGenre {
        public Genre[] genres;
    }
    private static class Genre {
        public int id;
        public String name;
    }

  private static class MovieDBAPI {
  public Movie[] results;
  }
    private static class Movie {
        public int id;
        public String title;
        public String overview;
        public int[] genre_ids;
        public double vote_average;
        public boolean video;
        public LocalDate release_date;
    }
}

Genre Ids to Genres

  • Make a second API request to get Genre data.
  • Use this solution to convert the array of Genre objects into a List<Genre>.
  • Use this solution to retrieve a Genre by its id.
  • The filter() method requires a Predicate object
    • The object of type T is a Genre object.
    • -> indicates a lamba expression
    • The value after the -> must be a boolean expression.
import com.google.gson.*;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;
import java.lang.reflect.Type;
import java.time.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class JsonDemo3 {
    public static void main(String[] args) throws IOException {
        // Remove before pushing to GitHub
        String apiKey = "";

        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url("https://api.themoviedb.org/3/trending/movie/day?language=en-US")
                .get()
                .addHeader("accept", "application/json")
                .addHeader("Authorization", "Bearer " + apiKey)
                .build();

        Request request2 = new Request.Builder()
                .url("https://api.themoviedb.org/3/genre/movie/list?language=en")
                .get()
                .addHeader("accept", "application/json")
                .addHeader("Authorization", "Bearer " + apiKey)
                .build();

        Response response = client.newCall(request).execute();
        String responseBody = response.body().string();
//        System.out.println(responseBody);

        Response response2 = client.newCall(request2).execute();
        String responseBody2 = response2.body().string();
//        System.out.println(responseBody2);

        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .registerTypeAdapter(LocalDate.class, new LocalDateDeserializer())
                .create();
//        JsonElement jsonElement = new JsonParser().parse(responseBody);
//        String json = gson.toJson(jsonElement);
//        System.out.print(json);
        MovieDBAPI movieDBAPI = null;
        try {
            movieDBAPI = gson.fromJson(responseBody, MovieDBAPI.class);
        } catch (JsonSyntaxException | JsonIOException e) {
            System.out.println(e.getMessage());
            System.exit(0);
        }

        MovieDBGenre movieDBGenre = null;
        try {
            movieDBGenre = gson.fromJson(responseBody2, MovieDBGenre.class);
        } catch (JsonSyntaxException | JsonIOException e) {
            System.out.println(e.getMessage());
            System.exit(0);
        }
        Genre[] genres = movieDBGenre.genres;
        List<Genre> genresList = Arrays.asList(genres);

        for(Movie movie : movieDBAPI.results) {
            System.out.println("ID: " + movie.id);
            System.out.println("Title: " + movie.title);
            System.out.println("Overview: " + movie.overview);
//            System.out.println("Genres: " + Arrays.toString(movie.genre_ids));
            System.out.print("Genres: ");
            for(int id: movie.genre_ids){
                Genre genre = genresList.stream().filter(a -> a.id == id).collect(Collectors.toList()).get(0);
                System.out.print(genre.name + ", ");
            }
            System.out.println();
            System.out.println("Vote Average: " + movie.vote_average);
            System.out.println("Video: " + (movie.video ? "Yes" : "No"));
            System.out.println("Release Date: " + movie.release_date);
            System.out.println();
        }
    }

    private static class LocalDateDeserializer implements JsonDeserializer<LocalDate> {
        @Override
        public LocalDate deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
            return LocalDate.parse(json.getAsString());
        }
    }

    private static class MovieDBGenre {
        public Genre[] genres;
    }
    private static class Genre {
        public int id;
        public String name;
    }

    private static class MovieDBAPI {
        public Movie[] results;
    }
    private static class Movie {
        public int id;
        public String title;
        public String overview;
        public int[] genre_ids;
        public double vote_average;
        public boolean video;
        public LocalDate release_date;
    }
}

Get More Data

  • In this example, I add the Person object's location and street address.
import com.google.gson.Gson;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;


public class JsonDemo2 {
    private static final String API_URL = "https://randomuser.me/api/?format=json&seed=abc&results=1&nat=us";

    public static void main(String[] args) throws Exception {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url(API_URL)
                .build();

        Response response = client.newCall(request).execute();
        String responseBody = response.body().string();
//        System.out.println(responseBody);
        Gson gson = new Gson();
        APIResponse apiResponse = gson.fromJson(responseBody, APIResponse.class);
        for (Person person : apiResponse.results) {
            System.out.println(person.name.first + " " + person.name.last);
            System.out.printf("%d %s\n", person.location.street.number, person.location.street.name);
            System.out.printf("%s, %s %s\n", person.location.city, person.location.state, person.location.postcode);
            System.out.printf("Latitude: %.4f, Longitude: %.4f\n", person.location.coordinates.latitude, person.location.coordinates.longitude);
            System.out.println(person.email);
        }
    }

    private static class APIResponse {
        private Person[] results;
    }

    private static class Person {
        private Name name;
        private Location location;
        private String email;
    }

    private static class Name {
        private String first;
        private String last;
    }

    private static class Location {
        private Street street;
        private String city;
        private String state;
        private int postcode;
        private Coordinates coordinates;
    }

    private static class Street {
        private int number;
        private String name;
    }

    private static class Coordinates {
        private double latitude;
        private double longitude;
    }
}

Handling Exceptions

  • In this example, I try to assign a String to an int.
  • If you have an attribute that doesn't exist, it will be assigned its default value (Object is null, int is 0, etc.)
package edu.kirkwood;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;


public class JsonDemo2 {
    private static final String API_URL = "https://randomuser.me/api/?format=json&seed=abc&results=1&nat=us";

    public static void main(String[] args) throws Exception {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url(API_URL)
                .build();

        Response response = client.newCall(request).execute();
        String responseBody = response.body().string();
//        System.out.println(responseBody);
        Gson gson = new Gson();
        APIResponse apiResponse = null;
        try {
            apiResponse = gson.fromJson(responseBody, APIResponse.class);
        } catch(JsonSyntaxException e) {
            System.out.println("ERROR: " + e.getMessage());
            System.exit(0);
        }
        for (Person person : apiResponse.results) {
            System.out.println(person.name.first + " " + person.name.last);
            System.out.printf("%d %s\n", person.location.street.number, person.location.street.name);
            System.out.printf("%s, %s %s\n", person.location.city, person.location.state, person.location.postcode);
            System.out.printf("Latitude: %.4f, Longitude: %.4f\n", person.location.coordinates.latitude, person.location.coordinates.longitude);
            System.out.println(person.email);
        }
    }

    private static class APIResponse {
        private Person[] results;
    }

    private static class Person {
        private Name name;
        private Location location;
        private String email;
    }

    private static class Name {
        private String first;
        private String last;
    }

    private static class Location {
        private Street street;
        private String city;
        private String state;
        private int postcode;
        private Coordinates coordinates;
    }

    private static class Street {
        private int number;
        private int name;
    }

    private static class Coordinates {
        private double latitude;
        private double longitude;
    }
}

Handling Dates

import com.google.gson.*;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.lang.reflect.Type;
import java.time.Instant;

public class JsonDemo2 {
    private static final String API_URL = "https://randomuser.me/api/?format=json&seed=abc&results=1&nat=us";

    public static void main(String[] args) throws Exception {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url(API_URL)
                .build();

        Response response = client.newCall(request).execute();
        String responseBody = response.body().string();
//        System.out.println(responseBody);
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(Instant.class, new InstantDeserializer())
                .create();
        APIResponse apiResponse = null;
        try {
            apiResponse = gson.fromJson(responseBody, APIResponse.class);
        } catch(JsonSyntaxException e) {
            System.out.println("ERROR: " + e.getMessage());
            System.exit(0);
        }
        for (Person person : apiResponse.results) {
            System.out.println(person.name.first + " " + person.name.last);
            System.out.printf("%d %s\n", person.location.street.number, person.location.street.name);
            System.out.printf("%s, %s %s\n", person.location.city, person.location.state, person.location.postcode);
            System.out.printf("Latitude: %.4f, Longitude: %.4f\n", person.location.coordinates.latitude, person.location.coordinates.longitude);
            System.out.println(person.email);
            System.out.printf("Born %s\n", person.dob.date);
        }
    }

    private static class InstantDeserializer implements JsonDeserializer<Instant> {
        @Override
        public Instant deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
            return Instant.parse(json.getAsString());
        }
    }

    private static class APIResponse {
        private Person[] results;
    }

    private static class Person {
        private Name name;
        private Location location;
        private String email;
        private DateOfBirth dob;
    }

    private static class Name {
        private String first;
        private String last;
    }

    private static class Location {
        private Street street;
        private String city;
        private String state;
        private int postcode;
        private Coordinates coordinates;
    }

    private static class Street {
        private int number;
        private String name;
    }

    private static class Coordinates {
        private double latitude;
        private double longitude;
    }

    private static class DateOfBirth {
        private Instant date;
    }
}

Comparing and Sorting

  • Implement the Comparable Interface in the RandomUser class.
  • Before printing users, sort them alphabetically.
import com.google.gson.*;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.lang.reflect.Type;
import java.time.Instant;
import java.util.Arrays;

public class JsonDemo2 {
    private static final String API_URL = "https://randomuser.me/api/?format=json&seed=abc&results=5&nat=us";

    public static void main(String[] args) throws Exception {
        OkHttpClient client = new OkHttpClient();

        Request request = new Request.Builder()
                .url(API_URL)
                .build();

        Response response = client.newCall(request).execute();
        String responseBody = response.body().string();
//        System.out.println(responseBody);
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(Instant.class, new InstantDeserializer())
                .create();
        APIResponse apiResponse = null;
        try {
            apiResponse = gson.fromJson(responseBody, APIResponse.class);
        } catch(JsonSyntaxException e) {
            System.out.println("ERROR: " + e.getMessage());
            System.exit(0);
        }
        Person[] people = apiResponse.results;
        Arrays.sort(people);
        for(Person person: people) {
            System.out.println(person.name.first + " " + person.name.last);
            System.out.printf("%d %s\n", person.location.street.number, person.location.street.name);
            System.out.printf("%s, %s %s\n", person.location.city, person.location.state, person.location.postcode);
            System.out.printf("Latitude: %.4f, Longitude: %.4f\n", person.location.coordinates.latitude, person.location.coordinates.longitude);
            System.out.println(person.email);
            System.out.printf("Born %s\n", person.dob.date);
            System.out.println();
        }
    }

    private static class InstantDeserializer implements JsonDeserializer<Instant> {
        @Override
        public Instant deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
            return Instant.parse(json.getAsString());
        }
    }

    private static class APIResponse {
        private Person[] results;
    }

    private static class Person implements Comparable<Person> {
        private Name name;
        private Location location;
        private String email;
        private DateOfBirth dob;
        
        @Override
        public int compareTo(Person o) {
            int result = this.name.last.compareTo(o.name.last);
            if (result == 0) {
                result = this.name.first.compareTo(o.name.first);
            }
            return result;
        }
    }

    private static class Name {
        private String first;
        private String last;
    }

    private static class Location {
        private Street street;
        private String city;
        private String state;
        private int postcode;
        private Coordinates coordinates;
    }

    private static class Street {
        private int number;
        private String name;
    }

    private static class Coordinates {
        private double latitude;
        private double longitude;
    }

    private static class DateOfBirth {
        private Instant date;
    }
}

Java 2 - Week 8

By Marc Hauschildt

Java 2 - Week 8

  • 391