javajsonjakarta-eejax-rsresteasy

Jakarta EE 10 serializing person entities results in recursion checker exception (from org.eclipse.yasson.internal.serializer.RecursionChecker)


I am trying to serialize Person entities to JSON using a simple webservice, but I get the following exception:

12:27:44,923 SEVERE [org.eclipse.yasson.internal.SerializationContextImpl] (default task-1) Generating incomplete JSON
12:27:44,927 ERROR [org.jboss.resteasy.core.providerfactory.DefaultExceptionMapper] (default task-1) RESTEASY002375: Error processing request GET /bbstats/ws/person/findall - net.bbstats.webservice.PersonWebService.getPersonList: jakarta.ws.rs.ProcessingException: RESTEASY008205: JSON Binding serialization error jakarta.json.bind.JsonbException: Unable to serialize property 'player' from net.bbstats.entity.Person
    at org.jboss.resteasy.resteasy-json-binding-provider@6.2.7.Final//org.jboss.resteasy.plugins.providers.jsonb.JsonBindingProvider.asyncWriteTo(JsonBindingProvider.java:227)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.writeTo(ServerWriterInterceptorContext.java:81)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.asyncProceed(AbstractWriterInterceptorContext.java:190)
    at org.jboss.resteasy.resteasy-crypto@6.2.7.Final//org.jboss.resteasy.security.doseta.DigitalSigningInterceptor.asyncAroundWriteTo(DigitalSigningInterceptor.java:142)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.asyncProceed(AbstractWriterInterceptorContext.java:196)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.interception.jaxrs.AbstractWriterInterceptorContext.getStarted(AbstractWriterInterceptorContext.java:158)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.lambda$getStarted$0(ServerWriterInterceptorContext.java:68)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.aroundWriteTo(ServerWriterInterceptorContext.java:87)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.getStarted(ServerWriterInterceptorContext.java:68)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.ServerResponseWriter.lambda$writeNomapResponse$3(ServerResponseWriter.java:166)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:365)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.ServerResponseWriter.executeFilters(ServerResponseWriter.java:243)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:100)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.ServerResponseWriter.writeNomapResponse(ServerResponseWriter.java:73)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:518)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:458)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:240)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:157)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:229)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:222)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:55)
    at org.jboss.resteasy.resteasy-core@6.2.7.Final//org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
    at jakarta.servlet.api@6.0.0//jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
    at deployment.bbstats-3.0.war//net.bbstats.framework.filter.NoCacheFilter.doFilter(NoCacheFilter.java:33)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:67)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at org.wildfly.security.elytron-web.undertow-server@4.0.0.Final//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.lambda$handleRequest$1(ElytronRunAsHandler.java:68)
    at org.wildfly.security.elytron-base@2.2.3.Final//org.wildfly.security.auth.server.FlexibleIdentityAssociation.runAsFunctionEx(FlexibleIdentityAssociation.java:103)
    at org.wildfly.security.elytron-base@2.2.3.Final//org.wildfly.security.auth.server.Scoped.runAsFunctionEx(Scoped.java:161)
    at org.wildfly.security.elytron-base@2.2.3.Final//org.wildfly.security.auth.server.Scoped.runAs(Scoped.java:73)
    at org.wildfly.security.elytron-web.undertow-server@4.0.0.Final//org.wildfly.elytron.web.undertow.server.ElytronRunAsHandler.handleRequest(ElytronRunAsHandler.java:67)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
    at io.undertow.core@2.3.10.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.core@2.3.10.Final//io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
    at io.undertow.core@2.3.10.Final//io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
    at org.wildfly.security.elytron-web.undertow-server-servlet@4.0.0.Final//org.wildfly.elytron.web.undertow.server.servlet.CleanUpHandler.handleRequest(CleanUpHandler.java:38)
    at io.undertow.core@2.3.10.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at org.wildfly.extension.undertow@31.0.0.Final//org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:44)
    at io.undertow.core@2.3.10.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at org.wildfly.extension.undertow@31.0.0.Final//org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:51)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52)
    at io.undertow.core@2.3.10.Final//io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:276)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:132)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at org.wildfly.extension.undertow@31.0.0.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1413)
    at org.wildfly.extension.undertow@31.0.0.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1413)
    at org.wildfly.extension.undertow@31.0.0.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1413)
    at org.wildfly.extension.undertow@31.0.0.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1413)
    at org.wildfly.extension.undertow@31.0.0.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1413)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:256)
    at io.undertow.servlet@2.3.10.Final//io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:101)
    at io.undertow.core@2.3.10.Final//io.undertow.server.Connectors.executeRootHandler(Connectors.java:393)
    at io.undertow.core@2.3.10.Final//io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:859)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
    at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1348)
    at org.jboss.xnio@3.8.12.Final//org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: jakarta.json.bind.JsonbException: Unable to serialize property 'player' from net.bbstats.entity.Person
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.lambda$serialize$0(ObjectSerializer.java:43)
    at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.serialize(ObjectSerializer.java:38)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.RecursionChecker.serialize(RecursionChecker.java:38)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.KeyWriter.serialize(KeyWriter.java:41)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullVisibilitySwitcher.serialize(NullVisibilitySwitcher.java:40)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.findSerializer(ObjectTypeSerializer.java:68)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.serializeValue(ObjectTypeSerializer.java:50)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.TypeSerializer$ValueSerializer.serialize(TypeSerializer.java:51)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.TypeSerializer.serialize(TypeSerializer.java:37)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.serialize(ObjectTypeSerializer.java:31)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.CollectionSerializer.lambda$serialize$0(CollectionSerializer.java:37)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.CollectionSerializer.serialize(CollectionSerializer.java:37)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.KeyWriter.serialize(KeyWriter.java:41)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullVisibilitySwitcher.serialize(NullVisibilitySwitcher.java:40)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
    at org.eclipse.yasson//org.eclipse.yasson.internal.SerializationContextImpl.serializeObject(SerializationContextImpl.java:197)
    at org.eclipse.yasson//org.eclipse.yasson.internal.SerializationContextImpl.marshall(SerializationContextImpl.java:133)
    at org.eclipse.yasson//org.eclipse.yasson.internal.SerializationContextImpl.marshall(SerializationContextImpl.java:159)
    at org.eclipse.yasson//org.eclipse.yasson.internal.JsonBinding.toJson(JsonBinding.java:121)
    at org.jboss.resteasy.resteasy-json-binding-provider@6.2.7.Final//org.jboss.resteasy.plugins.providers.jsonb.ManagedJsonb.toJson(ManagedJsonb.java:78)
    at org.jboss.resteasy.resteasy-json-binding-provider@6.2.7.Final//org.jboss.resteasy.plugins.providers.jsonb.JsonBindingProvider.asyncWriteTo(JsonBindingProvider.java:217)
    ... 72 more
