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

如何用Jackson实现POJO非空属性深拷贝及嵌套对象通用合并

Solution for Jackson Deep Merge with Non-Null Only Updates

Great question! I've run into this exact issue with Jackson's updateValue behavior before—here's a clean way to handle nested merging globally without having to configure each POJO individually:

The core problem you're facing is that:

  • Default updateValue replaces entire nested objects instead of merging their properties
  • Global setDefaultMergeable(true) allows merging, but also lets null values from the source overwrite non-null values in the destination
  • Configuring setMergeable(true) for each nested class isn't scalable with many POJOs

Fix: Custom BeanDeserializerModifier for Global Merging + Null Skipping

We can create a custom BeanDeserializerModifier to enable merging for all your POJO classes, while keeping the Nulls.SKIP rule to prevent null source values from overwriting destination data.

Step 1: Implement the BeanDeserializerModifier

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder;

public class GlobalMergeableModifier extends BeanDeserializerModifier {
    @Override
    public BeanDeserializerBuilder updateBuilder(
            DeserializationConfig config, BeanDescription beanDesc, BeanDeserializerBuilder builder) {
        // Enable merging for all your application POJOs (adjust the condition as needed)
        if (!beanDesc.getBeanClass().isPrimitive() && 
            !beanDesc.getBeanClass().getPackageName().startsWith("java.")) {
            builder.setMergeable(true);
        }
        return super.updateBuilder(config, beanDesc, builder);
    }
}

(Note: The condition here excludes primitive types and JDK classes—tweak it to match your project's POJO criteria, like checking for a custom annotation or specific package prefix.)

Step 2: Configure ObjectMapper with the Modifier + Null Skipping

ObjectMapper mapper = new ObjectMapper();

// Skip setting null values from source to destination
mapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));

// Register the custom modifier to enable global merging for your POJOs
mapper.registerModule(new SimpleModule() {
    @Override
    public void setupModule(SetupContext context) {
        super.setupModule(context);
        context.addBeanDeserializerModifier(new GlobalMergeableModifier());
    }
});

// Perform the update
Person result = mapper.updateValue(destPerson, sourcePerson);

What This Does

Let's validate with your example:

  • Source: homePhoneNumber="123", address.street="Street" (other fields null)
  • Destination: officePhoneNumber="456", homePhoneNumber="123", address.street="Street2", address.houseNumber="246"

After the update:

  • homePhoneNumber remains "123" (source matches destination, no change)
  • officePhoneNumber stays "456" (source has null, skipped)
  • address.street is updated to "Street" (source has non-null, merged)
  • address.houseNumber remains "246" (source has null, skipped—no overwrite)
  • The Address object is merged, not replaced entirely

Why This Works

  • The GlobalMergeableModifier tells Jackson to merge nested beans instead of replacing them, eliminating the need to configure each class individually.
  • Nulls.SKIP ensures that any null properties in the source don't overwrite existing non-null values in the destination—even when merging nested objects.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:17:21