ObjectMapper 将类转换为映射

问题描述 投票:0回答:2

ObjectMapper 将类转换为映射,我遇到了麻烦。 我有一个 util 类。像这样。

    @Slf4j
    public class JsonUtil {
        private static final ObjectMapper objectMapper;
        static {
            objectMapper = new ObjectMapper();
            JavaTimeModule javaTimeModule = new JavaTimeModule();
            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            objectMapper.registerModule(javaTimeModule);
            objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        }
        public static Map<String, Object> toMap(Object obj) {
            try {
                return objectMapper.convertValue(obj, new TypeReference<>() {
                });
            } catch (Exception e) {
                log.error("failed:", e);
                return Map.of();
            }
        }
    
        public static Map<String, Object> toMapIgnoreNull(Object obj) {
            try {
                return objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)
                        .convertValue(obj, new TypeReference<>() {
                        });
            } catch (Exception e) {
                log.error("failed:", e);
                return Map.of();
            }
        }
    }

toMapIgnoreNull
方法似乎将对象转换为映射,同时忽略空值。它使用 Jackson 的
ObjectMapper
来实现此目的。以下是其作用的详细说明:

  1. 它将序列化包含设置为

    NON_NULL
    ,这意味着空值不会包含在生成的 JSON 中。

  2. 它使用

    convertValue
    ObjectMapper
    方法将给定对象转换为地图。

如果在此过程中发生任何异常,它会捕获异常,记录错误消息,并返回一个空映射。

这个

toMap
方法也用于将对象转换为映射,但它不会忽略空值。它还利用了 Jackson 库中的
ObjectMapper
。下面是该方法的详细解释:

  1. 它使用

    convertValue
    ObjectMapper
    方法将给定对象转换为地图。

  2. 如果在此过程中发生任何异常,它会捕获异常,记录错误消息,并返回一个空地图。

与前一种方法相比,此方法不会过滤掉 null 值,而是保留对象中的所有键值对,即使该值为 null。

什么时候

@Data
    @EqualsAndHashCode(callSuper = false)
    static class User {
        private String username;
        private String password;
    }

    public static void johnOut() {
        User john = new User();
        john.setUsername("John");
        Map<String, Object> johnMap = toMap(john);
        System.out.println("result 1: " + johnMap);
        Map<String, Object> JohnIgnoreNullMap = toMapIgnoreNull(john);
        System.out.println("result 2: " + JohnIgnoreNullMap);
    }

    private static void jimOut() {
        User jim = new User();
        jim.setUsername("jim");
        Map<String, Object> jimIgnoreNullMap = toMapIgnoreNull(jim);
        System.out.println("result 3: " + jimIgnoreNullMap);
        Map<String, Object> jimMap = toMap(jim);
        System.out.println("result 4: " + jimMap);
    }

    public static void main(String[] args) {
        johnOut();
        jimOut();
    }
when `johnOut()` is in front, the console prints:

result 1: {username=John, password=null}
result 2: {username=John, password=null}
result 3: {username=jim, password=null}
result 4: {username=jim, password=null}

but when `jimOut()` is in front, the console prints:

result 3: {username=jim}
result 4: {username=jim}
result 1: {username=John}
result 2: {username=John}

why?

expectation:
result 1: {username=John, password=null}
result 2: {username=John, password=null}
result 3: {username=jim}
result 4: {username=jim}
or

result 3: {username=jim}
result 4: {username=jim}
result 1: {username=John, password=null}
result 2: {username=John, password=null}

Is there any experience that can help me?
java jackson
2个回答
0
投票

为了确保每个方法调用独立运行而不影响彼此的设置,需要在每个方法调用内创建一个新的ObjectMapper实例。这样,每个方法都有自己的 ObjectMapper 实例和自己的设置,并且在一个方法中所做的更改不会影响另一个方法的行为。

这里是类似问题的有用答案:https://stackoverflow.com/a/37409580/12403892


0
投票

您看到此行为的原因是您的

ObjectMapper
类中设置
JsonUtil
的方式。
ObjectMapper
实例设置为静态变量,这意味着它在
toMap
toMapIgnoreNull
方法的所有调用之间共享。

setSerializationInclusion(JsonInclude.Include.NON_NULL)
函数会更改
ObjectMapper
的状态 - 一旦调用它,使用相同
ObjectMapper
完成的所有后续序列化也将忽略空值,除非您专门重置它。

您在

johnOut()
jimOut()
中看到不同的结果,因为执行顺序决定了
ObjectMapper
是否设置为排除空值。

这是您的

JsonUtil
课程的改进版本:

@Slf4j
public class JsonUtil {
    private static final ObjectMapper objectMapper;
    static {
        objectMapper = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        objectMapper.registerModule(javaTimeModule);
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    }
    
    public static Map<String, Object> toMap(Object obj) {
        try {
            return objectMapper.convertValue(obj, new TypeReference<>() {});
        } catch (Exception e) {
            log.error("failed:", e);
            return Map.of();
        }
    }

    public static Map<String, Object> toMapIgnoreNull(Object obj) {
        try {
            ObjectMapper localMapper = objectMapper.copy();
            localMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
            return localMapper.convertValue(obj, new TypeReference<>() {});
        } catch (Exception e) {
            log.error("failed:", e);
            return Map.of();
        }
    }
}

toMapIgnoreNull
方法中,我创建了
ObjectMapper
的本地副本,以防止它影响
toMapIgnoreNull
toMap
的任何其他调用。现在,您的
johnOut()
jimOut()
方法可以按任意顺序排列,并且它们应该根据所使用的方法产生可预测的结果。

© www.soinside.com 2019 - 2024. All rights reserved.