JsonUtil(基于Jackson的实现)
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的。
有机会日后会进行补充的。
如果大家对这个系列有什么意见或者期待,可以给我留言,谢谢。
JsonUtil(基于Jackson的实现)的更多相关文章
- 基于Flume+LOG4J+Kafka的日志采集架构方案
		
本文将会介绍如何使用 Flume.log4j.Kafka进行规范的日志采集. Flume 基本概念 Flume是一个完善.强大的日志采集工具,关于它的配置,在网上有很多现成的例子和资料,这里仅做简单说 ...
 - Json工具类JsonUtil
		
import com.alibaba.fastjson.JSONArray; import com.fasterxml.jackson.core.JsonProcessingException; im ...
 - JackSon解析json字符串
		
JackSon解析json字符串 原文:http://blog.csdn.net/java_huashan/article/details/9353903 概述 jackson解析json例子 准备工 ...
 - Android 中Json解析的几种框架(Gson、Jackson、FastJson、LoganSquare)使用与对比
		
介绍 移动互联网产品与服务器端通信的数据格式,如果没有特殊的需求的话,一般选择使用JSON格式,Android系统也原生的提供了JSON解析的API,但是它的速度很慢,而且没有提供简介方便的接口来提高 ...
 - 关于fastjson与jackson在反序列化bool型时的区别
		
背景 在测试中,两个项目a,b的接口参数用到了Bool类型,当传参"0",项目a通过了,项目b报错了,排查了下,项目b的那个接口,在对传参反序列化时就出现了问题,最后发现两个项目使 ...
 - 10 分钟轻松学会 Jackson 反序列化自动适配子类
		
作者:丁仪 来源:https://chengxuzhixin.com/blog/post/Jackson-fan-xu-lie-hua-zi-dong-shi-pei-zi-lei.html json ...
 - java:sso(单点登录(single sign on),jsp文件动静态导入方式,session跨域)
		
1.jsp文件导入: 2.session跨域: 3.sso(单点登录(single sign on): sso Maven Webapp: LoginController.java: package ...
 - java:solr
		
1.solr(数据导入solr自带数据库): ImportItemController.java: package com.solr.controller; import org.springfr ...
 - java:redis(java代码操作redis,实体类mapper生成器(generator))
		
1.redis_demo Maven ItemMapper.xml: <?xml version="1.0" encoding="UTF-8" ?> ...
 
随机推荐
- VS2012中使用CEGUI项目发布到XP平台的问题(核心方法就一句话。“你项目使用的所有外部依赖库都用/MT编译。”)
			
接着上一篇文章,详细说说如何把一个带CEGUI的项目发布到XP平台. 这个问题纠缠了我好几天.这里把详细解决思路记下来.有同样问题的朋友可以少走很多弯路. 核心方法就一句话.“你项目使用的所有外部依赖 ...
 - delphi文件操作(比较全)
			
Delphi中默认有input和output两个文件变量,使用可以不用定义,直接使用. 但: input:只读.output:只写.用时注意以免引起异常. 文件是由文件名标识的一组数据的集合,文件通常 ...
 - Android 命令设置获取、IP地址、网关、dns
			
设置ip root@android:/ # ifconfig eth0 192.168.0.173 netmask 255.255.255.0 ifconfig eth0 192.168.0.173 ...
 - Android零基础入门第6节:配置优化SDK Manager,正式约会女神
			
原文:Android零基础入门第6节:配置优化SDK Manager,正式约会女神 在前几期中总结分享了Android的前世今生.Android 系统架构和应用组件那些事.带你一起来聊一聊Androi ...
 - 事件循环和线程没有必然关系(就像Windows子线程默认没有消息循环一样),模态对话框和事件循环也没有必然关系(QWidget直接就可以)
			
周末天冷,索性把电脑抱到床上上网,这几天看了 dbzhang800 博客关于 Qt 事件循环的几篇 Blog,发现自己对 Qt 的事件循环有不少误解.从来只看到现象,这次借 dbzhang800 的博 ...
 - HOLLOW_BRUSH等价于NULL_BRUSH,都代表透明化刷
			
NULL_BRUSH 或HOLLOW_BRUSH和GetStockObject函数 备注:HOLLOW_BRUSH等价于NULL_BRUSH,都代表透明化刷 HGDIOBJ GetStockObjec ...
 - wchar_t string on Linux, OS X and Windows
			
Making wchar_t work on Linux, OS X and Windows for CMarkup release 10.1 I learned a couple of humble ...
 - 冒泡排序C#实现,使用委托,包括三种方式:Fun<>,匿名方法,Lambda表达式
			
冒泡排序是一种简单的排序方法,适合于小量数字排序,对于大量数字(超过10个),还有更高效的排序方法. 这里的实现的冒泡排序,需实现功能: 不仅数字排序,还要对任意对象排序 示例: 对People对象的 ...
 - ASP.NET 5 牛刀小試(二):加入第三方 DI 容器
			
上回介绍了 ASP.NET vNext 自带容器的基本用法,这次要试试把 ASP.NET vNext 的自带容器换成 Autofac. 这一次,在编写范例程序的过程中,光是解决 KRE 与相关套件的版 ...
 - 【Linux】Linux 环境下splite以及一些正则使用
			
由于在windows下,遍历目录,想查找满足条件的文件: dir /s > ..\fileresult.txt 结果得到的文件过大,999多MB的txt: split -b 10k date.f ...