JsonUtil(基于Jackson的实现)

前言:

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

其中有四个原因:

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

场景:

  1. 由于业务的需要(如session集中保存),我们需要将某个对象(如用户信息)保存到Redis中,而Redis无法保存对象。所以我们需要将对象进行序列化操作,从而将对象保存起来,并在日后提取出来时,进行反序列化。
  2. 由于业务的需求(如消息队列的消息),我们需要将一组对象(如订单信息)发送到消息队列,而消息队列是无法发送对象的(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;
}
}
}

依赖:

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

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

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

总结:

Json序列化的Util当然不止这一种。还有很多方式,乃至不是基于Jackson的,如基于Gson的。

有机会日后会进行补充的。

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

JsonUtil(基于Jackson的实现)的更多相关文章

  1. 基于Flume+LOG4J+Kafka的日志采集架构方案

    本文将会介绍如何使用 Flume.log4j.Kafka进行规范的日志采集. Flume 基本概念 Flume是一个完善.强大的日志采集工具,关于它的配置,在网上有很多现成的例子和资料,这里仅做简单说 ...

  2. Json工具类JsonUtil

    import com.alibaba.fastjson.JSONArray; import com.fasterxml.jackson.core.JsonProcessingException; im ...

  3. JackSon解析json字符串

    JackSon解析json字符串 原文:http://blog.csdn.net/java_huashan/article/details/9353903 概述 jackson解析json例子 准备工 ...

  4. Android 中Json解析的几种框架(Gson、Jackson、FastJson、LoganSquare)使用与对比

    介绍 移动互联网产品与服务器端通信的数据格式,如果没有特殊的需求的话,一般选择使用JSON格式,Android系统也原生的提供了JSON解析的API,但是它的速度很慢,而且没有提供简介方便的接口来提高 ...

  5. 关于fastjson与jackson在反序列化bool型时的区别

    背景 在测试中,两个项目a,b的接口参数用到了Bool类型,当传参"0",项目a通过了,项目b报错了,排查了下,项目b的那个接口,在对传参反序列化时就出现了问题,最后发现两个项目使 ...

  6. 10 分钟轻松学会 Jackson 反序列化自动适配子类

    作者:丁仪 来源:https://chengxuzhixin.com/blog/post/Jackson-fan-xu-lie-hua-zi-dong-shi-pei-zi-lei.html json ...

  7. java:sso(单点登录(single sign on),jsp文件动静态导入方式,session跨域)

    1.jsp文件导入: 2.session跨域: 3.sso(单点登录(single sign on): sso Maven Webapp: LoginController.java: package ...

  8. java:solr

    1.solr(数据导入solr自带数据库):   ImportItemController.java: package com.solr.controller; import org.springfr ...

  9. java:redis(java代码操作redis,实体类mapper生成器(generator))

    1.redis_demo Maven  ItemMapper.xml: <?xml version="1.0" encoding="UTF-8" ?> ...

随机推荐

  1. 运行control userpasswords2实现winXP自动登录

    原文:运行control userpasswords2实现winXP自动登录 如果你的计算机只是自己一人在用,且每次都用同一个用户名(或者你根本没在意过什么是用户名),而每次都要输入密码,是否太麻烦了 ...

  2. 使用ServiceStack.Redis实现Redis数据读写

    原文:使用ServiceStack.Redis实现Redis数据读写 User.cs实体类 public class User { public string Name { get; set; } p ...

  3. 【Linux】PuTTY----------windows访问Linux 快捷方便

    第一步:百度PuTTY,下载好后直接运行,界面如下: 第二步:后输入IP:10.45.XX.XX,直接点击open按钮 第三步:输入用户名: 第四步:密码~ 现在,您就可以对你访问的linux设备进行 ...

  4. 微信小程序把玩(三十五)Video API

    原文:微信小程序把玩(三十五)Video API 电脑端不能测试拍摄功能只能测试选择视频功能,好像只支持mp4格式,值得注意的是成功之后返回的临时文件路径是个列表tempFilePaths而不是tem ...

  5. WinForm子线程调用主线程

    public Form1() { InitializeComponent(); Thread t = new Thread(ThreadWorker); t.Start(); } private vo ...

  6. Android零基础入门第87节:Fragment添加、删除、替换

    前面一起学习了Fragment的创建和加载,以及其生命周期方法,那么接下来进一步来学习Fragment的具体使用,本期先来学习Fragment添加.删除.替换. 一.概述 在前面的学习中,特别是动态加 ...

  7. 30+ 强大的Buddypress主题–开始您的社区站点吧

    BuddyPress起源于2008年,当时设计者设想添加社交网络功能到WordPress多用户版本中.第一个正式稳定版本的发布是在2009年的5月.自从那时起.BuddyPress开始快速的成长和演变 ...

  8. java高级知识

    1. Spring读取配置文件的流程,怎样引入注解? 2. Spring是怎么注入新的java文件的? 3. CAS锁是什么实现机制,在java的哪些包里最常使用?什么情况下会使用CAS锁? 4. 线 ...

  9. c++ LeetCode (初级字符串篇) 九道算法例题代码详解(二)

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11089327.html 已经刷了很多篇leetcode题了,不过最近在找c++的实习工作(大佬 ...

  10. 深入浅出Ajax

    原文(我的GitHub):https://github.com/liangfengbo/frontend-ability/issues/1 学习大纲 理解Ajax的工作原理 Ajax核心-XMLHtt ...