JsonUtil(基于Jackson的实现)

JsonUtil(基于Jackson的实现)

前言:

其实,我一直想写一个有关Util的系列。java

其中有四个缘由:apache

  1. Util包做为项目的重要组成,是几乎每一个项目不可或缺的一部分。而且Util包的Util每每具备足够的通用性,可用于不一样的项目。
  2. Util包中的代码封装每每很是有意思,对他们的学习,也有助于自身代码水平与认知的提升。
  3. 目前网上对Util包的总结不多,或者说很零散,没有作成一个系列的。我但愿能作成一个系列,之后缺什么Util均可以直接经过这个系列找到须要的Util。
  4. 借此机会,能够更好地与外界进行技术的交流,得到更多的指导。

场景:

  1. 因为业务的须要(如session集中保存),咱们须要将某个对象(如用户信息)保存到Redis中,而Redis没法保存对象。因此咱们须要将对象进行序列化操做,从而将对象保存起来,并在往后提取出来时,进行反序列化。
  2. 因为业务的需求(如消息队列的消息),咱们须要将一组对象(如订单信息)发送到消息队列,而消息队列是没法发送对象的(RabbitMQ后面是支持的,另外,序列化的消息,便于后台查看)。因此咱们须要将一组对象进行序列化操做,从而将对象保存起来,并在往后提取出来时,进行反序列化。

做用:

JsonUtil就是用来进行单个或复数个对象的序列化与反序列化操做。json

代码:

package top.jarry.learning.util;
    
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.codehaus.jackson.map.DeserializationConfig;
    import org.codehaus.jackson.map.ObjectMapper;
    import org.codehaus.jackson.map.SerializationConfig;
    import org.codehaus.jackson.map.annotate.JsonSerialize;
    import org.codehaus.jackson.type.JavaType;
    import org.codehaus.jackson.type.TypeReference;
    
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    
    /**
     * @Description:
     * @Author: jarry
     */
    @Slf4j
    public class JsonUtil {
    
        // 创建Jackson的ObjectMapper对象
        private static ObjectMapper objectMapper = new ObjectMapper();
    
        // 创建Json操做中的日期格式
        private static final String JSON_STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
        // DateTimeUtil.STANDARD_FORMAT = "yyyy-mm-dd HH:mm:ss";   
                // 日期格式若是设置为这个,会出现月份出错的问题(先是5月变3月,而后就不断增长,甚至超过12月),具体缘由待查
    
        static {
    
            //对象的全部字段所有列入
            objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.ALWAYS);
    
            //取消默认转换timestamps形式
            objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
    
            //忽略空Bean转json的错误
            objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
    
            //全部的日期格式都统一为如下的样式
            objectMapper.setDateFormat(new SimpleDateFormat(JSON_STANDARD_FORMAT));
    
            //反序列化
            //忽略 在json字符串中存在,可是在java对象中不存在对应属性的状况。防止错误
            objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        }
    
        /**
         * 完成对象序列化为字符串
         * @param obj 源对象
         * @param <T>
         * @return
         */
        public static <T> String obj2String(T obj) {
            if (obj == null) {
                return null;
            }
            try {
                return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);
            } catch (Exception e) {
                log.warn("Parse Object to String error", e);
                return null;
            }
        }
    
        /**
         * 完成对象序列化为字符串,可是字符串会保证必定的结构性(提升可读性,增长字符串大小)
         * @param obj 源对象
         * @param <T>
         * @return
         */
        public static <T> String obj2StringPretty(T obj) {
            if (obj == null) {
                return null;
            }
            try {
                return obj instanceof String ? (String) obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
            } catch (Exception e) {
                log.warn("Parse Object to String error", e);
                return null;
            }
        }
    
        /**
         * 完成字符串反序列化为对象
         * @param str 源字符串
         * @param clazz 目标对象的Class
         * @param <T>
         * @return
         */
        public static <T> T string2Obj(String str, Class<T> clazz) {
            if (StringUtils.isEmpty(str) || clazz == null) {
                return null;
            }
            try {
                return (clazz == String.class) ? (T) str : objectMapper.readValue(str, clazz);
            } catch (IOException e) {
                log.warn("Parse String to Object error", e);
                return null;
            }
        }
    
        //jackson在反序列化时,若是传入List,会自动反序列化为LinkedHashMap的List
        //因此重载一下方法,解决以前String2Obj没法解决的问题
    
        /**
         * 进行复杂类型反序列化工做 (自定义类型的集合类型)
         *
         * @param str 源字符串
         * @param typeReference 包含elementType与CollectionType的typeReference
         * @param <T>
         * @return
         */
        public static <T> T string2Obj(String str, TypeReference<T> typeReference) {
            if (StringUtils.isEmpty(str) || typeReference == null) {
                return null;
            }
            try {
                return (T) ((typeReference.getType().equals(String.class)) ? str : objectMapper.readValue(str, typeReference.getClass()));
            } catch (IOException e) {
                log.warn("Parse String to Object error", e);
                return null;
            }
        }
    
        /**
         * 进行复杂类型反序列化工做(可变类型数量的)
         *
         * @param str             须要进行反序列化的字符串
         * @param collectionClass 须要反序列化的集合类型 因为这里的类型未定,且为了防止与返回值类型T冲突,故采用<?>表示泛型
         * @param elementClasses  集合中的元素类型(可多个)   此处同上经过<?>...表示多个未知泛型
         * @param <T>             返回值的泛型类型是由javatype获取的
         * @return
         */
        public static <T> T string2Obj(String str, Class<?> collectionClass, Class<?>... elementClasses) {
            JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
            try {
                return objectMapper.readValue(str, javaType);
            } catch (IOException e) {
                log.warn("Parse String to Object error", e);
                return null;
            }
        }
    }

