Last Updated: April 26, 2023

This is part 3 of the Bleeding edge Java series. Start at the introduction if you haven’t already.

Printing is the process of converting a stream of JSON tokens into JSON text. For our library, our printer only needs to print a single JSON token at a time. Our serializer already produces a stream of JSON tokens and the JDK has a large set of existing methods for managing streams.

What we need is a mapping function that can be passed to the JDK Stream’s
map() method. We can define a Java interface for this:

public interface JsonPrinter
    CharSequence print(JsonToken jsonToken);

With an instance of this interface we can serialize from a Java object to a
stream of JSON tokens to JSON text like this:

String jsonText = serializer.serialize(object)

The implementation for our printer is very straightforward. Note the code uses a StringUtils utility from the link at the end of this article:

CharSequence print(JsonToken jsonToken)
    return switch (jsonToken) {
        case NumberToken(var number) -> number.toString();
        case StringToken(var string) -> StringUtils.quoteAndEscape(string);
        case BooleanToken(var value) -> value ? "true" : "false";
        case NullToken __ -> "null";
        case BeginArrayToken __ -> "[";
        case EndArrayToken __ -> "]";
        case BeginObjectToken __ -> "{";
        case EndObjectToken __ -> "}";
        case ObjectNameToken(var name) -> StringUtils.quoteAndEscape(name) + ":";
        case ValueSeparatorToken __ -> ",";

Here again we take advantage of enhanced switch and pattern matching (see Serialization for more details). Additionally, we use the deconstruction feature of Record Patterns. With the first case statement, case NumberToken(var number), the compiler matches if the token is a number token and then extracts the number token’s value, binding it to the new variable number. It can be thought of as this Java code:

if (jsonToken instanceOf NumberToken) {
    var number = ((NumberToken) jsonToken).value();

Finally, notice that the switch statement does not need a default case. We have case statements for all possible implementations in the JsonToken sealed hierarchy. The Java compiler knows this and doesn’t require a default. If in the future we add a new JsonToken type this code would no longer compile.

Test it out for yourself!

In the prior article we developed a serializer that can serialize a Java record into a stream of JSON tokens. We now have a printer that can map a JSON token into JSON text. Let’s put this together in jshell. The example use these files:

From a terminal with Java 19 installed, run the following (note you’ll need the wget utility):

wget -nc https://raw.githubusercontent.com/starburstdata/developer-blog-assets/main/bleeding-edge-java/code/TypeToken.java
wget -nc https://raw.githubusercontent.com/starburstdata/developer-blog-assets/main/bleeding-edge-java/code/JsonToken.java
wget -nc https://raw.githubusercontent.com/starburstdata/developer-blog-assets/main/bleeding-edge-java/code/JsonSerializer.java
wget -nc https://raw.githubusercontent.com/starburstdata/developer-blog-assets/main/bleeding-edge-java/code/StringUtils.java
wget -nc https://raw.githubusercontent.com/starburstdata/developer-blog-assets/main/bleeding-edge-java/code/JsonPrinter.java
jshell --enable-preview TypeToken.java JsonToken.java JsonSerializer.java StringUtils.java JsonPrinter.java

Inside jshell let’s serialize a Java record into JSON text:

var serializer = JsonSerializer.instance();
var printer = JsonPrinter.instance();

record Person(String name, int age) {}
var person = new Person("someone", 28);

String jsonText = serializer.serialize(person)  // serialize record to stream of tokens
    .map(printer::print)                        // map each JsonToken to a String (as a CharSequence)
    .collect(Collectors.joining());             // collect into a String


We can now serialize a Java record into JSON text. Let’s look at parsing into tokens next in parsing.

We’re hiring

Want to be able to use the latest features of Java? We’re hiring!

Jordan Zimmerman is a Senior Software Engineer working on Starburst Galaxy.

Start Free with
Starburst Galaxy

Up to $500 in usage credits included

  • Query your data lake fast with Starburst's best-in-class MPP SQL query engine
  • Get up and running in less than 5 minutes
  • Easily deploy clusters in AWS, Azure and Google Cloud
For more deployment options:
Download Starburst Enterprise

Please fill in all required fields and ensure you are using a valid email address.