Java ObjectMapper的使用和使用过程中遇到的问题
作者:Alex_81D
背景:
在Java开发中,ObjectMapper是Jackson库的核心类,用于将Java对象序列化为JSON字符串,或者将JSON字符串反序列化为Java对象。由于其功能强大且易于使用,ObjectMapper成为了处理JSON数据的常用工具,它可以帮助我们快速的进行各个类型和Json类型的相互转换。然而,在实际开发中,很多开发者可能会犯一个常见的错误:频繁地创建ObjectMapper实例。
先说一下我们代码使用中发现的一些习惯案例:
一、ObjectMapper的使用
1.引入Jackson的依赖
<!-- 根据自己需要引入相关版本依赖。 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.10</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.10</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.10</version> </dependency>
2. ObjectMapper的常用配置
private static final ObjectMapper mapper; public static ObjectMapper getObjectMapper(){ return this.mapper; } static{ //创建ObjectMapper对象 mapper = new ObjectMapper() //configure方法 配置一些需要的参数 // 转换为格式化的json 显示出来的格式美化 mapper.enable(SerializationFeature.INDENT_OUTPUT); //序列化的时候序列对象的那些属性 //JsonInclude.Include.NON_DEFAULT 属性为默认值不序列化 //JsonInclude.Include.ALWAYS 所有属性 //JsonInclude.Include.NON_EMPTY 属性为 空(“”) 或者为 NULL 都不序列化 //JsonInclude.Include.NON_NULL 属性为NULL 不序列化 mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); //反序列化时,遇到未知属性会不会报错 //true - 遇到没有的属性就报错 false - 没有的属性不会管,不会报错 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); //如果是空对象的时候,不抛异常 mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); // 忽略 transient 修饰的属性 mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true); //修改序列化后日期格式 mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); //处理不同的时区偏移格式 mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); mapper.registerModule(new JavaTimeModule()); }
3.ObjectMapper的常用方法
3.1 json字符串转对象
ObjectMapper mapper = new ObjectMapper(); String jsonString = "{\"name\":\"Hyl\", \"age\":20}"; //将字符串转换为对象 Student student = mapper.readValue(jsonString, Student.class); System.out.println(student); //将对象转换为json字符串 jsonString = mapper.writeValueAsString(student); System.out.println(jsonString); 结果: Student [ name: Hyl, age: 20 ] { "name" : "Hyl", "age" : 20 }
3.2 数组和对象之间转换
//对象转为byte数组 byte[] byteArr = mapper.writeValueAsBytes(student); System.out.println(byteArr); //byte数组转为对象 Student student= mapper.readValue(byteArr, Student.class); System.out.println(student); 结果: [B@3327bd23 Student [ name: Hyl, age: 20 ]
3.3 集合和json字符串之间转换
List<Student> studentList= new ArrayList<>(); studentList.add(new Student("hyl1" ,20 , new Date())); studentList.add(new Student("hyl2" ,21 , new Date())); studentList.add(new Student("hyl3" ,22 , new Date())); studentList.add(new Student("hyl4" ,23 , new Date())); String jsonStr = mapper.writeValueAsString(studentList); System.out.println(jsonStr); List<Student> studentList2 = mapper.readValue(jsonStr, List.class); System.out.println("字符串转集合:" + studentList2 ); 结果: [ { "name" : "hyl1", "age" : 20, "sendTime" : 1525164212803 }, { "name" : "hyl2", "age" : 21, "sendTime" : 1525164212803 }, { "name" : "hyl3", "age" : 22, "sendTime" : 1525164212803 }, { "name" : "hyl4", "age" : 23, "sendTime" : 1525164212803 } ] [{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字符串之间转换
Map<String, Object> testMap = new HashMap<>(); testMap.put("name", "22"); testMap.put("age", 20); testMap.put("date", new Date()); testMap.put("student", new Student("hyl", 20, new Date())); String jsonStr = mapper.writeValueAsString(testMap); System.out.println(jsonStr); Map<String, Object> testMapDes = mapper.readValue(jsonStr, Map.class); System.out.println(testMapDes); 结果: { "date" : 1525164212803, "name" : "22", "student" : { "name" : "hyl", "age" : 20, "sendTime" : 1525164212803, "intList" : null }, "age" : 20 } {date=1525164212803, name=22, student={name=hyl, age=20, sendTime=1525164212803, intList=null}, age=20}
3.5 日期转json字符串
// 修改时间格式 mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); Student student = new Student ("hyl",21, new Date()); student.setIntList(Arrays.asList(1, 2, 3)); String jsonStr = mapper.writeValueAsString(student); System.out.println(jsonStr); 结果: { "name" : "hyl", "age" : 21, "sendTime" : "2020-07-23 13:14:36", "intList" : [ 1, 2, 3 ] }
3.6 js中将字符串转换为json对象
var data = "{\"name\":\"Hyl\", \"age\":20}"; var student = eval(data); console.info(student.name); console.info(student.age); 结果: Hyl 20
https://www.jb51.net/program/32374191h.htm
二、频繁地创建ObjectMapper实例带来的思考:
这种做法不仅会降低程序的性能,还可能引发一些难以察觉的问题。因为每次创建ObjectMapper实例时,都需要消耗一定的内存和计算资源。如果频繁创建实例,这些资源的消耗会迅速积累,最终影响程序的性能和稳定性。
那么,如何高效地使用ObjectMapper呢?答案是尽可能地复用ObjectMapper实例。下面是一些建议:
1.单例模式 单例模式:将ObjectMapper实例作为单例对象管理,确保整个应用程序中只有一个实例。这样可以避免重复创建实例,减少资源消耗。可以使用Java的单例模式来实现这一点,例如:
public class ObjectMapperHolder { private static final ObjectMapper objectMapper = new ObjectMapper(); public static ObjectMapper getObjectMapper() { return objectMapper; } }
在需要使用ObjectMapper的地方,可以通过 ObjectMapperHolder.getObjectMapper()
来获取实例。
- 配置共享:如果应用程序中有多个模块或组件需要使用ObjectMapper,可以考虑将这些模块或组件的ObjectMapper配置统一到一个共享的配置文件中。这样,每个模块或组件都可以使用相同的ObjectMapper实例,避免了重复创建。
- 线程安全:由于ObjectMapper实例是复用的,因此需要确保它是线程安全的。Jackson库已经为我们处理了这个问题,ObjectMapper实例本身是线程安全的。但是,如果我们在ObjectMapper上注册了自定义的序列化器或反序列化器,那么这些自定义组件可能需要额外的线程安全措施。
2.优化建议
除了避免频繁创建ObjectMapper实例外,还有一些其他的优化建议:
- 启用缓存:ObjectMapper提供了一些缓存机制,如属性访问器缓存和类型缓存。通过启用这些缓存,可以提高序列化和反序列化的性能。
- 自定义序列化器和反序列化器:对于特殊的Java类型或复杂的JSON结构,可以编写自定义的序列化器和反序列化器。这不仅可以提高性能,还可以使代码更加清晰和易于维护。
- 调整日期格式:在序列化日期类型的Java对象时,可以通过设置ObjectMapper的日期格式来避免生成冗长的日期字符串。这可以减小JSON字符串的大小,提高传输和解析的效率。
总之,高效地使用ObjectMapper可以避免不必要的性能损耗和潜在的问题。通过复用ObjectMapper实例、配置共享、确保线程安全以及采用其他优化措施,我们可以充分发挥ObjectMapper的强大功能,提高Java应用程序的性能和稳定性。
https://developer.baidu.com/article/details/3233714
三、附:JSONUtils的部分方法:
import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.*; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; /** * Json utils */ @SuppressWarnings("deprecation") public class JsonUtils { private static final ObjectMapper objectMapper; private static Logger logger = LoggerUtil.getLogger(); static { objectMapper = new ObjectMapper(); // Remove the default timestamp format objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); // Set to Shanghai time zone in China objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8")); objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false); // Null value not serialized objectMapper.setSerializationInclusion(Include.NON_NULL); // Compatible processing when attributes are not present during deserialization objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // Uniform format of dates when serializing objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); // It is forbidden to deserialize "Enum" with "int" on behalf of "Enum" objectMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true); objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); // objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, // true); objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); // Single quote processing objectMapper.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); // objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); } public static ObjectMapper getObjectMapper() { return objectMapper; } public static <T> T toObjectNoException(String json, Class<T> clazz) { try { return objectMapper.readValue(json, clazz); } catch (JsonParseException e) { logger.error(e.getMessage(), e); } catch (JsonMappingException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } return null; } public static <T> String toJsonNoException(T entity) { try { return objectMapper.writeValueAsString(entity); } catch (JsonGenerationException e) { logger.error(e.getMessage(), e); } catch (JsonMappingException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } return null; } public static <T> String toFormatJsonNoException(T entity) { try { return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(entity); } catch (JsonGenerationException e) { logger.error(e.getMessage(), e); } catch (JsonMappingException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } return null; } public static <T> T toCollectionNoException(String json, TypeReference<T> typeReference) { try { return objectMapper.readValue(json, typeReference); } catch (JsonParseException e) { logger.error(e.getMessage(), e); } catch (JsonMappingException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } return null; } public static String toString(Object object) throws JsonProcessingException { return objectMapper.writeValueAsString(object); } public static <T> T toObject(String jsonString, Class<T> rspValueType) throws JsonParseException, JsonMappingException, IOException { return objectMapper.readValue(jsonString, rspValueType); } public static JsonNode readJsonNode(String jsonStr, String fieldName) { if (StringUtils.isEmpty(jsonStr)) { return null; } try { JsonNode root = objectMapper.readTree(jsonStr); return root.get(fieldName); } catch (IOException e) { logger.error("parse json string error:" + jsonStr, e); return null; } } @SuppressWarnings("unchecked") public static <T> T readJson(JsonNode node, Class<?> parametrized, Class<?>... parameterClasses) throws Exception { JavaType javaType = objectMapper.getTypeFactory().constructParametricType(parametrized, parameterClasses); return (T) objectMapper.readValue(toString(node), javaType); } public class CustomDateSerializer extends JsonSerializer<Date> { @Override public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); String formattedDate = formatter.format(value); jgen.writeString(formattedDate); } } }
到此这篇关于Java ObjectMapper的使用和使用过程中遇到的问题的文章就介绍到这了,更多相关Java ObjectMapper使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!