将 POJO 的空字段作为映射中的键 java

Keep null fields of a POJO as keys in map java

我有一个 POJO 并使用对象映射器将其转换为地图。我希望地图在其条目集中包含 POJO 的所有字段,即使它们的值为 null。因此,该条目在我创建的地图中可能为空。

Map<String, Object> oldDetails = objectMapper.convertValue(oldFields, Map.class);

我试过使用下面的代码片段,但这对我没有帮助。

objectMapper.setDefaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.ALWAYS, JsonInclude.Include.ALWAYS));

还有什么我可以在这里使用的想法吗?

我的 POJO:

@JsonInclude(NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class BaseFields {

    @JsonProperty("number") protected String number;
    @JsonProperty("name") protected String name;
    @JsonProperty("dob") protected String dob;

}

还有其他 类 继承自 BaseFields,例如。

@JsonInclude(NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class CustomerBaseFields extends BaseFields {
    @JsonProperty("email") protected String email;         
}

现在有一种方法可以将 ChangedBaseFields 的实例转换为映射。

ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, true);
        objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
        objectMapper.setDefaultPropertyInclusion(JsonInclude.Value.construct(JsonInclude.Include.ALWAYS, JsonInclude.Include.ALWAYS));
        Map<String, Object> oldDetails = objectMapper.convertValue(baseFields, Map.class);

但是如果像电子邮件这样的字段在对象中为空,我的地图中将不会收到关键电子邮件。

下面是简单的代码,证明您的代码实际上可以在没有任何额外配置的情况下使用默认的 ObjectMapper。

package java17test;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Map;

public class Test {
    public static void main(String[] args) {
        var objectMapper = new ObjectMapper();
        
        var oldFields = new TestClass();
        oldFields.setFirstName("first name");

        Map<?, ?> oldDetails = objectMapper.convertValue(oldFields, Map.class);
        oldDetails.forEach((key, value) -> System.out.println(key + "=" + value));
    }

    static class TestClass {
        private String firstName;
        private String lastName;

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    }
}

输出:

firstName=first name
lastName=null

您可能有一个 X-Y problem 因为您在对 vbezhenar 的回答的评论中提到您的目的是比较相同类型的两个对象的字段,以找出哪些字段发生了变化。

您不必使用 Jackson 即可。

这是比较两个对象的简单纯 Java(无依赖)方式:

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

public class Utils {
    /**
     * Compares two objects of the same type, returning the field values that are different.
     *
     * @param o1 an object
     * @param o2 another object
     * @return a map of field name to a two-element array of the two different values of o1 and o2
     */
    public static <T> Map<String, Object[]> comparePojos(T o1, T o2) throws IllegalAccessException {
        Map<String, Object[]> diffs = new LinkedHashMap<>();

        for (Field field : o1.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            Object[] values = {field.get(o1), field.get(o2)};
            if (!values[0].equals(values[1]))
                diffs.put(field.getName(), values);
        }
        return diffs;
    }
}

class UtilsDemo {
    public static void main(String[] args) throws IllegalAccessException {
        SamplePojo a = new SamplePojo("alice", 42);
        SamplePojo b = new SamplePojo("bob", 42);
        Utils.comparePojos(a, b).forEach((k, v) -> System.out.println(k + ": " + Arrays.toString(v)));
    }
}

class SamplePojo {
    public SamplePojo(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}