Java Multi-line and String Templates in Action

In this article we will learn how to use the Java Multi-line feature and and we can combine it with String Templates to create complex Object structures such as JSON payloads or even JSON Objects!

What is Java Multiline?

The Java multiline feature, introduced in JDK 13, aims to enhance the readability and usability of long strings or blocks of text within Java code. Before this feature, developers had to concatenate multiple strings or use escape characters (such as \n for a newline) to represent multiline text. However, this method often led to code that was hard to read and maintain.

With the multiline feature, developers can now define multiline strings directly within the code without using concatenation or escape characters. This is achieved using the text blocks syntax.

Java Multiline Syntax:

A text block starts and ends with three double quotes """, followed by a newline. The content within the text block is considered as-is, including spaces, tabs, and newlines, until the closing three double quotes.

String textBlock = """
    This is a multiline
    text block in Java.
    It allows easy representation
    of long text without the need
    for concatenation or escape characters.
    """;

Key Benefits:

  1. Readability: Text blocks improve code readability by maintaining the original formatting of the text.
  2. Simplification: They simplify the representation of multiline strings without the need for escape characters or manual concatenation.
  3. Enhanced Usability: Text blocks are especially useful for embedding SQL queries, JSON, HTML, or other structured text directly within the code.

Adding String Templates to the mix

String templates in Java complement the language’s standard string literals and text blocks. They blend literal text with embedded expressions and template processors to generate customized outcomes. Embedded expressions resemble regular Java expressions but feature distinct syntax to distinguish them from the surrounding literal text within the string template. A template processor merges the template’s literal text with the values from embedded expressions, ultimately creating a finalized output.

The following example declares a template expression that uses the template processor STR and contains a set of expressions, in it:

Person myperson = new Person(12345678, "John", "Smith", "[email protected]", "+1234567890");
String jsonString = STR."""
        {
            "customerId": "\{myperson.customerId()}",
            "firstName": "\{myperson.firstName()}",
            "lastName": "\{myperson.lastName()}",
            "email": "\{myperson.email()}",
            "phone": "\{myperson.phone()}"
        }
        """;

This is the Person class which you can use for this example:

public record Person(int customerId, String firstName, String lastName, String email, String phone) {
}

Adding a TemplateProcessor

In our example, we are producing the JSON String Payload using the basic STR template. What about producing directly the jakarta.json.JsonObject ?

To do that, you need to implement the StringTemplate.Processor interface and create your own template processor. The TemplateProcessor will return objects of any type, not just String, and throw check exceptions if processing fails.

var JSON = StringTemplate.Processor.of(
            (StringTemplate stJSON) -> {
                try (JsonReader jsonReader = Json.createReader(new StringReader(
                    stJSON.interpolate()))) {
                    return jsonReader.readObject();
                }    
            }
);

This StringTemplate.Processor is specifically designed to process JSON templates. It uses the provided StringTemplate object, resolves any embedded expressions within the template using interpolate(), and then parses the resulting string content as JSON to produce a JsonObject.

This is a full example which covers all the points discussed in this article. That is,

  • Firstly, we build a JSON String using a Template, replacing the value with actual values from a Java Record.
  • Then, we build a JsonObject using a Template Processor. We will be using the same Java Record for that:
//usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS jakarta.json.bind:jakarta.json.bind-api:3.0.0
//DEPS org.eclipse:yasson:3.0.3

//DEPS jakarta.json:jakarta.json-api:2.1.2
//DEPS org.glassfish:jakarta.json:2.0.1

//SOURCES Person.java
//PREVIEW
//JAVA 21+

import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
import jakarta.json.bind.JsonbException;
import jakarta.json.JsonReader;
import jakarta.json.Json;
import java.io.StringReader;
import jakarta.json.JsonObject;
import java.io.StringWriter;
import java.util.Collections;
import jakarta.json.stream.JsonGenerator;
import jakarta.json.JsonWriter;

public class TemplateProcessor {

    static String customerId;

    public static void main(String[] args) throws Exception {

        // Create a StringTemplate processor that can parse JSON strings
        var JSON = StringTemplate.Processor.of(
            // Callback function that processes JSON strings
            (StringTemplate stJSON) -> {
                try (JsonReader jsonReader = Json.createReader(new StringReader(
                    stJSON.interpolate()))) {
                    // Read the JSON object from the StringReader
                    return jsonReader.readObject();
                }
            }
        );
        Person person = new Person(12345678, "John", "Smith", "[email protected]", "+1234567890");
        String strPerson = STR."""
                {
                    "customerId": "\{person.customerId()}",
                    "firstName": "\{person.firstName()}",
                    "lastName": "\{person.lastName()}",
                    "email": "\{person.email()}",
                    "phone": "\{person.phone()}"
                }
                """;
        System.out.println(String.format("JSON String built with Template: %s", strPerson));      
                JsonObject jsonObject = JSON."""
                    {
                        "customerId": "\{person.customerId()}",
                        "firstName": "\{person.firstName()}",
                        "lastName": "\{person.lastName()}",
                        "email": "\{person.email()}",
                        "phone": "\{person.phone()}"
                    }
                    """;
        // Deserialize JSON to Person record
        try {
            StringWriter stringWriter = new StringWriter();

            // Create a JsonWriter with pretty printing (indentation) configuration
            JsonWriter jsonWriter = Json.createWriterFactory(
                    Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, true))
                    .createWriter(stringWriter);
    
            // Write the JSON object with indentation to the StringWriter
            jsonWriter.writeObject(jsonObject);
    
            // Close the JsonWriter to release resources
            jsonWriter.close();
    
            // Get the formatted JSON string from the StringWriter
            String formattedJson = stringWriter.toString();
    
            // Print the formatted JSON
            System.out.println("Formatted JSON Object:");
            System.out.println(formattedJson);
        } catch (JsonbException e) {
            e.printStackTrace();
        }
    }
}

Here is the output when you execute the JBang Script:

By the way, are you new to JBang ? Then check out this article: JBang: Create Java scripts like a pro

The JBang script is available here: https://github.com/fmarchioni/mastertheboss/blob/master/jbang/TemplateProcessor.java

Conclusion

In this blog post, we explored the use of StringTemplate to manipulate JSON strings. We demonstrated how to create JSON strings from Person objects, deserialize JSON strings into JsonObjects, and format JSON strings for better readability. The StringTemplate library provides a powerful and flexible approach for working with JSON data, and we hope this code serves as an example of its capabilities.

Found the article helpful? if so please follow us on Socials