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"
}
}
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:
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:
{
"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:
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:
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.