Search code examples
jakarta-eejsonbopen-liberty

Custom JsonbSerializer serialises properly on one machine, generates incomplete JSON on another


I'm trying a custom serialiser for Order objects in Jakarta EE. The problem is that it works properly on one machine, while the other complains about incomplete JSON. It's the exact same code pulled from our Github, including pom.xml, server.xml, and other files that handle dependencies. The only difference is that one machine uses Maven 3.6.0, while the other has Maven 3.6.3. The order being serialised is also exactly the same.

The API function being called:

    @Path("/{id}")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getOrder(@PathParam("id") long id) {
        Order o = orderManager.find(id);

        if (o == null) {
            return Response.status(Response.Status.NOT_FOUND).entity("Order not found.").build();
        }

        return Response.ok(o).build();
    }

The custom serialiser:

public class OrderSerializer implements JsonbSerializer<Order> {
    @Override
    public void serialize(Order order, JsonGenerator jsonGenerator, SerializationContext serializationContext) {
        jsonGenerator.writeStartObject();
        jsonGenerator.write("id", order.getId());
        jsonGenerator.write("dateCreated", order.getDateCreated().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        jsonGenerator.write("total", order.getTotalPrice());
        jsonGenerator.write("deliveryAddress", order.getDeliveryAddress());

        if (order.getCustomer() != null) {
            jsonGenerator.writeStartObject("customer");
            jsonGenerator.write("id", order.getCustomer().getId());
            jsonGenerator.write("name", order.getCustomer().getName());
            jsonGenerator.write("email", order.getCustomer().getEmail());
            jsonGenerator.write("telephone", order.getCustomer().getTelephone());
            jsonGenerator.write("billingAddress", order.getCustomer().getBillingAddress());
            jsonGenerator.writeEnd();
        }

        if (order.getNote() != null) {
            jsonGenerator.write("note", order.getNote());
        }
        jsonGenerator.write("status", order.getStatus().toString());

        jsonGenerator.writeStartArray("products");
        for (ProductInOrder pio : order.getProducts()) {
            jsonGenerator.writeStartObject();
            jsonGenerator.write("id", pio.getProduct().getId());
            jsonGenerator.write("name", pio.getProduct().getName());
            jsonGenerator.write("amount", pio.getAmount());
            jsonGenerator.writeEnd();
        }
        jsonGenerator.writeEnd();

        if (order.getPlan() != null) {
            jsonGenerator.writeStartObject("plan");
            jsonGenerator.write("id", order.getPlan().getId());
            jsonGenerator.write("date", order.getPlan().getDate().format(DateTimeFormatter.ISO_LOCAL_DATE));
            jsonGenerator.write("difficulty", order.getPlan().getDifficulty());
            jsonGenerator.write("completed", order.getPlan().isCompleted());
            jsonGenerator.writeEnd();
        }

        if (order.getDelivery() != null) {
            jsonGenerator.writeStartObject("delivery");
            jsonGenerator.write("id", order.getDelivery().getId());
            jsonGenerator.write("date", order.getDelivery().getDate().format(DateTimeFormatter.ISO_LOCAL_DATE));
            if (order.getDelivery().getDeliveredOn() != null) {
                jsonGenerator.write("deliveredOn", order.getDelivery().getDeliveredOn().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            }
            if (order.getDelivery().getDriver() != null) {
                jsonGenerator.writeStartObject("driver");
                jsonGenerator.write("id", order.getDelivery().getDriver().getId());
                jsonGenerator.write("name", order.getDelivery().getDriver().getName());
                jsonGenerator.write("email", order.getDelivery().getDriver().getEmail());
                jsonGenerator.writeEnd();
            }
            jsonGenerator.writeEnd();
        }


        jsonGenerator.writeEnd();

    }

}

The error being thrown by our Open Liberty server:

[ERROR   ] Generating incomplete JSON
[INFO] [WARNING ] CWPMI2006W: The MicroProfile Metrics exception mapper detected and is handling an unhandled exception from the RESTful web service application. 
[INFO]                                                                                                                jakarta.json.bind.JsonbException: Internal error: null
[INFO]     at org.eclipse.yasson.internal.SerializationContextImpl.marshall(SerializationContextImpl.java:137)
[INFO]     at [internal classes]
[INFO]     at com.ourproject.rest.OrderAPI.getOrder(OrderAPI.java:62)
[INFO]     at com.ourproject.rest.OrderAPI$Proxy$_$$_WeldSubclass.getOrder(Unknown Source)
[INFO]     at jdk.internal.reflect.GeneratedMethodAccessor1515.invoke(Unknown Source)
[INFO]     at java.base/java.lang.reflect.Method.invoke(Method.java:566)
[INFO]     at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
[INFO]     at [internal classes]
[INFO] Caused by: java.lang.NullPointerException
[INFO]     at org.eclipse.parsson.JsonGeneratorImpl.writeEscapedString(JsonGeneratorImpl.java:539)
[INFO]     at [internal classes]
[INFO]     at com.ourproject.serialisation.OrderSerializer.serialize(OrderSerializer.java:25)
[INFO]     at com.ourproject.serialisation.OrderSerializer.serialize(OrderSerializer.java:11)
[INFO]     at org.eclipse.yasson.internal.serializer.UserDefinedSerializer.serialize(UserDefinedSerializer.java:35)

Solution

  • The issue was implementation specific, I noticed my Customer JSON test data was wrong on one of the machines. The Customer then got assigned to the Order, and the serialiser tried to access the Customer's phone number which was NULL because of an error on my part. Thanks to Scott Kurz for pointing me in the right direction.