javaserializationjax-rsquarkus

JaxRS: Generating incomplete JSON, Unable to serialize property 'finalScores' from FinalScoreResponse


I have an endpoint that when called gives this error:

2024-10-23 12:21:14,135 SEVERE [org.ecl.yas.int.SerializationContextImpl] (executor-thread-3) Generating incomplete JSON
2024-10-23 12:21:14,136 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-3) HTTP Request to /api/contacts/scores/compound/gambit-digital.com failed, error id: 971b0a13-4991-45ff-b720-dfb50644855f-20: jakarta.json.bind.JsonbException: Unable to serialize property 'finalScores' from at.hksolutions.gambit.boundary.jaxrs.FinalScoreResponse

The endpoint looks like this:

@GET
@Path("mypath")
@Produces(MediaType.APPLICATION_JSON)
public FinalScoreResponse getFinalScores() {
    .....
    // Some logic
    // Serialization fails:
    return new FinalScoreResponse(finalScores,  cache.lastUpdated);
}

A FinalScoreResponse looks like this:

public record FinalScoreResponse(
        List<FinalScore> finalScores,
        Date lastUpdated
) {
}

What I have tried:

So the issue is clearly related to List<FinalScore> not being able to be serialized within the FinalScoreResponse. It is weird though, because on its own, it can be serialized.

A FinalScore looks like this:

public record FinalScore(
        String id,
        String name,
        double score,
        int normalizedScore,
        String lifecycleStage
) {
}

Any solutions to having it serialized within the FinalScoreResponse?

Note: I can confirm the logic and endpoint gets called properly, it's solely are serialization issue.


Solution

  • The issue was that my List<FinalScore> finalScores was being incorrectly deserialized with objectMapper.readValue(json) before it was used in the response. Instead of getting a list of FinalScore objects, I ended up with a raw List of objects with unknown types. Interestingly, this didn't throw an error during deserialization:

    List<FinalScore> finalScores = cacheService.fromJson(json);
    

    This occurred due to Java's type erasure, which strips away generic type information at runtime, causing the JSON to be deserialized into a raw List<Object>. Without specifying the target type, the deserialization framework couldn’t infer that the list should contain FinalScore objects.

    To fix this, I needed to provide the correct type information explicitly by using a TypeReference. Here's the updated fromJson method:

    objectMapper.readValue(json, new TypeReference<List<FinalScore>>() {});
    

    This ensures that the JSON is correctly deserialized into a List<FinalScore>, avoiding the type erasure issue and preventing any unexpected runtime behavior.