adam bien's blog

Serializing and Deserializing Java Records with "stock" Jakarta EE 9 JSON-B 📎

To serialize a Java Record with "stock" JSON-B (Jakarta 9 JSON Binding), you will have to declare a dependency (or use an implementation which ships with your runtime) to a JSON-B implementation:

<dependency>
    <groupId>org.eclipse</groupId>
    <artifactId>yasson</artifactId>
    <version>2.0.2</version>
</dependency>    

A Java Record:


public record Workshop(String title, Date date, String description) {
}    

can be serialized by configuring JSON-B to use private fields with a custom PropertyVisibilityStrategy:


public class PrivateVisibilityStrategy implements PropertyVisibilityStrategy {
    @Override
    public boolean isVisible(Field field) {
        return true;
    }
    @Override
    public boolean isVisible(Method method) {
        return false;
    }
}     

The PrivateVisibilityStrategy has to be passed to JsonbConfig:


import jakarta.json.bind.JsonbBuilder;
import jakarta.json.bind.JsonbConfig;

public interface Mapper {

    final static JsonbConfig config = new JsonbConfig()
            .withFormatting(true)
            .withPropertyVisibilityStrategy(new PrivateVisibilityStrategy());

    static String save(Workshop workshop) {
        var jsonb = JsonbBuilder.create(config);
        return jsonb.toJson(workshop);
    }
        
}    

To read the Workshop record back, you will have to create a factory method annotated with @JsonbCreator:


public record Workshop(String title, Date date, String description) {

    @JsonbCreator
    public static Workshop create(String title, Date date, String description) {
        return new Workshop(title, date, description);
    }
}

The Jakarta JSON-B runtime will use the factory method instead of the default constructor to create a record instance and populate it with the JSON data. The parameter names have to be available for reflection. Either make the parameter names available via a compiler switch: (<maven.compiler.parameters>true</maven.compiler.parameters>) or directly annotate the parameters:


import jakarta.json.bind.annotation.JsonbProperty;

//...
@JsonbCreator
public static Workshop create(@JsonbProperty("title") String title, 
                              @JsonbProperty("date") Date date,
                              @JsonbProperty("description") String description) {
    return new Workshop(title, date, description);
}    

Now the Workshop record:


public record Workshop(String title, Date date, String description) {

    @JsonbCreator
    public static Workshop create(String title,Date date,String description) {
        return new Workshop(title, date, description);
    }    
}

can be serialized and deserialized in a Java SE application:


@Test
public void serializeThenDeserialize() {
    var workshop = new Workshop("hello",new Date(),"nothing");

    var serialized = Mapper.save(workshop);

    System.out.println(serialized);

    var copy = Mapper.load(serialized);
    assertEquals(workshop.title(), copy.title());
}

The test generates the following output:


{
    "date": "2021-10-07T07:58:17.606Z[UTC]",
    "description": "nothing",
    "title": "hello"
}