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
<https://search.maven.org/artifact/org.apache.commons/commons-lang3/3.9/jar>
* jackson
<https://search.maven.org/artifact/org.codehaus.jackson/jackson-mapper-asl/1.9.13/jar>
(该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的。
有机会日后会进行补充的。

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

友情链接
ioDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信