Caused by: jakarta.json.bind.JsonbException: Unable to serialize property 'person' from net.bbstats.entity.Player
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.lambda$serialize$0(ObjectSerializer.java:43)
    at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:684)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.serialize(ObjectSerializer.java:38)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.RecursionChecker.serialize(RecursionChecker.java:38)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.KeyWriter.serialize(KeyWriter.java:41)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullVisibilitySwitcher.serialize(NullVisibilitySwitcher.java:40)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.findSerializer(ObjectTypeSerializer.java:68)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.serializeValue(ObjectTypeSerializer.java:50)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.TypeSerializer$ValueSerializer.serialize(TypeSerializer.java:51)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.TypeSerializer.serialize(TypeSerializer.java:37)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.serialize(ObjectTypeSerializer.java:31)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ValueGetterSerializer.serialize(ValueGetterSerializer.java:43)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.lambda$serialize$0(ObjectSerializer.java:41)
    ... 96 more
Caused by: jakarta.json.bind.JsonbException: Recursive reference has been found in class class net.bbstats.entity.Person.
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.RecursionChecker.serialize(RecursionChecker.java:36)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.KeyWriter.serialize(KeyWriter.java:41)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullVisibilitySwitcher.serialize(NullVisibilitySwitcher.java:40)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.CyclicReferenceSerializer.serialize(CyclicReferenceSerializer.java:39)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.findSerializer(ObjectTypeSerializer.java:68)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.serializeValue(ObjectTypeSerializer.java:50)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.TypeSerializer$ValueSerializer.serialize(TypeSerializer.java:51)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.TypeSerializer.serialize(TypeSerializer.java:37)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.types.ObjectTypeSerializer.serialize(ObjectTypeSerializer.java:31)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ValueGetterSerializer.serialize(ValueGetterSerializer.java:43)
    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.lambda$serialize$0(ObjectSerializer.java:41)
    ... 110 more

Some recursion checker is complaining. This used to work using Java EE 8.

I simply want to show a list of person that have the roles as a player, a coach or a referee. A person can have all three roles, any combination of the three or none at all.

Contact abstract/super entity;

@Entity
@Table(name = "\"Contacts\"")
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
@XmlRootElement
public abstract class Contact implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    protected Integer id;

    @Basic(optional = false)
    @Column(insertable = false, updatable = false)
    @Convert(converter = ContactTypeConverter.class)
    protected ContactType type;

    ...
}

Person entity;

