Jakarta EE自定义JsonbSerializer跨机器序列化异常排查
在Jakarta EE环境中,我为Order对象实现了自定义JsonbSerializer。同一代码(包含pom.xml、server.xml等依赖配置)从GitHub拉取后,在使用Maven 3.6.0的机器上能正常序列化,但在使用Maven 3.6.3的机器上,Open Liberty服务器抛出“生成不完整JSON”错误,且两次序列化的Order对象完全一致。
调用的API函数
@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(); }
自定义序列化器
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(); } }
Open Liberty服务器错误日志
[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)
解决方案
从错误栈可以明确,空指针异常发生在OrderSerializer.java第25行,对应代码jsonGenerator.write("deliveryAddress", order.getDeliveryAddress());——说明order.getDeliveryAddress()返回了null,而Maven 3.6.3拉取的Eclipse Parsson新版本依赖,在处理null字符串时会直接抛出空指针,而Maven 3.6.0使用的旧版本依赖对null有容错处理,因此没有报错。
修复步骤
添加非空判断:在序列化可能为null的字段前增加判断,避免传递null给JsonGenerator:
// 原代码 jsonGenerator.write("deliveryAddress", order.getDeliveryAddress()); // 修改后 if (order.getDeliveryAddress() != null) { jsonGenerator.write("deliveryAddress", order.getDeliveryAddress()); }同时检查序列化器中其他类似字段,比如
customer的telephone、billingAddress,driver的email等,都需要补充非空判断。全局配置null处理策略:如果不想逐个字段判断,可以通过
JsonbConfig统一设置null值处理规则,让框架自动处理:JsonbConfig config = new JsonbConfig().withNullValues(true); // 输出null值,或设置为false忽略null字段 Jsonb jsonb = JsonbBuilder.create(config);这种方式更简洁,但如果需要精确控制某些字段的输出逻辑,还是手动添加非空判断更稳妥。
内容的提问来源于stack exchange,提问作者stitch123

