You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jakarta EE自定义JsonbSerializer跨机器序列化异常排查

问题:Jakarta EE自定义JsonbSerializer在不同Maven版本下序列化失败

在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有容错处理,因此没有报错。

修复步骤

  1. 添加非空判断:在序列化可能为null的字段前增加判断,避免传递null给JsonGenerator:

    // 原代码
    jsonGenerator.write("deliveryAddress", order.getDeliveryAddress());
    
    // 修改后
    if (order.getDeliveryAddress() != null) {
        jsonGenerator.write("deliveryAddress", order.getDeliveryAddress());
    }
    

    同时检查序列化器中其他类似字段,比如customer的telephone、billingAddress,driver的email等,都需要补充非空判断。

  2. 全局配置null处理策略:如果不想逐个字段判断,可以通过JsonbConfig统一设置null值处理规则,让框架自动处理:

    JsonbConfig config = new JsonbConfig().withNullValues(true); // 输出null值,或设置为false忽略null字段
    Jsonb jsonb = JsonbBuilder.create(config);
    

    这种方式更简洁,但如果需要精确控制某些字段的输出逻辑,还是手动添加非空判断更稳妥。

内容的提问来源于stack exchange,提问作者stitch123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 19:37:08