@Entity
@Table(name = "\"Persons\"")
@DiscriminatorValue("P")
@NamedQuery(name = Person.FIND_ALL, query = "SELECT pe FROM Person pe ORDER BY pe.lastName, pe.firstName")
@NamedEntityGraph(name = Person.FETCH_EMAIL_ADDRESSES_PHONE_NUMBERS_AND_ROLES,
    attributeNodes = {
        @NamedAttributeNode(value = "player"),
        @NamedAttributeNode(value = "coach"),
        @NamedAttributeNode(value = "referee")}
)
public class Person extends Contact
{
    private static final long serialVersionUID = 1L;

    public static final String FIND_ALL = "Person.findAll";
    public static final String FETCH_ROLES = "Person.fetchRoles";

    @Basic(optional = false)
    @Column(name = "first_name")
    private String firstName;

    @Basic(optional = false)
    @Column(name = "last_name")
    private String lastName;

    @Basic(optional = false)
    @Column
    @Enumerated(EnumType.STRING)
    private PersonGender gender = PersonGender.MALE;

    @Basic
    @Column(name = "birth_date")
    private LocalDate birthDate;

    @Basic(optional = false)
    @Column(name = "is_incognito")
    private Boolean incognito = Boolean.FALSE;

    @OneToOne(mappedBy = "person", fetch = FetchType.LAZY)
    private Coach coach;

    @OneToOne(mappedBy = "person", fetch = FetchType.LAZY)
    private Player player;

    @OneToOne(mappedBy = "person", fetch = FetchType.LAZY)
    private Referee referee;
    
    ...
}

Player entity (possible role #1);

@Entity
@Table(name = "\"Players\"")
@XmlRootElement
public class Player implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @Column
    private Integer id;

    @Basic
    @Column(name = "registration_nbr")
    private String registrationNbr;

    @OneToOne(optional = false, fetch = FetchType.EAGER)
    @JoinColumn(name = "id", insertable = false, updatable = false)
    @XmlTransient
    private Person person;
    
    ...
}

Coach entity (possible role #2);

@Entity
@Table(name = "\"Coaches\"")
@XmlRootElement
public class Coach implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @Column
    private Integer id;

    @Basic
    @Column(name = "license_nbr")
    private String licenseNbr;

    @OneToOne(optional = false, fetch = FetchType.EAGER)
    @JoinColumn(name = "id", insertable = false, updatable = false)
    @XmlTransient
    private Person person;
    
    ...
}

Referee entity (possible role #3);

@Entity
@Table(name = "\"Referees\"")
@XmlRootElement
public class Referee implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @Column
    private Integer id;

    @Basic
    @Column(name = "license_nbr")
    private String licenseNbr;

    @OneToOne(optional = false, fetch = FetchType.EAGER)
    @JoinColumn(name = "id", insertable = false, updatable = false)
    @XmlTransient
    private Person person;
    
    ...
}

As you can see I wasn't able to use inheritance for Player, Referee and Coach. JPA is specified to only support total inheritance. So basically, there's a simple @OneToOne mapping for each role.

Note that there's @DiscriminatorValue("P") on the Person entity, because there are persons, clubs and arenas, which can generally be contacted and are derived from Contact via real @Inheritance(strategy = InheritanceType.JOINED) (see entity above). I omitted those in my example.

PersonWebService:

@Path("/person")
public class PersonWebService {
    @Inject
    private PersonService personService;

    @GET
    @Path("/findall")
    @Produces(MediaType.APPLICATION_JSON)
    public List<Person> getPersonList() {
        List<Person> persons = personService.findAllWithFetchGraph(Person.FETCH_ROLES); // <-- EXCEPTION!

        return persons;
    }

    @GET
    @Path("/find/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Person getPerson(@PathParam("id") Integer id) {
        Person person = personService.findByPk(id);

        return person;
    }
}

Here's the error again:

RESTEASY008205: JSON Binding serialization error jakarta.json.bind.JsonbException: Unable to serialize property 'player' from net.bbstats.entity.Person

QUESTION:

What's wrong here?

Why is there an exception being thrown, given that Player.person is declared as @XmlTransient private Person person;? This happens for all role entities BTW. I want all persons to serialize the three roles into JSON for further processing.

My setup is Jakarta EE 10 using WildFly 31.0.0.Final. Basically everything being used (class-loaded) is coming from there, as-is. I have no special webapp libraries regarding webservices/JSON.

RESTeasy seems to be 6.0.0.Beta1.


Solution

  • WildFly 31 and RESTEasy use Jakarta JSON Binding as the default JSON object serializer. The XML binding annotations do not work there. You either need to change the annotations to use the Jakarta JSON Binding annotations or switch to using Jackson as the JSON serializer 2.

    If you want to use Jackson for all deployments by default you can change the resteasy-prefer-jackson-over-jsonb. With CLI the command would be:

    /subsystem=jaxrs:write-attribute(name=resteasy-prefer-jackson-over-jsonb, value=true)
    

    FWIW the RESTEasy version for WildFly 31 is 6.2.7.Final.