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的。
有機會往後會進行補充的。學習

若是你們對這個系列有什麼意見或者期待,能夠給我留言,謝謝。調試

相關文章
相關標籤/搜索