JsonUtil(基于Jackson的实现)

简介: JsonUtil(基于Jackson的实现)前言:其实,我一直想写一个有关Util的系列。其中有四个原因:Util包作为项目的重要组成,是几乎每个项目不可或缺的一部分。并且Util包的Util往往具有足够的通用性,可用于不同的项目。

JsonUtil(基于Jackson的实现)
前言:
其实,我一直想写一个有关Util的系列。

其中有四个原因:

Util包作为项目的重要组成,是几乎每个项目不可或缺的一部分。并且Util包的Util往往具有足够的通用性,可用于不同的项目。
Util包中的代码封装往往非常有意思,对他们的学习,也有助于自身代码水平与认知的提高。
目前网上对Util包的总结很少,或者说很零散,没有做成一个系列的。我希望能做成一个系列,以后缺什么Util都可以直接通过这个系列找到需要的Util。
借此机会,可以更好地与外界进行技术的交流,获得更多的指导。
场景:
由于业务的需要(如session集中保存),我们需要将某个对象(如用户信息)保存到Redis中,而Redis无法保存对象。所以我们需要将对象进行序列化操作,从而将对象保存起来,并在日后提取出来时,进行反序列化。
由于业务的需求(如消息队列的消息),我们需要将一组对象(如订单信息)发送到消息队列,而消息队列是无法发送对象的(RabbitMQ后面是支持的,另外,序列化的消息,便于后台查看)。所以我们需要将一组对象进行序列化操作,从而将对象保存起来,并在日后提取出来时,进行反序列化。
作用:
JsonUtil就是用来进行单个或复数个对象的序列化与反序列化操作。

代码:

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;
        }
    }
}

依赖:
commons-lang3
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的过程中,遇到过一个问题,就是日期序列化,反序列化,出现问题。

不过,经过一次次调试与追踪后,发现只要修改了日期格式就可以避免这个问题(其实当时真的没有想到Util会出现这种问题,所以花了不少时间)。

具体原因,问了一圈,也没有得到答案。看来只能留待日后了。

总结:
Json序列化的Util当然不止这一种。还有很多方式,乃至不是基于Jackson的,如基于Gson的。
有机会日后会进行补充的。

如果大家对这个系列有什么意见或者期待,可以给我留言,谢谢。

相关文章
|
JSON 算法 安全
不破不立!Fastjson2.0 性能炸裂,为了下一个十年
Alibaba Fastjson: 目前在人类已知范围内,这个星球跑的最快的Java JSON库。在过去的十年里,fastjson v1作为国内github star最多和最受欢迎的json解析库,如今fastjson v2 重磅来袭,性能炸裂。
18898 2
不破不立!Fastjson2.0 性能炸裂,为了下一个十年
|
JSON Java 测试技术
为了理直气壮怼回去,写了一个日志切面输出接口出入参数
我们在日常排查问题过程中知道,入参传错是导致接口调用失败的常见原因之一。特别是提供给第三方调用的**回调接口和openAPI接口**,由于无法保证第三方开发人员的水平,经常问题不断,反反复复找你问为啥掉不通,甚至吐槽写的“啥玩意接口”,这时候你肯定一脸懵逼,怒火中烧,想展开撕逼甩锅大战,但是对方有可能是甲方金主爸爸并且你没有第一时间掌握证据证明证是对方调用的问题,你只能忍着问他是如何调接口的,卑微请求他把传参发过来看看。。。为了扭转局势,挺直腰杆怼回去:能不能靠谱点?今天我们就来讲讲系统服务中如何优雅地实现统一打印接口API参数日志,方便服务端开发快速甩锅还能拿出证据!!!
416 0
为了理直气壮怼回去,写了一个日志切面输出接口出入参数
|
监控 druid Java
Spring Boot 3 集成 Druid 连接池详解
在现代的Java应用中,使用一个高效可靠的数据源是至关重要的。Druid连接池作为一款强大的数据库连接池,提供了丰富的监控和管理功能,成为很多Java项目的首选。本文将详细介绍如何在Spring Boot 3项目中配置数据源,集成Druid连接池,以实现更高效的数据库连接管理。
10437 2
Spring Boot 3 集成 Druid 连接池详解
|
设计模式 JSON 架构师
你真的需要防腐层吗?DDD 系统间的7种关系梳理与实践
当提到系统间交互的时候,人们都会想到大名鼎鼎的防腐层,用来防止其他系统的模型变更对本系统造成影响。但是在实践这个模式的过程中,我们常常会遇到问题。此时我们也应该考虑下其他的系统交互方式。
27857 12
你真的需要防腐层吗?DDD 系统间的7种关系梳理与实践
|
Java 程序员 编译器
Java|如何正确地在遍历 List 时删除元素
从源码分析如何正确地在遍历 List 时删除元素。为什么有的写法会导致异常,而另一些不会。
364 3
|
消息中间件 JSON Java
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
Spring Boot、Spring Cloud与Spring Cloud Alibaba版本对应关系
28246 0
|
前端开发 Java
Java HotSpot(TM) 64-Bit Server VM warning
Java HotSpot(TM) 64-Bit Server VM warning
4974 1
|
Java Maven
intellij idea如何查看项目maven依赖关系图
这篇文章介绍了如何在IntelliJ IDEA中查看项目的Maven依赖关系图,包括使用Maven工具栏和相关操作来展示和查看依赖细节。
|
Go 开发者
golang的http客户端封装
golang的http客户端封装
424 0
|
数据采集 设计模式 自然语言处理
设计模式最佳套路2 —— 愉快地使用管道模式
管道模式(Pipeline Pattern) 是责任链模式(Chain of Responsibility Pattern)的常用变体之一。在管道模式中,管道扮演着流水线的角色,将数据传递到一个加工处理序列中,数据在每个步骤中被加工处理后,传递到下一个步骤进行加工处理,直到全部步骤处理完毕。 PS:纯的责任链模式在链上只会有一个处理器用于处理数据,而管道模式上多个处理器都会处理数据。
13147 0
设计模式最佳套路2 —— 愉快地使用管道模式

热门文章

最新文章