【问题标题】:Deep copy of properties one object to anothe from jackson?从杰克逊到另一个对象的属性的深层复制?
【发布时间】:2019-11-28 06:25:17
【问题描述】:

我正在执行 UPDATE 操作,以便将传入 POJO 中设置的所有 non-null 属性复制到另一个对象(相同类型)中,嵌套属性也会发生这种情况。

例如:

POJO:

    public class Person {

        private String homePhoneNumber;
        private String officePhoneNumber;

        private Address address;

        public String getHomePhoneNumber() {
            return homePhoneNumber;
        }

        // getter/setters


    }

    public class Address {

        private String street;
        private String houseNumber;

        public String getStreet() {
            return street;
        }
        // getter/setters
    }

    //  Source

        Person sourcePerson = new Person();
        sourcePerson.setHomePhone("123");

        Address address1 = new Address();
        address1.setStreet("Street");

        sourcePerson.setAddress(address1);

    //Dest person

        Person destPerson = new Person();
        destPerson.setOfficePhone("456");
        destPerson.setHomePhone("123");

        Address address2 = new Address();
        address2.setStreet("Street2");
        address2.setHouseNumber("246");

        destPerson.setAddress(address2);

        ObjectMapper mapper = new ObjectMapper();
       //skip setters for null values
mapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));

        Person result = mapper.updateValue(destPerson, sourcePerson);

所以我想复制sourcePerson 中设置的所有non-null 属性以在destPerson 中复制,即覆盖destperson 的那些destperson 属性设置,保留其他属性不变。

使用 Person result = mapper.updateValue(destPerson, sourcePerson); 不适用于嵌套属性。它将整个 Address 对象从源替换到目标

我通过 jackson 搜索找到 jackson 中的 merge 功能:

mapper.setDefaultMergeable(true);

但是,添加此配置会使sourcePerson 中的null 值也使destPerson 中的值无效,这似乎很奇怪。

mapper.configOverride(Address.class).setMergeable(true);

上述配置适用于我想要的。但是我有很多 POJO 嵌套资源,所以我不想要每个 POJO 的特定配置。

这可以通过jackson 以干净的方式实现吗?

【问题讨论】:

    标签: json serialization jackson deserialization jackson-databind


    【解决方案1】:

    您可以首先启用与合并

    相关的错误报告

    com.fasterxml.jackson.databind.MapperFeature#IGNORE_MERGE_FOR_UNMERGEABLE

    这必须是假的。

    mapper.configOverride() 可以工作,而mapper.setDefaultMergeable() 却不行,这确实很奇怪。

    我在您的示例中没有看到二传手。你不是在Person 类中使用@JsonSetter 注释吗?然后他们会覆盖映射器配置。

    jackson-databind 单元测试中,我看到他们使用的是 mapper.readerForUpdating() 而不是 mapper.updateValue()

     private final ObjectMapper MAPPER = objectMapperBuilder()
                // 26-Oct-2016, tatu: Make sure we'll report merge problems by default
                .disable(MapperFeature.IGNORE_MERGE_FOR_UNMERGEABLE)
                .build();
    
        public void testBeanMergingWithNullDefault() throws Exception
        {
            // By default `null` should simply overwrite value
            ConfigDefault config = MAPPER.readerForUpdating(new ConfigDefault(5, 7))
                    .readValue(aposToQuotes("{'loc':null}"));
            assertNotNull(config);
            assertNull(config.loc);
    
            // but it should be possible to override setting to, say, skip
    
            // First: via specific type override
            // important! We'll specify for value type to be merged
            ObjectMapper mapper = newObjectMapper();
            mapper.configOverride(AB.class)
                .setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));
            config = mapper.readerForUpdating(new ConfigDefault(137, -3))
                    .readValue(aposToQuotes("{'loc':null}"));
            assertNotNull(config.loc);
            assertEquals(137, config.loc.a);
            assertEquals(-3, config.loc.b);
    
            // Second: by global defaults
            mapper = newObjectMapper();
            mapper.setDefaultSetterInfo(JsonSetter.Value.forValueNulls(Nulls.SKIP));
            config = mapper.readerForUpdating(new ConfigDefault(12, 34))
                    .readValue(aposToQuotes("{'loc':null}"));
            assertNotNull(config.loc);
            assertEquals(12, config.loc.a);
            assertEquals(34, config.loc.b);
        }
    

    也值得尝试在Person 类中直接使用com.fasterxml.jackson.annotation.JsonMerge

    【讨论】:

    • 我无法编辑我的数据模型,因此无法注释它们。但是,我尝试了与mapper.setDefaultMergeable(true) 一起使用的mapper.readerForUpdating(dest).readValue();奇怪的是它不适用于mapper.updateValue()。最后,我希望我的List 被替换而不是合并,所以为此添加了mapper.configOverride(List.class).setMergeable(false);
    • 还有一个要求,当用户将属性设置为 nulldefault-valuenull 时,我想区分该属性。例如:一个 POJO 的 BigInteger 属性设置为 null,而它的默认值也是 null。那么我可以在反序列化时做些什么来区分这个是用户设置的null而不是默认设置吗?
    • @SiddharthTrikha 查看com.fasterxml.jackson.databind.deser.std.NumberDeserializers.BigIntegerDeserializer,似乎为空BigInteger 设置的默认值是BigInteger.ZERO。因此,也许您可​​以将 null (与零不同)用于用户设置的情况。
    • 好吧,我没有看到我的 BigInteger 默认设置为 0,但它不是我必须处理的唯一数据类型。可以有任何数据类型ListBoolean 等,其中用户定义的nulldefault null 需要区分。我一直在想,在反序列化的同时可以做一些微分吗?或者你有更好的解决方案?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-18
    • 2017-09-03
    • 2015-09-22
    • 1970-01-01
    • 2012-09-18
    相关资源
    最近更新 更多