參考:【私人定制jackson】定制jackson的自定義序列化(null值的處理)
在springboot中使用jackson留攒,返回json數(shù)據(jù)時(shí)輸出null值時(shí)按照屬性類型輸出具體形式
可以配置其一個(gè)MappingJackson2HttpMessageConverter類会油,這個(gè)類同時(shí)可以做另一個(gè)事情,防止ie對(duì)json數(shù)據(jù)當(dāng)做文件進(jìn)行下載。
MappingJackson2HttpMessageConverter類中可以取到一個(gè)ObjectMapper唠亚,即jackson序列化的主類区丑。
@Configuration
class JacksonHttpMessageConvertersConfiguration {
@Configuration
@ConditionalOnClass(ObjectMapper.class)
@ConditionalOnBean(ObjectMapper.class)
@ConditionalOnProperty(name= HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY,havingValue="jackson",matchIfMissing=true)
protected static class MappingJackson2HttpMessageConverterConfiguration {
? @Bean
? ?@ConditionalOnMissingBean(value=? ? ? MappingJackson2HttpMessageConverter.class,ignoredType= {
"org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter",
"org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter"})
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(
ObjectMapper objectMapper) {
return new? MappingJackson2HttpMessageConverter(objectMapper);
? ? ? ?}
? ? }
}
上面的代碼是springboot自帶的配置文件,在packageorg.springframework.boot.autoconfigure.web包中
而我們可應(yīng)通過(guò)自己配置的ObjectMapper來(lái)異化jackson在具體操作中解決null輸出的類型
在MappingJackson2HttpMessageConverter中最為重要的就是writeInternal方法橱野,代碼如下
@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
// The following has been deprecated as late as Jackson 2.2 (April 2013);
// preserved for the time being, for Jackson 2.0/2.1 compatibility.
@SuppressWarnings("deprecation")
JsonGenerator jsonGenerator =
this.objectMapper.getJsonFactory().createJsonGenerator(outputMessage.getBody(), encoding);
// A workaround for JsonGenerators not applying serialization features
// https://github.com/FasterXML/jackson-databind/issues/12
if (this.objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
jsonGenerator.useDefaultPrettyPrinter();
? }
try {
if (this.jsonPrefix != null) {
jsonGenerator.writeRaw(this.jsonPrefix);
? ? }
//此處進(jìn)行序列化
this.objectMapper.writeValue(jsonGenerator, object);
? ? }catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
? ? ?}
}
可以發(fā)現(xiàn)在writeInternal方法中使用了this.objectMapper.writeValue(jsonGenerator, object);
進(jìn)行序列化朽缴,跟進(jìn)去,看到一句話:
_serializerProvider(config).serializeValue(jgen, value);
看來(lái)這個(gè)就是具體的序列化的方法了水援。
public void serializeValue(JsonGenerator jgen, Object value)? ? ? ? throws IOException, JsonGenerationException? ? {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?if (value == null) {? ? ? ? ? ? _serializeNull(jgen);? ? ? ? ? ? return;? ? ? ? }? ? ? ?
? ? ? ? Class cls = value.getClass();? ? ? ? // true, since we do want to cache root-level typed? ? ? ? ? ? ? ? ? ? ? ? ? ?serializers (ditto for null property)? ? ? ? final JsonSerializerser = findTypedValueSerializer(cls, true, null);? ? ?
try {
? ? ? ? ser.serialize(value, jgen, this);
? ?} catch (IOException ioe) { // As per [JACKSON-99], pass IOException and subtypes as-is
? throw ioe;
? ?} catch (Exception e) { // but wrap RuntimeExceptions, to get path information
String msg = e.getMessage();
if (msg == null) {
msg = "[no message for "+e.getClass().getName()+"]";
? ?}
throw new JsonMappingException(msg, e);
? }
}
在這里我們找到了對(duì)于null處理的方法
if (value == null) {? ? ? ? ? ? _serializeNull(jgen);? ? ? ? ? ? return;? ? ? ? }
繼續(xù)跟進(jìn)_serializeNull(jgen)
protected void_serializeNull(JsonGenerator gen)throwsIOException
{
JsonSerializer ser = getDefaultNullValueSerializer();
try{
ser.serialize(null,gen, this);
? ? }catch(IOException ioe) {// no wrapping for IO (and derived)
throwioe;
? ? }catch(Exception e) {// but others do need to be, to get path etc
String msg = e.getMessage();
if(msg ==null) {
? ? msg ="[no message for "+e.getClass().getName()+"]";
? }? ?
reportMappingProblem(e,msg);
? ?}
}
發(fā)現(xiàn)JsonSerializer ser = getDefaultNullValueSerializer()
JsonSerializer是一個(gè)抽象類密强,具有多個(gè)實(shí)現(xiàn)
public JsonSerializer getDefaultNullValueSerializer() {
return _nullValueSerializer;
}
ser.serialize(null,gen, this);
而在_nullValueSerializer中ser的具體實(shí)現(xiàn)是這樣的
@Override ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? public? void? serialize(Object value, JsonGenerator jgen, SerializerProvider provider)throwsIOException, JsonGenerationException
{
jgen.writeNull();
}
只要替換掉這個(gè)_nullValueSerializer? 就可以了。
但是這個(gè)jsonSerializer有一個(gè)比較嚴(yán)重的問(wèn)題蜗元,就是這個(gè)nullValueSerializer是全局的或渤,即所有的null都會(huì)應(yīng)用這個(gè)JsonSerializer,在這個(gè)類中無(wú)法判斷類型奕扣。
所以繼續(xù)向下跟代碼:
跟入 ser.serialize(value, jgen, this); ?這個(gè)方法薪鹦,發(fā)現(xiàn)其有許多的實(shí)現(xiàn),通過(guò)調(diào)試模式,進(jìn)入了一個(gè)叫做BeanSerializer的類池磁,其實(shí)現(xiàn)為:
/*** Main serialization method that will delegate actual output to
* configured
* {@linkBeanPropertyWriter} instances.*/
@Overridepublicfinalvoidserialize(Object bean, JsonGenerator jgen, SerializerProvider provider)throwsIOException, JsonGenerationException
{
if(_objectIdWriter !=null) {
_serializeWithObjectId(bean, jgen, provider,true);return;
?? }
jgen.writeStartObject();if(_propertyFilterId !=null) {
serializeFieldsFiltered(bean, jgen, provider);
?? }else{
//調(diào)試模式下最終走了這個(gè)方法? ? ? ? ??
serializeFields(bean, jgen, provider);? ? ??
??? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? jgen.writeEndObject();?
? }
protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider)throwsIOException, JsonGenerationException
{finalBeanPropertyWriter[] props;if(_filteredProps !=null&& provider.getActiveView() !=null) {
props=_filteredProps;
? }else{
props=_props;
?? }inti = 0;try{for(finalintlen = props.length; i < len; ++i) {
BeanPropertyWriter prop=props[i];
if(prop !=null) {//can have nulls in filtered list
prop.serializeAsField(bean, jgen, provider);
? }
?? }if(_anyGetterWriter !=null) {
_anyGetterWriter.getAndSerialize(bean, jgen, provider);
?? }
?? }catch(Exception e) {
String name= (i == props.length) ? "[anySetter]": props[i].getName();
wrapAndThrow(provider, e, bean, name);
?? }catch(StackOverflowError e) {/*04-Sep-2009, tatu: Dealing with this is tricky, since we do not
*? have many stack frames to spare... just one or two; can't
*? make many calls.*/JsonMappingException mapE=newJsonMappingException("Infinite recursion (StackOverflowError)", e);
String name= (i == props.length) ? "[anySetter]": props[i].getName();
mapE.prependPath(newJsonMappingException.Reference(bean, name));throwmapE;
?? }
}
這個(gè)方法中最重要的一個(gè)東西就是BeanPropertyWriter 這個(gè)類奔害,這個(gè)類是由SerializerFactory 工廠進(jìn)行實(shí)例化的,其作用是對(duì)bean中的每個(gè)字段進(jìn)行jackson操作的封裝地熄,其中封裝了字段的一些元信息华临,和對(duì)此字段進(jìn)行jackson序列化的操作,那么問(wèn)題來(lái)了端考,這么說(shuō)來(lái)雅潭,這個(gè)BeanPropertyWriter類其實(shí)就是jackson真正如何對(duì)每個(gè)bean進(jìn)行轉(zhuǎn)json的最終的操作的實(shí)現(xiàn),那么我們是不是只要替換掉這個(gè)類就可以了跛梗。那么看看jackson為我們預(yù)留的對(duì)此類進(jìn)行自定義的方法寻馏。
jackson通過(guò)JsonSerializer來(lái)對(duì)javabean序列化,此serializer都是通過(guò)一個(gè)SerializerFactory活的的核偿,在這個(gè)工廠類中诚欠,找到了一個(gè)這個(gè)方法:
@SuppressWarnings("unchecked")
protected JsonSerializer constructBeanSerializer(SerializerProvider prov,
BeanDescription beanDesc)throwsJsonMappingException
{//13-Oct-2010, tatu: quick sanity check: never try to create bean serializer for plain Object//05-Jul-2012, tatu: ... but we should be able to just return "unknown type" serializer, right?
if(beanDesc.getBeanClass() == Object.class) {returnprov.getUnknownTypeSerializer(Object.class);
//throw new IllegalArgumentException("Can not create bean serializer for Object.class");}
finalSerializationConfig config =prov.getConfig();
BeanSerializerBuilder builder=constructBeanSerializerBuilder(beanDesc);
builder.setConfig(config);
//First: any detectable (auto-detect, annotations) properties to serialize?
//注意這里,這里為每個(gè)屬性實(shí)例化了一個(gè)BeanPropertyWriter? ? ? ?
List props =findBeanProperties(prov, beanDesc, builder);
if(props ==null) {
props=newArrayList();
}//[JACKSON-440] Need to allow modification bean properties to serialize:
//這里通過(guò)_factoryConfig中的配置:BeanSerializerModifier 對(duì)這個(gè)props做了change(修改)漾岳,
if(_factoryConfig.hasSerializerModifiers()) {for(BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
props=mod.changeProperties(config, beanDesc, props);
}
}//Any properties to suppress?props = filterBeanProperties(config, beanDesc, props);
//.....之后的省略
重點(diǎn)注意:
//這里通過(guò)_factoryConfig中的配置:? ?BeanSerializerModifier 對(duì)這個(gè)props做了change(修改)轰绵,
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()){
props =mod.changeProperties(config, beanDesc, props);
? }
}
這里從factoryConfig中拿出來(lái)了一個(gè)Modifiers集合,并且通過(guò)這些Modifiers對(duì)List進(jìn)行了修改尼荆,那么這樣就簡(jiǎn)單了左腔,我們只要自己定義一個(gè)Modifyer對(duì)某個(gè)List類型的BeanPropertyWriter進(jìn)行修改集合了。
首先定義一個(gè)Modifyer
public class MyBeanSerializerModifier extends BeanSerializerModifier {
private JsonSerializer _nullArrayJsonSerializer =new MyNullArrayJsonSerializer();
@Override
public? List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List beanProperties) {
//循環(huán)所有的beanPropertyWriter
for(int i = 0; i < beanProperties.size(); i++) {
BeanPropertyWriter writer=beanProperties.get(i);
//判斷字段的類型捅儒,如果是array液样,list,set則注冊(cè)nullSerializer
if(isArrayType(writer)) {
//給writer注冊(cè)一個(gè)自己的nullSerializer? ? ? ? ? ? ? ? writer.assignNullSerializer(this.defaultNullArrayJsonSerializer());
? }
?? }
??? return beanProperties;
?? }
? //判斷是什么類型
protected boolean isArrayType(BeanPropertyWriter writer) {
Class clazz =writer.getPropertyType();
return clazz.isArray() || clazz.equals(List.class) || clazz.equals(Set.class);
? }
protected? JsonSerializer<Object>? defaultNullArrayJsonSerializer() {
??? return? _nullArrayJsonSerializer;
? }
}
一個(gè)對(duì)null值處理的JsonSeralizer:
publicclass MyNullArrayJsonSerializer extends JsonSerializer{
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)throwsIOException, JsonProcessingException {if(value ==null) {
jgen.writeStartArray();
jgen.writeEndArray();
?? }else{
?? jgen.writeObject(value);
?? }
? }
}
還是那個(gè)MappingJackson2HttpMessageConverter:
@Configuration
public classJsonConfig {
@Bean
publicMappingJackson2HttpMessageConvertermappingJacksonHttpMessageConverter() {
final MappingJackson2HttpMessageConverter converter = new? MappingJackson2HttpMessageConverter();
ObjectMapper mapper = converter.getObjectMapper();
// 為mapper注冊(cè)一個(gè)帶有SerializerModifier的Factory巧还,此modifier主要做的事情為:當(dāng)序列化類型為array鞭莽,list、set時(shí)麸祷,當(dāng)值為空時(shí)澎怒,序列化成[]
mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(newMyBeanSerializerModifier()));
returnconverter;
? }
}
大功告成!阶牍!