JSON processing with Java
In this article I'll introduce you to JSON parsing solutions in Java. There are not as many built-in options to choose from like with XML and that particular solution is cumbersome too so I will take a look at GSON and Jackson besides the Java API for JSON Processing.
I won't write about what JSON is, I assume you know this much and just want to know how to deal with this kind of data in your Java project.
The example file
For this article I'll stick with a simple example, a list of books with some properties:
{
"books": [
{
"id": "_001",
"title": "Beginning XML, 4th Edition",
"author": "David Hunter",
"copyright": 2007,
"publisher": "Wrox",
"isbn": "0470114878"
},
{
"id": "_002",
"title": "XML in a Nutshell, Third Edition",
"author": "O’Reilly Media, Inc",
"copyright": 2004,
"publisher": "O’Reilly Media, Inc",
"isbn": "0596007647"
},
{
"id": "_003",
"title": "Learning XML, Second Edition",
"author": "Erik Ray",
"copyright": 2003,
"publisher": "O’Reilly Media, Inc.",
"isbn": "0596004206"
},
{
"id": "_004",
"title": "XML processing and website scraping in Java",
"author": "Gabor Laszlo Hajba",
"copyright": 2016,
"publisher": "LeanPub"
}
]
}
These values will be read into instances of the following class:
/**
* This is the sample class we will fill with data from the books.json file for the examples.
*
* @author GHajba
*
*/
public class Book {
private String id;
private String title;
private String author;
private int copyright;
private String publisher;
private String isbn;
// getters and setters omitted
@Override
public String toString() {
final StringBuilder stringRepresentation = new StringBuilder();
final String newLine = System.getProperty("line.separator");
stringRepresentation.append(this.getClass().getName() + " {" + newLine);
stringRepresentation.append(" ID: " + this.id + newLine);
stringRepresentation.append(" Title: " + this.title + newLine);
stringRepresentation.append(" Copyright: " + this.copyright + newLine);
stringRepresentation.append(" Publisher: " + this.publisher + newLine);
stringRepresentation.append(" ISBN: " + this.isbn + newLine);
stringRepresentation.append("}");
return stringRepresentation.toString();
}
}
And naturally there is a Publictaions wrapper class which contains all the Books:
/**
* Sample parent class to extract Book objects from JSON.
*
* @author GHajba
*
*/
public class Publications {
List<Book> books;
// getters and setters omitted
@Override
public String toString() {
return "Publications: [\n"
+ this.books.stream().map(Objects::toString).collect(Collectors.joining("\n"))
+ "\n]";
}
}
Note that this class is only needed for the Gson and Jackson examples. For the Java API the Book class fits our needs.
Java API for JSON Processing
Let's start right away with the built-in solution. Actually it is not as built-in as for XML parsing—here you need either a default implementation residing in the Glassfish project (so you need an extra library) or you have to write your own solution.
None of them is the best but for the sake of simplicity let's stick with the usage of the default implementation. I won't go into detail how to add a library to your project—and you will need to add the other two libraries later on too.
Let' see how it works with the built-in JSON parsing of Java. The core concept is to read some input (it can be a file, an input stream or an already available String object) which is presented in JSON format and to convert the contents into Java objects.
To achieve this with the standard API we need the following:
- a json.JsonReader obtained from javax.json.Json.createReader() (name it reader)
- the whole json.JsonObject obtained from reader.readObject() (name it jsonObject)
Now we have all your contents in a nice JsonObject structure, it is time to extract them to usable objects like Publications and Books. For this we have to write our own parsing system where we are aware of the content structure of the data we got.
In the example case we know we have an array of elements and this array is called "books". So we instruct the API to get fetch this array:
JsonArray jsonArray = jsonObject.getJsonArray("books");
Now we have to iterate over this array and convert its contents to Book objects:
for(JsonObject bookObject : jsonArray.getValuesAs(JsonObject.class)) {
Book b = new Book();
b.setId(bookObject.getString("id"));
// other setters omitted
System.out.println(b);
}
As you can see in the example above, we have to get the elements of the array as JsonObjects so we have to use the specific method JsonArray.getValuesAs(Class<T>). In the body of the loop you can see that we create a new instance of the Book class and set every parameter through a setter method. An alternative would be to have a constructor which takes all the parameters extracted, but it won't be readable so I stick with the setter-version.
What about isbn?
As you may have noticed the property isbn is not present in the extraction loop. This is because it is not present in the fourth book and this leads to a little problem with the standard API.
If you have a JsonObject which does not contain a given property you try to access you will get a NullPointerException. This is bad but we can easily fix this problem:
if(bookObject.containsKey("isbn")) {
b.setIsbn(bookObject.getString("isbn"));
}
Well, this solution is quite good for now but what about other cases when more fields are optional in the JSON content? In that case you have to verify the existence of each (optional) property or you can write some generic methods which will do the null-check prior to requesting the property.
Gson
Gson is the JSON parser for Java developed by Google. The main goal of Gson is to provide simple ways to convert objects to JSON and vice versa.
Because of this it is really simple to parse JSON input in Java. To achieve this you only need the following:
- a new com.google.gson.GsonBuilder.GsonBuilder() (name it builder)
- a google.gson.Gson object obtained with builder.create() (name it gson)
To read the contents of a file into an object just create a java.io.Reader (naturally you can utilize a java.lang.String too and do not need a file in every case) and call com.google.gson.Gson.fromJson(Reader, Class<Publications>).
In my example case the whole process would look like this:
/**
* This is an example class to load JSON data with Gson.
*
* @author GHajba
*
*/
public class GsonExample {
public static void main(String... args) {
final GsonBuilder builder = new GsonBuilder();
final Gson gson = builder.create();
final Publications publications = gson.fromJson(new FileReader("books.json"), Publications.class);
System.out.println(publications);
}
}
The Publications class is the wrapper object which holds the java.util.List of Book objects which are read from the JSON file. Naturally there is some error handling which you need to take care of but I omit it because it won't make the code clear.
The solution is simple and clear. However, sometimes you need custom mappings to tell Gson how one object should behave when written to a file or established from JSON data but this is out of the scope of the current article.
Jackson
Just like Gson, Jackson aims to be the easiest and most usable JSON library for Java developers.
To map JSON data into your class structure you will need the following:
- a new com.fasterxml.jackson.databind.ObjectMapper.ObjectMapper() (name it mapper)
And that's it. Now you can call the readValue method on mapper with the same parameters as previously (a Reader and the target class to map your data to).
In my example this would look like this (again, I omit error handling to keep the code readable)
/**
* Example class load JSON data with Jackson.
*
* @author GHajba
*
*/
public class JacksonExample {
public static void main(String... args) {
final ObjectMapper mapper = new ObjectMapper();
final Publications publications = mapper.readValue(new FileReader("books.json"), Publications.class);
System.out.println(publications);
}
}
As you can see, using Jackson is as simple as it is with Gson. One difference is that to have the mapping work you need at least getters for each property in your class which you want to fill in with Jackson. This means if you have a class where the id property only exists for internal verification / just to display it in the string representation of your Object you will need a public getter too. If you do not provide a getter you may encounter an exception like this one:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "id" (class hu.japy.dev.json.Book), not marked as ignorable (5 known properties: "title", "author", "publisher", "isbn", "copyright"])
Above there was no publicly available getter for the id attribute.
Another difference is that you have more options for the input source. You can use Strings, Files, Readers, and InputStreams. But this is out of the scope of this article and you can look into the various ways in the JavaDoc.
Conclusion
As you can see, Java provides a default solution for parsing JSON data into your project but it is rather bothersome and you need to implement a great deal to get things rolling. However there are some slick third-party libraries you can utilize which leverage the amount of required code and provide you the necessary objects. So I suggest that you use either Gson or Jackson for JSON parsing until Java does not provide a leaner way to handle this problem.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment