使用低版本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依賴吧!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章