使用低版本Jackson 2的类级@JsonInclude包含策略的bug

本文出处:http://blog.csdn.net/chaijunkun/article/details/45110623,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。

Jackson是Java语言中非常好用的对象与JSON相互转换的工具。然而本人的一次使用过程中发现了其老版本在某些情况下没有按照我们既定的序列化策略来生成JSON。本文将以使用过程中的例子来说明这一问题并给出相应的解决方法。

首先例子中要实现的功能是将对象转换为JSON时(序列化过程),如果对象的某个属性值为null,则该属性不参与序列化,生成的JSON结果也不会包含该属性。

如果你使用过Jackson,尤其是Jackson 2,就知道这样一个功能可以通过设置ObjectMapper对象的SerializationInclusion策略来实现:

ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);

然而巧合的是,将要被序列化的对象其中有一个字段需要使用特别的序列化方式,先来看一下这个类的定义:

/**
 * 动物bean
 * @author chaijunkun
 * @since 2015年4月18日
 */
//此类级注解暂时不启用,待文中提到时再启用
//@JsonInclude(Include.NON_NULL)
public class Animal {

	/** 名称 */
	private String name;

	/** 性别 */
	@JsonSerialize(using = BooleanSerializer.class)
	private Boolean sex;

	//getters and setters

}

在Jackson中,对于Boolean类型sex的属性,序列化结果为{"sex":true}或者{"sex":false}。但我希望当sex为true时,序列化结果为1,否则为0,即{"sex":1}或者{"sex":0}。这就需要自定义一个Boolean类型的序列化器来解决:

/**
 * 自定义布尔序列化器
 * @author chaijunkun
 * @since 2015年4月18日
 */
public class BooleanSerializer extends JsonSerializer<Boolean> {

	@Override
	public void serialize(Boolean value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
		if (Boolean.FALSE.equals(value)){
			jgen.writeNumber(0);
		}else{
			jgen.writeNumber(1);
		}
	}

}

为了验证可用性,编写了如下的单元测试:

public class JacksonTest {

	private static final Logger logger = LoggerFactory.getLogger(JacksonTest.class);

	@Test
	public void doTest() throws IOException{
		ObjectMapper mapper = new ObjectMapper();
		mapper.setSerializationInclusion(Include.NON_NULL);
		StringWriter sw = null;
		String json = null;
		try{
			Animal animal = new Animal();
			//代码块:1,设置name和sex属性
			{
				animal.setName("dog");
				animal.setSex(true);
			}
			//代码块:2,只设置name属性
			{
				animal.setName("dog");
			}
			//代码块:3,什么也不设置
			{}
			sw = new StringWriter();
			JsonGenerator generator = mapper.getFactory().createGenerator(sw);
			generator.writeObject(animal);

			json = sw.toString();
			generator.close();
		}finally {
			IOUtils.closeQuietly(sw);
		}
		logger.info(json);
	}
}

首先把代码块2和3都屏蔽掉,保留代码块1,运行结果为:

{"name":"dog","sex":1}

结果看起来挺正常的。现在单独启用代码块2、代码块3,分别得到的结果为:

{"name":"dog","sex":null}
{"sex":null}

问题出现了,之前不是设置了默认的序列化策略,空属性不参与序列化吗?为何sex字段还是输出null了?原因在于我们对sex字段使用了@JsonSerializer注解。经过查看源码发现,该注解除了using参数外,还有一个include参数,该参数可以不设置,但是隐含的默认值为Inclusion.ALWAYS,也就是说该属性总是参与序列化。那么有没有办法屏蔽掉它呢?可以通过显式设置注解中include参数为Inclusion.NON_NULL来实现,不过在Jackson 2中已经不推荐这种使用方法,而是推荐在属性上增加@JsonInclude(Include.NON_NULL)注解来实现。

@JsonInclude(Include.NON_NULL)注解固然好用,但是每一个属性上都标注上这样一个注解很累,有没有更好的实践呢?当然有,它支持类级注解,只要将它写在类定义上即可实现所有属性为null时都不参与序列化。OK,问题来了,类级注解到底好不好用呢?还是那个单元测试,只启用代码块2,来看下运行结果:

{"name":"dog","sex":null}

坑爹啦!居然没起作用!空的sex字段仍然输出了。来看下我使用的Jackson 2版本

<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.1.0</version>
</dependency>

是一个相对老的版本,在http://mvnrepository.com/上看到有版本更新,目前的最新版本是2.5.2,遂更新了下试试,果然问题解决了,单独运行代码块2后的输出结果:

{"name":"dog"}

后来经过反复测试,能够支持@JsonInclude注解类级使用的最低版本为2.3.0-rc1,当然如果不考虑候选版本的话,正式版的最低要求为2.3.0。所以,赶快升级你的Jackson 2依赖吧!

时间: 2024-08-24 22:46:16

