參考:【私人定制jackson】定制jackson的自定義序列化(null值的處理)
在springboot中使用jackson,返回json數據時輸出null值時按照屬性類型輸出具體形式
可以配置其一個MappingJackson2HttpMessageConverter類,這個類同時可以做另一個事情,防止ie對json數據當做文件進行下載。
MappingJackson2HttpMessageConverter類中可以取到一個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包中
而我們可應通過自己配置的ObjectMapper來異化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);
? ? }
//此處進行序列化
this.objectMapper.writeValue(jsonGenerator, object);
? ? }catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
? ? ?}
}
可以發現在writeInternal方法中使用了this.objectMapper.writeValue(jsonGenerator, object);
進行序列化,跟進去,看到一句話:
_serializerProvider(config).serializeValue(jgen, value);
看來這個就是具體的序列化的方法了。
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);
? }
}
在這里我們找到了對于null處理的方法
if (value == null) {? ? ? ? ? ? _serializeNull(jgen);? ? ? ? ? ? return;? ? ? ? }
繼續跟進_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);
? ?}
}
發現JsonSerializer ser = getDefaultNullValueSerializer()
JsonSerializer是一個抽象類,具有多個實現
public JsonSerializer getDefaultNullValueSerializer() {
return _nullValueSerializer;
}
ser.serialize(null,gen, this);
而在_nullValueSerializer中ser的具體實現是這樣的
@Override ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? public? void? serialize(Object value, JsonGenerator jgen, SerializerProvider provider)throwsIOException, JsonGenerationException
{
jgen.writeNull();
}
只要替換掉這個_nullValueSerializer? 就可以了。
但是這個jsonSerializer有一個比較嚴重的問題,就是這個nullValueSerializer是全局的,即所有的null都會應用這個JsonSerializer,在這個類中無法判斷類型。
所以繼續向下跟代碼:
跟入 ser.serialize(value, jgen, this); ?這個方法,發現其有許多的實現,通過調試模式,進入了一個叫做BeanSerializer的類,其實現為:
/*** 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{
//調試模式下最終走了這個方法? ? ? ? ??
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;
?? }
}
這個方法中最重要的一個東西就是BeanPropertyWriter 這個類,這個類是由SerializerFactory 工廠進行實例化的,其作用是對bean中的每個字段進行jackson操作的封裝,其中封裝了字段的一些元信息,和對此字段進行jackson序列化的操作,那么問題來了,這么說來,這個BeanPropertyWriter類其實就是jackson真正如何對每個bean進行轉json的最終的操作的實現,那么我們是不是只要替換掉這個類就可以了。那么看看jackson為我們預留的對此類進行自定義的方法。
jackson通過JsonSerializer來對javabean序列化,此serializer都是通過一個SerializerFactory活的的,在這個工廠類中,找到了一個這個方法:
@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?
//注意這里,這里為每個屬性實例化了一個BeanPropertyWriter? ? ? ?
List props =findBeanProperties(prov, beanDesc, builder);
if(props ==null) {
props=newArrayList();
}//[JACKSON-440] Need to allow modification bean properties to serialize:
//這里通過_factoryConfig中的配置:BeanSerializerModifier 對這個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);
//.....之后的省略
重點注意:
//這里通過_factoryConfig中的配置:? ?BeanSerializerModifier 對這個props做了change(修改),
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()){
props =mod.changeProperties(config, beanDesc, props);
? }
}
這里從factoryConfig中拿出來了一個Modifiers集合,并且通過這些Modifiers對List進行了修改,那么這樣就簡單了,我們只要自己定義一個Modifyer對某個List類型的BeanPropertyWriter進行修改集合了。
首先定義一個Modifyer
public class MyBeanSerializerModifier extends BeanSerializerModifier {
private JsonSerializer _nullArrayJsonSerializer =new MyNullArrayJsonSerializer();
@Override
public? List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List beanProperties) {
//循環所有的beanPropertyWriter
for(int i = 0; i < beanProperties.size(); i++) {
BeanPropertyWriter writer=beanProperties.get(i);
//判斷字段的類型,如果是array,list,set則注冊nullSerializer
if(isArrayType(writer)) {
//給writer注冊一個自己的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;
? }
}
一個對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);
?? }
? }
}
還是那個MappingJackson2HttpMessageConverter:
@Configuration
public classJsonConfig {
@Bean
publicMappingJackson2HttpMessageConvertermappingJacksonHttpMessageConverter() {
final MappingJackson2HttpMessageConverter converter = new? MappingJackson2HttpMessageConverter();
ObjectMapper mapper = converter.getObjectMapper();
// 為mapper注冊一個帶有SerializerModifier的Factory,此modifier主要做的事情為:當序列化類型為array,list、set時,當值為空時,序列化成[]
mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(newMyBeanSerializerModifier()));
returnconverter;
? }
}
大功告成!!