15.4. Parsing JSON in Java

15.4.1. JSON to Java

In this section, the Gson library (i.e., Google’s JSON library) will be used to parse a JSON string into one or more Java objects. To get us started, consider the following data formatted using JSON:

{
    "name": "Jay",
    "age": 19,
    "classes": [
        "CSCI 1302",
        "CSCI 1730"
    ]
}

Now, assume that this JSON data is retrieved by a Java program (e.g., from the body of an HTTP response to a successful HTTP request) and that the program now has access to that JSON data via a String variable named jsonString. With that in mind, our goal is to create a Java object containing the same data as the object described by jsonString without having to manually parse through it using String methods like indexOf, substring, etc. Once we have the data in the form of a Java object, we will be able to interact using Java code, just like we do with any other kind of object in Java.

To accomplish our goal, we first need to model the JSON data using one or more Java classes that declare the same set of variables. We can use existing classes that come with Java when specifying the variable types, if appropriate (e.g., the name and age variables in the JSON data store a string and integer, respectively, so String and int can be used as their data types in Java). This won’t work for the overall object depicted in the JSON data as none of the classes that come with Java are designed to represent that specific kind of object. That is perfectly okay. In this situation, we just manually create a new Java class ourselves to represent the object. Since the overall object in the JSON data looks like it describes an a student, let’s name this new Java class Student:

public class Student {
    String name;
    int age;
    String[] classes;
} // Student

Now that we have a Java class named Student that can be used to describe Student objects, we have all the Java classes and data types that are needed to represent the JSON data using Java objects. Instead of constructing these objects manually, we instead call Gson’s fromJson(String, Class) method to parse the data from a String formatted using JSON directly into a new Student object containing the desired instance information:

// parse JSON-formatted string into a Student object
Student jay = GSON.<Student>fromJson(jsonString, Student.class);

// inspect the result
System.out.println(jay.name);
System.out.println(jay.age);
System.out.println("Classes:");
for (int i = 0; i < jay.classes.length; i++) {
    String className = jay.classes[i];
    System.out.println(" - " + className);
} // for

Here is the expected output:

Jay
19
Classes:
 - CSCI 1302
 - CSCI 1730
 - CSCI 2610

Working Example

A working copy of the example presented above is included in the starter code for this chapter.

15.4.2. Array Objects

When the outermost portion of some JSON data is an array, you need only model a class for the array’s element type and not the array itself. For example, consider the following JSON:

[
  {
    "name": "Sue",
    "age": 22
  },
  {
    "name": "Bob",
    "age": 21
  }
]

Each element of this array can be modelled using a Person class with name and age variables:

public class Person {
    String name;
    int age;
} // Person

When parsing this JSON using Gson’s toJson(Object), you will need to use Person[].class instead of Person.class. For example, assuming json refers to a String with the same JSON depicted above:

Person[] people = GSON.<Person[]>fromJson(json, Person[].class);

15.4.3. Map<K, V> Objects

Consider the JSON examples below, each taken from the body of HTTP responses for two slightly different HTTP requests:

{
  "releases": {
    "na": "...",
    "eu": "..."
  }
}
{
  "releases": {
    "jp": "...",
    "eu": "..."
  }
}

Each example depicts an object with a releases variable that itself refers to an object with a different set of variable names. Since the releases variable refers to an object with variable names that vary, a Map<K, V> is needed.

A Map<K, V> is a mapping from “keys” (i.e., variable names) to “values” (i.e., the values of the keys / variables). To model this in a class that can be used by Gson, declare releases as a Map<String, String> variable as follows:

public class ExampleResponse {
    Map<String, String> releases;
} // ExampleResponse

In the example above, K repreents the “key” type and is replaced with String. The keys in this mapping are the variable names that vary (e.g., "na", "eu", "jp", etc.). Likewise, V represent the “value” type and is also replaced with String. If the values in some JSON that you want to model a different type, then replace V with the name of a class for that type.

Now consider two more JSON examples and a Java class named Course that can be used to represent a description of a course:

{
  "CSCI 1301": {
    "name": "Introduction to Computing and Programming"
  },
  "CSCI 1302": {
    "name": "Software Development"
  }
}
{
  "CSCI 1302": {
    "name": "Software Development"
  },
  "CSCI 2720": {
    "name": "Data Structures"
  }
}
Listing 15.7 Model Class for Describing a Course
public class Course {
    String name;
} // Course

The outermost objects depicted in the two JSON examples above can be modelled using a Map<String, Course>. Whenever the outermost object is a Map<K, V>, use Gson’s TypeToken class and fromJson(String, TypeToken) method (i.e., instead of the fromJson(String, Class) overload) so that Gson is able to correctly parameterize K and V. For example:

Listing 15.8 Parsing JSON to a Map<K, V> with fromJson and a TypeToken
TypeToken<Map<String, Course>> courseMapType  = new TypeToken<Map<String, Course>() {};
Map<String, Course> map = GSON.fromJson(json, courseMmapType);

if (map.containsKey("CSCI 1302")) {
    Course course = map.get("CSCI 1302");
    String name = course.name;
    System.out.println(name);
} // if

15.4.4. Adjusting Variable Names

Variable names in JSON are not always camel case; however, the variables in your model classes should be in order to adhere to Java conventions. Consider the following JSON for a music track:

Listing 15.9 Example JSON for a Music Track
{
  "artist": "Daft Punk & Nile Rogers",
  "album": "Random Access Memories",
  "track_name": "Give Life Back to Music"
}

If you use the variable names depicted in the JSON directly when modelling the data with a Java class, then you might write something like this, which violates code style conventions in Java since instance variable names are expected to be written in camel case:

Listing 15.10 Model Class for Music Track Data with Invalid Style
public class MusicTrack {
    String artist;
    String album;
    String track_name;
} // MusicTrack

Gson provides a @SerializedName annotation that can be used to explicitly state what JSON variable a Java variable corresponds to. In this case, @SerializedName enables us to follow Java code style conventions and use trackName instead of track_name in our class:

Listing 15.11 Model Class for Music Track Data with Valid Style
public class MusicTrack {
    String artist;
    String album;
    @SerializedName("track_name")
    String trackName;
} // MusicTrack

When parsing JSON using fromJson, Gson will assign the value of track_name to the trackName variable in the resulting Java object. Likewise, when serializing to JSON using toJson, Gson will assign the value of trackName to the track_name variable in the resulting JSON string.