使用低版本Jackson 2的类级@JsonInclude包含策略的bug的相关文章

Android较低版本(&lt;5.2) 页面默认Select选择框效果的BUG解决

Bug描述: 使用低版本安卓(<5.2),在微信上打开网页,点击下拉框,会出现如下图所示的用来展示select选项的弹出框: 在选项较少的时候,可以向下滑动,将选项滑到底部 滑动前: 滑动后: 期望达到的效果: 解决方案: 判断是否是微信环境: function isWeixinBrowser(){ return /micromessenger/.test(navigator.userAgent.toLowerCase()); } 判断安卓版号: var userAgent = navigato

SpringBoot高版本修改为低版本时测试类报错解决

有时在使用idea通过Spring Initailizr创建项目时,默认只能创建最近的版本的SpringBoot项目. 这是如果想要换成版本,就可以在项目创建好了之后,在pom文件中直接将版本修改过来. 如下所示 比如在创建项目时默认的版本为2.2.2版本: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artif

低版本中使用高版本出现的类怎么办?

原理概述 简单来说就是三个字——黑魔法. 利用这种黑魔法的例子已经越来越多,我所知道的最早使用这种方法的是一个老外在三年为了解决NSUUID而使用的. 我们国内团队开发的FDStackView是一个非常好的开源库,已经有1500+颗星星了,希望大家多多支持我们国内的团队,在FDStackView库中也用到了相同的技术,网上有人发出了分析实现原理的文章,但分析的很浅,而且根本没有说在点子上,使得这种黑魔法的魅力并没有被大家欣赏到,我这里做了一些功课,把这个原理详细的阐述一下,以及这里的关键点在哪里

兼容低版本ie的getByClass方法

ecma5为我们提供了实用的getElementsByClassName()方法;可惜这个方法在低版本ie下直接挂掉了. 在不依赖jq的前提下自己实现了一下方法, function getByClass(oParent,sClass){ if(oParent.getElementsByClassName){ //本除返回的是dom集合,类数组而非数组 return oParent.getElementsByClassName(sClass); } var result = []; var re

低版本系统兼容的ActionBar(二)ActionProvider+分离式ActionBar+分离式的ActionMode

       这篇文章主要讲的是在低版本兼容的ActionBar中实现自定义的ActionProvider,ShareActionProvider的使用方法,如何实现分离式ActionBar,外加在分离式ActionBar上的ActionMode的效果. 一.自定义ActionProvider 建立一个类,继承android.support.v4.view.ActionProvider,然后复写里面的方法即可.主要就是初始化视图和相应点击事件. 范例一: SettingsActionProvid

android api实现高斯模糊,且兼容低版本

一.利用android api实现高斯模糊 关于高斯模糊网上已经有很多不错的文章介绍了,在这里就不重复了.先来看一个效果 效果已经看到了,就来看看怎么实现的吧.首先是实现高斯模糊的核心代码 <span style="white-space:pre"> </span>public static Bitmap blurBitmap(Bitmap bitmap, Context context) { // 用需要创建高斯模糊bitmap创建一个空的bitmap Bit

低版本GCC程序向高版本移植的兼容性问题

将低版本gcc编译过的程序移植到高版本GCC时, 可能会出现一些兼容性问题. 原因是, 为了适应新的标准,一些旧的语法规则被废弃了. 关于这方面的一些具体资料可从该处查询. 这里只是自己遇到的其中一个问题. 错误提示: In instantiation of ‘int t(T) [with T = int]’ required from here error: ‘f’ was not declared in this scope, and no declarations were found b

AngularJS开发指南7:AngularJS本地化,国际化,以及兼容IE低版本浏览器

AngularJS本地化,国际化 国际化,简写为i18n,指的是使产品快速适应不同语言和文化. 本地化,简称l10n,是指使产品在特定文化和语言市场中可用. 对开发者来说,国际化一个应用意味着将所有的文字和其他因地区而异的数据从应用中抽离出来. 本地化意味着为这些抽离的数据和文字提供翻译和转变成本地的格式. 目前,AngularJS支持日期,数字和货币的国际化和本地化. 另外,AngularJS还通过ngPluralize指令支持本地多元化. 所有的AngularJS本地化组件都依赖于$loca

jQ1.5中的事件系统(低版本的事件系统)

jQ的一个个版本事系统都在修正着bug和不断优化, 而且看了事件系统对事件的兼容更加熟悉, 更加了解jQ内部的事件机制. 因为jQ对事件系统引入了事件命名空间,事件的代理, 事件的手动触发,事件描述等等各种概念, 对事件的可操控性大大增加, 这个也是库存在的意义, 不是说只要处理addEventListener和attachEvent可以做到的:在大型的项目中事件系统也可以作为发布者和派发者,对整个系统进行充分的解耦, 这些做为自己的笔记,一步一步走, 只是大概看了看, 还有不懂的地方, 最好的