Showing posts with label records. Show all posts
Showing posts with label records. Show all posts

Saturday, 27 July 2024

jakarta.json.bind.JsonbException: Cannot create instance of a record: class SomeClass, Multiple constructors found.

So I've been trying to use Java Records as data holders for JSONB serialisation and deserialisation. And it works well, until I encountered the following:

jakarta.json.bind.JsonbException: Cannot create instance of a record: class SomeClass, Multiple constructors found.

It turns out, I can put in an annotation @JsonbCreator on it, as explained in reference [1].

It just takes a bit of work, as I cannot put the annotation on top of a record class, it needs to be put on a constructor. The way this works, is to create a compact constructor in the record.

The compact constructor is usually used to put in some sort of validation in the record class upon instantiation, but here we need it for the annotation @JsonbCreate.

Brian Goetz in the comments of reference [2] indicates that this is the proper way to do it.

Unfortunately, it causes one of those "empty constructors" messages of SonarLint.

Warning:(31, 10) Remove this redundant constructor which is the same as a default one.

I assume SonarLint will fix this eventually.

For completeness, my code:

public record AdminItem(Integer id,
                        String belongsto,
                        Long room,
                        String shopkeeper,
                        String owner,
                        LocalDateTime creation)
{

  @JsonbCreator
  public AdminItem
  {
    // empty constructor, because I need to put the annotation @JsonbCreator somewhere.
  }

  public AdminItem(Item item)
  {
    this(item.getId(), 
        item.getBelongsTo() == null ? null : item.getBelongsTo().getName(),
        item.getRoom() == null ? null : item.getRoom().getId(),
        null,
        item.getOwner() == null ? null : item.getOwner().getName(),
        item.getCreation());
  }

}

References

[1] Carlos Chacin - 💾 Java 14 Records 🐞 with JakartaEE JSON-B
https://carloschac.in/2020/04/20/java-records-jsonb/
[2] StackOverflow - Constructor annotation on java records
https://stackoverflow.com/questions/67168624/constructor-annotation-on-java-records

Thursday, 13 October 2022

Why don't record classes follow the Java Bean conventions?

Records are named tuples1.

Notice that the name of the field accessors does not start with get and, therefore, does not conform to the JavaBeans conventions.

We can declare getter methods if we really want to, and let them refer to the record method, but I'd opt not to if possible. We can do it, for a little while, until frameworks catch up to the new code convention, which will be pretty quick.

As Brian Goetz wrote in an online thread2:

“. . . the language has a responsibility to look forward as well as backward, and balance the needs of existing code with the needs of new code. Taking a bad library naming convention and burning it into the language forever would have been the worse choice.”

Builders

We can create builders for Records, if we like. See [3] for instance, but I'm not a fan of automatic code generation.

FreeMarker

The reason I started on this blog, is because there's a plethora of different frameworks/tools/what-have-you available that all center around the Java Beans conventions.

In most cases, records are not a good fit for these, as those frameworks and tools operate on the notion that these are mutable data classes.

But sometimes, there are definitely simple use cases where it would come in handy.

For example with the use of Freemarker4, a templating engine that converts your templates and Java POJOs to nice html pages (or other kinds of pages, there's no limit really).

And these follow the getX() convention.

So, one way would be to create the blasted getter methods in the Record class, but then you're adding boilerplate again, and I feel uncomfortable with that.

Perhaps an idea is to create my own DefaultObjectWrapper5.

So, Freemarker will need some changes to support record classes6. Something like automatically generating a hashmap of all the methods that have no arguments, yet return something... and preferable do not start with get?

References

[1] Data Classes and Sealed Types for Java - Brian Goetz February 2019
https://openjdk.java.net/projects/amber/design-notes/records-and-sealed-classes
[2] ForumPost - Feedback for records: Accessors name() vs. getName()
https://mail.openjdk.java.net/pipermail/amber-dev/2020-August/006414.html
[3] GitHub - RecordBuilder
https://github.com/Randgalt/record-builder
[4] FreeMarker
https://freemarker.apache.org/
[5] FreeMarker - Object wrappers
https://freemarker.apache.org/docs/pgui_datamodel_objectWrapper.html#pgui_datamodel_defaultObjectWrapper
[6] Apache JIRA - FREEMARKER-183
https://issues.apache.org/jira/browse/FREEMARKER-183
JLS - 8.10. Record Classes
https://docs.oracle.com/javase/specs/jls/se16/html/jls-8.html#jls-8.10
JavaMagazine - Records Come to Java
https://blogs.oracle.com/javamagazine/post/records-come-to-java
JavaMagazine - Java records: Serialization, marshaling, and bean state validation
https://blogs.oracle.com/javamagazine/post/diving-into-java-records-serialization-marshaling-and-bean-state-validation