依赖:

  1. commons-lang3
  2. jackson(该jar包可能有点老,能够考虑更新,不过能够正常使用)

应用:

public void onInitializationInclinationMessage(String initializationInclinationStr,
                                                       @Headers Map<String, Object> headers, Channel channel) throws IOException {
    
            log.info("InitializationInclinationConsumer/onInitializationInclinationMessage has received: {}", initializationInclinationStr);
    
            // 1.接收数据,并反序列化出对象
            InitializationInclination initializationInclination = JsonUtil.string2Obj(initializationInclinationStr, InitializationInclination.class);
    
            // 2.数据校验,判断是否属于该终端数据
            if (initializationInclination == null) {
                Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
                channel.basicAck(deliveryTag, false);
            }
    
            if (!GuavaCache.getKey(TERMINAL_ID).equals(initializationInclination.getTerminalId())) {
                log.info("refuse target initializationInclination with terminalId({}).current_terminalId({})", initializationInclination.getTerminalId(), GuavaCache.getKey(TERMINAL_ID));
                return;
            }
    
            // 3.将消息传入业务服务,进行消费
            ServerResponse response = iInitializationInclinationService.receiveInitializationInclinationFromMQ(initializationInclination);
    
            // 4.对成功消费的数据进行签收
            if (response.isSuccess()) {
                //因为配置中写的是手动签收,因此这里须要经过Headers来进行签收
                Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
                channel.basicAck(deliveryTag, false);
            }
public void onInclinationTotalMessage(String inclinationTotalListStr, @Headers Map<String, Object> headers, Channel channel) throws Exception {
    
            log.info("InclinationConsumer/onInclinationTotalMessage has received: {}", inclinationTotalListStr);
    
            // 1.对消息进行反序列化操做
            List<InclinationTotal> inclinationTotalList = JsonUtil.string2Obj(inclinationTotalListStr, List.class, InclinationTotal.class);
    
            // 2.对数据进行校验
            if (CollectionUtils.isEmpty(inclinationTotalList)){
                //因为配置中写的是手动签收,因此这里须要经过Headers来进行签收
                Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
                channel.basicAck(deliveryTag, false);
            }
    
            // 3.将消息传入业务服务,进行消费
            ServerResponse response = iInclinationService.insertTotalDataByList(inclinationTotalList);
    
            // 4.对成功消费的数据进行签收
            if (response.isSuccess()) {
                //因为配置中写的是手动签收,因此这里须要经过Headers来进行签收
                Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);
                channel.basicAck(deliveryTag, false);
            }
        }

问题:

在使用这个JsonUtil的过程当中,遇到过一个问题,就是日期序列化,反序列化,出现问题。session

不过,通过一次次调试与追踪后,发现只要修改了日期格式就能够避免这个问题(其实当时真的没有想到Util会出现这种问题,因此花了很多时间)。app

具体缘由,问了一圈,也没有获得答案。看来只能留待往后了。maven

总结:

Json序列化的Util固然不止这一种。还有不少方式,乃至不是基于Jackson的,如基于Gson的。
有机会往后会进行补充的。学习

若是你们对这个系列有什么意见或者期待,能够给我留言,谢谢。调试

相关文章
相关标签/搜索