Skip to content
On this page

Using JSON

Earlier, we discussed the XML data format. XML is feature-rich but comes with cumbersome tags and complex formatting. Its usage on the web is declining, being increasingly replaced by the JSON data structure.

JSON stands for JavaScript Object Notation. It removes all executable JavaScript code, retaining only the object format of JavaScript. A typical JSON example is as follows:

json
{
    "id": 1,
    "name": "Core Java",
    "author": {
        "firstName": "Abc",
        "lastName": "Xyz"
    },
    "isbn": "1234567",
    "tags": ["Java", "Network"]
}

As a data transmission format, JSON has several significant advantages:

  • JSON only allows UTF-8 encoding, avoiding encoding issues;
  • JSON only permits the use of double quotes for keys, with special characters escaped using \, resulting in a simple format;
  • Browsers have built-in JSON support. If data is sent to the browser in JSON format, it can be directly processed with JavaScript.

Therefore, JSON is suitable for representing hierarchical structures because of its simple format, supporting only the following data types:

  • Key-Value Pairs: {"key": value}
  • Arrays: [1, 2, 3]
  • Strings: "abc"
  • Numbers (integers and floating-point): 12.34
  • Boolean Values: true or false
  • Null Values: null

Browsers directly support reading and writing JSON using JavaScript:

javascript
// JSON string to JavaScript object:
jsObj = JSON.parse(jsonStr);

// JavaScript object to JSON string:
jsonStr = JSON.stringify(jsObj);

Thus, when developing web applications, using JSON for data transmission is very convenient on the browser side. Since JSON is inherently suitable for JavaScript processing, the vast majority of REST APIs choose JSON as their data transmission format.

Now the question arises: How can Java read and write JSON?

In Java, there is also a standard JSR 353 API for JSON. However, as we saw earlier with XML, it is best if we can directly convert between XML and JavaBeans. Similarly, if we can directly convert between JSON and JavaBeans, it becomes much simpler to use.

Common third-party libraries for parsing JSON include:

  • Jackson
  • GSON
  • JSON-lib
  • ...

Notice that Jackson, the library we mentioned earlier for parsing XML, can also parse JSON! Therefore, we only need to include the following Maven dependency:

xml
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.0</version>
</dependency>

Then, define the JavaBean and use the following code to parse a JSON file:

java
InputStream input = Main.class.getResourceAsStream("/book.json");
ObjectMapper mapper = new ObjectMapper();
// Ignore unknown JavaBean properties during deserialization:
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Book book = mapper.readValue(input, Book.class);

The core code involves creating an ObjectMapper object. Disabling the DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES feature ensures that parsing does not throw an error if the JavaBean lacks certain properties present in the JSON.

Converting JSON to a JavaBean is called deserialization. Conversely, converting a JavaBean to JSON is called serialization. To achieve serialization from a JavaBean to JSON, only one line of code is needed:

java
String json = mapper.writeValueAsString(book);

To parse certain JSON values into specific Java objects, such as LocalDate, it is entirely possible. For example:

json
{
    "name": "Core Java",
    "pubDate": "2016-09-01"
}

To parse into:

java
public class Book {
    public String name;
    public LocalDate pubDate;
}

Simply include the standard JSR 310 data type module in Maven:

xml
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.12.0</version>
</dependency>

Then, when creating the ObjectMapper, register a new JavaTimeModule:

java
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());

Sometimes, the built-in parsing rules and extended parsing rules do not meet our requirements. In such cases, custom parsing can be implemented.

For example, suppose the isbn field in the Book class is a BigInteger:

java
public class Book {
    public String name;
    public BigInteger isbn;
}

However, the JSON data does not follow the standard integer format:

json
{
    "name": "Core Java",
    "isbn": "978-7-111-54742-6"
}

Direct parsing would certainly result in an error. At this point, we need to create a custom IsbnDeserializer to parse strings containing non-numeric characters:

java
public class IsbnDeserializer extends JsonDeserializer<BigInteger> {
    @Override
    public BigInteger deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        // Read the original JSON string content:
        String s = p.getValueAsString();
        if (s != null) {
            try {
                return new BigInteger(s.replace("-", ""));
            } catch (NumberFormatException e) {
                throw new JsonParseException(p, s, e);
            }
        }
        return null;
    }
}

Then, use annotations in the Book class to specify the custom deserializer:

java
public class Book {
    public String name;
    
    // Use the custom IsbnDeserializer when deserializing the isbn field:
    @JsonDeserialize(using = IsbnDeserializer.class)
    public BigInteger isbn;
}

Similarly, to customize serialization, you would create a custom IsbnSerializer and annotate the Book class with @JsonSerialize(using = ...).

Deserialization

During deserialization, Jackson requires that Java classes have a default no-argument constructor. Otherwise, it cannot instantiate the class directly. For classes with parameterized constructors, ensure that a no-argument constructor is also provided to facilitate deserialization.

For enum fields, Jackson treats them as String types. For example:

java
public class Book {
    public DayOfWeek start = DayOfWeek.MONDAY;
}

Serializes to:

json
{
    "start": "MONDAY"
}

For record types, Jackson automatically detects the parameterized constructor and matches it with the JSON keys, allowing direct deserialization. Support for record types requires Jackson version 2.12.0 or higher.

Exercise

Use Jackson to parse JSON.

Summary

  • JSON is a lightweight data representation format commonly used in web applications;
  • Jackson facilitates the conversion between JavaBeans and JSON;
  • Modules can be used to extend the data types that Jackson can handle;
  • Custom JsonSerializer and JsonDeserializer can be implemented to tailor the serialization and deserialization processes.
Using JSON has loaded