参考:【私人定制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;
}
}
大功告成!!