• ObjectMapper的使用和使用过程中引发的思考


    背景:

    Java开发中,ObjectMapper是Jackson库的核心类,用于将Java对象序列化为JSON字符串,或者将JSON字符串反序列化为Java对象。由于其功能强大且易于使用,ObjectMapper成为了处理JSON数据的常用工具,它可以帮助我们快速的进行各个类型和Json类型的相互转换。然而,在实际开发中,很多开发者可能会犯一个常见的错误:频繁地创建ObjectMapper实例

    先说一下我们代码使用中发现的一些习惯案例:

    一、ObjectMapper的使用

    1.引入Jackson的依赖

    1. <!-- 根据自己需要引入相关版本依赖。 -->
    2. <dependency>
    3. <groupId>com.fasterxml.jackson.core</groupId>
    4. <artifactId>jackson-core</artifactId>
    5. <version>2.9.10</version>
    6. </dependency>
    7. <dependency>
    8. <groupId>com.fasterxml.jackson.core</groupId>
    9. <artifactId>jackson-databind</artifactId>
    10. <version>2.9.10</version>
    11. </dependency>
    12. <dependency>
    13. <groupId>com.fasterxml.jackson.core</groupId>
    14. <artifactId>jackson-annotations</artifactId>
    15. <version>2.9.10</version>
    16. </dependency>

    2. ObjectMapper的常用配置

    1. private static final ObjectMapper mapper;
    2. public static ObjectMapper getObjectMapper(){
    3. return this.mapper;
    4. }
    5. static{
    6. //创建ObjectMapper对象
    7. mapper = new ObjectMapper()
    8. //configure方法 配置一些需要的参数
    9. // 转换为格式化的json 显示出来的格式美化
    10. mapper.enable(SerializationFeature.INDENT_OUTPUT);
    11. //序列化的时候序列对象的那些属性
    12. //JsonInclude.Include.NON_DEFAULT 属性为默认值不序列化
    13. //JsonInclude.Include.ALWAYS 所有属性
    14. //JsonInclude.Include.NON_EMPTY 属性为 空(“”) 或者为 NULL 都不序列化
    15. //JsonInclude.Include.NON_NULL 属性为NULL 不序列化
    16. mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
    17. //反序列化时,遇到未知属性会不会报错
    18. //true - 遇到没有的属性就报错 false - 没有的属性不会管,不会报错
    19. mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    20. //如果是空对象的时候,不抛异常
    21. mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    22. // 忽略 transient 修饰的属性
    23. mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
    24. //修改序列化后日期格式
    25. mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    26. mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    27. //处理不同的时区偏移格式
    28. mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    29. mapper.registerModule(new JavaTimeModule());
    30. }

    3.ObjectMapper的常用方法

    3.1 json字符串转对象

    1. ObjectMapper mapper = new ObjectMapper();
    2. String jsonString = "{\"name\":\"Hyl\", \"age\":20}";
    3. //将字符串转换为对象
    4. Student student = mapper.readValue(jsonString, Student.class);
    5. System.out.println(student);
    6. //将对象转换为json字符串
    7. jsonString = mapper.writeValueAsString(student);
    8. System.out.println(jsonString);
    9. 结果:
    10. Student [ name: Hyl, age: 20 ]
    11. {
    12. "name" : "Hyl",
    13. "age" : 20
    14. }

    3.2 数组和对象之间转换

    1. //对象转为byte数组
    2. byte[] byteArr = mapper.writeValueAsBytes(student);
    3. System.out.println(byteArr);
    4. //byte数组转为对象
    5. Student student= mapper.readValue(byteArr, Student.class);
    6. System.out.println(student);
    7. 结果:
    8. [B@3327bd23
    9. Student [ name: Hyl, age: 20 ]

    3.3 集合和json字符串之间转换

    1. List studentList= new ArrayList<>();
    2. studentList.add(new Student("hyl1" ,20 , new Date()));
    3. studentList.add(new Student("hyl2" ,21 , new Date()));
    4. studentList.add(new Student("hyl3" ,22 , new Date()));
    5. studentList.add(new Student("hyl4" ,23 , new Date()));
    6. String jsonStr = mapper.writeValueAsString(studentList);
    7. System.out.println(jsonStr);
    8. List studentList2 = mapper.readValue(jsonStr, List.class);
    9. System.out.println("字符串转集合:" + studentList2 );
    10. 结果:
    11. [ {
    12. "name" : "hyl1",
    13. "age" : 20,
    14. "sendTime" : 1525164212803
    15. }, {
    16. "name" : "hyl2",
    17. "age" : 21,
    18. "sendTime" : 1525164212803
    19. }, {
    20. "name" : "hyl3",
    21. "age" : 22,
    22. "sendTime" : 1525164212803
    23. }, {
    24. "name" : "hyl4",
    25. "age" : 23,
    26. "sendTime" : 1525164212803
    27. } ]
    28. [{name=hyl1, age=20, sendTime=1525164212803}, {name=hyl2, age=21, sendTime=1525164212803}, {name=hyl3, age=22, sendTime=1525164212803}, {name=hyl4, age=23, sendTime=1525164212803}]

    3.4 map和json字符串之间转换

    1. Map testMap = new HashMap<>();
    2. testMap.put("name", "22");
    3. testMap.put("age", 20);
    4. testMap.put("date", new Date());
    5. testMap.put("student", new Student("hyl", 20, new Date()));
    6. String jsonStr = mapper.writeValueAsString(testMap);
    7. System.out.println(jsonStr);
    8. Map testMapDes = mapper.readValue(jsonStr, Map.class);
    9. System.out.println(testMapDes);
    10. 结果:
    11. {
    12. "date" : 1525164212803,
    13. "name" : "22",
    14. "student" : {
    15. "name" : "hyl",
    16. "age" : 20,
    17. "sendTime" : 1525164212803,
    18. "intList" : null
    19. },
    20. "age" : 20
    21. }
    22. {date=1525164212803, name=22, student={name=hyl, age=20, sendTime=1525164212803, intList=null}, age=20}

    3.5 日期转json字符串

    1. // 修改时间格式
    2. mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    3. Student student = new Student ("hyl",21, new Date());
    4. student.setIntList(Arrays.asList(1, 2, 3));
    5. String jsonStr = mapper.writeValueAsString(student);
    6. System.out.println(jsonStr);
    7. 结果:
    8. {
    9. "name" : "hyl",
    10. "age" : 21,
    11. "sendTime" : "2020-07-23 13:14:36",
    12. "intList" : [ 1, 2, 3 ]
    13. }

    3.6 js中将字符串转换为json对象

    1. var data = "{\"name\":\"Hyl\", \"age\":20}";
    2. var student = eval(data);
    3. console.info(student.name);
    4. console.info(student.age);
    5. 结果:
    6. Hyl
    7. 20

    https://blog.csdn.net/technologyleader/article/details/123358279

    二、频繁地创建ObjectMapper实例带来的思考:

    这种做法不仅会降低程序的性能,还可能引发一些难以察觉的问题。因为每次创建ObjectMapper实例时,都需要消耗一定的内存和计算资源。如果频繁创建实例,这些资源的消耗会迅速积累,最终影响程序的性能和稳定性。

    那么,如何高效地使用ObjectMapper呢?答案是尽可能地复用ObjectMapper实例。下面是一些建议:

    1.单例模式

    1. 单例模式:将ObjectMapper实例作为单例对象管理,确保整个应用程序中只有一个实例。这样可以避免重复创建实例,减少资源消耗。可以使用Java的单例模式来实现这一点,例如:
    1. public class ObjectMapperHolder {
    2. private static final ObjectMapper objectMapper = new ObjectMapper();
    3. public static ObjectMapper getObjectMapper() {
    4. return objectMapper;
    5. }
    6. }

    在需要使用ObjectMapper的地方,可以通过 ObjectMapperHolder.getObjectMapper() 来获取实例。

    1. 配置共享:如果应用程序中有多个模块或组件需要使用ObjectMapper,可以考虑将这些模块或组件的ObjectMapper配置统一到一个共享的配置文件中。这样,每个模块或组件都可以使用相同的ObjectMapper实例,避免了重复创建。
    2. 线程安全:由于ObjectMapper实例是复用的,因此需要确保它是线程安全的。Jackson库已经为我们处理了这个问题,ObjectMapper实例本身是线程安全的。但是,如果我们在ObjectMapper上注册了自定义的序列化器或反序列化器,那么这些自定义组件可能需要额外的线程安全措施。

    2.优化建议

    除了避免频繁创建ObjectMapper实例外,还有一些其他的优化建议:

    • 启用缓存:ObjectMapper提供了一些缓存机制,如属性访问器缓存和类型缓存。通过启用这些缓存,可以提高序列化和反序列化的性能。
    • 自定义序列化器和反序列化器:对于特殊的Java类型或复杂的JSON结构,可以编写自定义的序列化器和反序列化器。这不仅可以提高性能,还可以使代码更加清晰和易于维护。
    • 调整日期格式:在序列化日期类型的Java对象时,可以通过设置ObjectMapper的日期格式来避免生成冗长的日期字符串。这可以减小JSON字符串的大小,提高传输和解析的效率。

    总之,高效地使用ObjectMapper可以避免不必要的性能损耗和潜在的问题。通过复用ObjectMapper实例、配置共享、确保线程安全以及采用其他优化措施,我们可以充分发挥ObjectMapper的强大功能,提高Java应用程序的性能和稳定性。

    https://developer.baidu.com/article/details/3233714

    三、附:JSONUtils的部分方法:

    1. import com.fasterxml.jackson.annotation.JsonInclude.Include;
    2. import com.fasterxml.jackson.core.JsonGenerationException;
    3. import com.fasterxml.jackson.core.JsonGenerator;
    4. import com.fasterxml.jackson.core.JsonParseException;
    5. import com.fasterxml.jackson.core.JsonProcessingException;
    6. import com.fasterxml.jackson.core.type.TypeReference;
    7. import com.fasterxml.jackson.databind.*;
    8. import org.apache.commons.lang3.StringUtils;
    9. import org.slf4j.Logger;
    10. import java.io.IOException;
    11. import java.text.SimpleDateFormat;
    12. import java.util.Date;
    13. import java.util.TimeZone;
    14. /**
    15. * Json utils
    16. */
    17. @SuppressWarnings("deprecation")
    18. public class JsonUtils {
    19. private static final ObjectMapper objectMapper;
    20. private static Logger logger = LoggerUtil.getLogger();
    21. static {
    22. objectMapper = new ObjectMapper();
    23. // Remove the default timestamp format
    24. objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    25. // Set to Shanghai time zone in China
    26. objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
    27. objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
    28. // Null value not serialized
    29. objectMapper.setSerializationInclusion(Include.NON_NULL);
    30. // Compatible processing when attributes are not present during deserialization
    31. objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    32. // Uniform format of dates when serializing
    33. objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    34. // It is forbidden to deserialize "Enum" with "int" on behalf of "Enum"
    35. objectMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
    36. objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
    37. // objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY,
    38. // true);
    39. objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    40. // Single quote processing
    41. objectMapper.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
    42. // objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
    43. }
    44. public static ObjectMapper getObjectMapper() {
    45. return objectMapper;
    46. }
    47. public static T toObjectNoException(String json, Class clazz) {
    48. try {
    49. return objectMapper.readValue(json, clazz);
    50. } catch (JsonParseException e) {
    51. logger.error(e.getMessage(), e);
    52. } catch (JsonMappingException e) {
    53. logger.error(e.getMessage(), e);
    54. } catch (IOException e) {
    55. logger.error(e.getMessage(), e);
    56. }
    57. return null;
    58. }
    59. public static String toJsonNoException(T entity) {
    60. try {
    61. return objectMapper.writeValueAsString(entity);
    62. } catch (JsonGenerationException e) {
    63. logger.error(e.getMessage(), e);
    64. } catch (JsonMappingException e) {
    65. logger.error(e.getMessage(), e);
    66. } catch (IOException e) {
    67. logger.error(e.getMessage(), e);
    68. }
    69. return null;
    70. }
    71. public static String toFormatJsonNoException(T entity) {
    72. try {
    73. return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(entity);
    74. } catch (JsonGenerationException e) {
    75. logger.error(e.getMessage(), e);
    76. } catch (JsonMappingException e) {
    77. logger.error(e.getMessage(), e);
    78. } catch (IOException e) {
    79. logger.error(e.getMessage(), e);
    80. }
    81. return null;
    82. }
    83. public static T toCollectionNoException(String json, TypeReference typeReference) {
    84. try {
    85. return objectMapper.readValue(json, typeReference);
    86. } catch (JsonParseException e) {
    87. logger.error(e.getMessage(), e);
    88. } catch (JsonMappingException e) {
    89. logger.error(e.getMessage(), e);
    90. } catch (IOException e) {
    91. logger.error(e.getMessage(), e);
    92. }
    93. return null;
    94. }
    95. public static String toString(Object object) throws JsonProcessingException {
    96. return objectMapper.writeValueAsString(object);
    97. }
    98. public static T toObject(String jsonString, Class rspValueType)
    99. throws JsonParseException, JsonMappingException, IOException {
    100. return objectMapper.readValue(jsonString, rspValueType);
    101. }
    102. public static JsonNode readJsonNode(String jsonStr, String fieldName) {
    103. if (StringUtils.isEmpty(jsonStr)) {
    104. return null;
    105. }
    106. try {
    107. JsonNode root = objectMapper.readTree(jsonStr);
    108. return root.get(fieldName);
    109. } catch (IOException e) {
    110. logger.error("parse json string error:" + jsonStr, e);
    111. return null;
    112. }
    113. }
    114. @SuppressWarnings("unchecked")
    115. public static T readJson(JsonNode node, Class parametrized, Class... parameterClasses) throws Exception {
    116. JavaType javaType = objectMapper.getTypeFactory().constructParametricType(parametrized, parameterClasses);
    117. return (T) objectMapper.readValue(toString(node), javaType);
    118. }
    119. public class CustomDateSerializer extends JsonSerializer {
    120. @Override
    121. public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider)
    122. throws IOException, JsonProcessingException {
    123. SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
    124. String formattedDate = formatter.format(value);
    125. jgen.writeString(formattedDate);
    126. }
    127. }
    128. }
  • 相关阅读:
    yum安装jdk环境
    11.0 堆参数调优入门之堆参数调整
    从零开始:新手快速在国产操作系统中搭建高可用K8S(V1.28)集群落地实践
    山东大学项目实训十六——可控音乐变压器Controllable Music Transformer
    TCP粘包问题解决方案
    TCP/IP之IP地址分类
    C语言中的typedef struct用法
    类加载器ClassLoader
    奥迪AUDI EDI INVOIC发票报文详解
    3.流的输入/输出
  • 原文地址:https://blog.csdn.net/Alex_81D/article/details/139961091