【私人定制jackson】定制jackson的自定义序列化(null值的处理)
最近用springMVC做服务端的http+json的接口,出现一个不是特别容易解决的问题:
在对List类型的值进行处理时,有一部分服务是有做一些逻辑判断的,在逻辑判断不通过的时候会返回一个null值,
而有一些值是直接通过jpa查询到的List类型的值则会进行实例化,即同样是List类型,一个是null,一个"[]"。
最简单的办法是在null值的地方全部实例化一个new ArrayList<?>(0);但是这样会修改很多地方,而且对于这些情况都要进行实例化分配内存不是那么的理想。
所以就在springMvc转json的地方做手脚。
我们都知道springMvc是使用jackson做的json序列化工具。
@Bean
public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setSupportedMediaTypes(ImmutableList.of(MediaType.TEXT_HTML, MediaType.APPLICATION_JSON));
return converter;
}
可以配置其一个MappingJackson2HttpMessageConverter类,这个类同时可以做另一个事情,防止ie对json数据当做文件进行下载。
MappingJackson2HttpMessageConverter类中可以取到一个ObjectMapper,即jackson序列化的主类。
查看代码看到:
@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);
}
}
看到使用了
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 JsonSerializer<Object> ser = 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);
}
}
ok,我们本来的目的是 对null值的处理,那么在这个地方我们看到了一个对null的处理,
/**
* @since 2.0
*/
public JsonSerializer<Object> getDefaultNullValueSerializer() {
return _nullValueSerializer;
}
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException
{
jgen.writeNull();
}
那么,我们是不是只要替换掉这个_nullValueSerializer 就可以了呢,是的,这个一个比较常规的对于null值处理的方法。
具体参考:http://blog.csdn.net/zshake/article/details/17582691
但是这个jsonSerializer有一个比较严重的问题,就是这个nullValueSerializer是全局的,即所有的null都会应用这个JsonSerializer,在这个类中无法判断类型。
我无法判断当我是List类型时怎样,普通类型时怎样。
所以继续向下跟代码:
跟入 ser.serialize(value, jgen, this); 这个方法,发现其有许多的实现,通过调试模式,进入了一个叫做BeanSerializer的类,其实现为:
/**
* Main serialization method that will delegate actual output to
* configured
* {@link BeanPropertyWriter} instances.
*/
@Override
public final void serialize(Object bean, JsonGenerator jgen, SerializerProvider provider)
throws IOException, 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)
throws IOException, JsonGenerationException
{
final BeanPropertyWriter[] props;
if (_filteredProps != null && provider.getActiveView() != null) {
props = _filteredProps;
} else {
props = _props;
}
int i = 0;
try {
for (final int len = 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 = new JsonMappingException("Infinite recursion (StackOverflowError)", e);
String name = (i == props.length) ? "[anySetter]" : props[i].getName();
mapE.prependPath(new JsonMappingException.Reference(bean, name));
throw mapE;
}
}
这个方法中最重要的一个东西就是BeanPropertyWriter 这个类,这个类是由SerializerFactory 工厂进行实例化的,其作用是对bean中的每个字段进行jackson操作的封装,其中封装了字段的一些元信息,
和对此字段进行jackson序列化的操作,那么问题来了,这么说来,这个BeanPropertyWriter类其实就是jackson真正如何对每个bean进行转json的最终的操作的实现,那么我们是不是只要替换掉这个类就可以了
呢,答案是肯定的。
那么看看jackson为我们预留的对此类进行自定义的方法。
jackson通过JsonSerializer来对javabean序列化,此serializer都是通过一个SerializerFactory活的的,在这个工厂类中,找到了一个这个方法:
@SuppressWarnings("unchecked")
protected JsonSerializer<Object> constructBeanSerializer(SerializerProvider prov,
BeanDescription beanDesc)
throws JsonMappingException
{
// 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) {
return prov.getUnknownTypeSerializer(Object.class);
// throw new IllegalArgumentException("Can not create bean serializer for Object.class");
}
final SerializationConfig config = prov.getConfig();
BeanSerializerBuilder builder = constructBeanSerializerBuilder(beanDesc);
builder.setConfig(config); // First: any detectable (auto-detect, annotations) properties to serialize?
//注意这里,这里为每个属性实例化了一个BeanPropertyWriter
List<BeanPropertyWriter> props = findBeanProperties(prov, beanDesc, builder);
if (props == null) {
props = new ArrayList<BeanPropertyWriter>();
}
// [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<BeanPropertyWriter>进行了修改,那么这样就简单了,我们只要自己定义一个Modifyer对某个List类型的BeanPropertyWriter进行修改集合了。
首先定义一个Modifyer
public class MyBeanSerializerModifier extends BeanSerializerModifier { private JsonSerializer<Object> _nullArrayJsonSerializer = new MyNullArrayJsonSerializer(); @Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> 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:
public class MyNullArrayJsonSerializer extends JsonSerializer<Object> { @Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
if (value == null) {
jgen.writeStartArray();
jgen.writeEndArray();
} else {
jgen.writeObject(value);
}
}
}
主要是看看怎么设置到jackson里:
还是那个MappingJackson2HttpMessageConverter:
@Bean
public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = converter.getObjectMapper();
// 为mapper注册一个带有SerializerModifier的Factory,此modifier主要做的事情为:当序列化类型为array,list、set时,当值为空时,序列化成[]
mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier())); converter.setSupportedMediaTypes(ImmutableList.of(MediaType.TEXT_HTML, MediaType.APPLICATION_JSON));
return converter;
}
看看效果:
在设置mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));
我们转换一个类:
public class NullTest { private String key; private List<String> list; private Map<String, String> map; private int[] array = new int[0]; private String[] array2; private java.util.Date now = new java.util.Date(); //getter.....setter..... }
@RequestMapping("test/aaa")
@ResponseBody
public ResponseResult test() {
System.err.println("=====");
return this.successResult().data(new NullTest());
}
之前:
{"success":true,"code":0,"message":"","data":{"key":null,"list":null,"map":null,"array":[],"array2":null,"now":1450167151924}}
之后:
{"success":true,"code":0,"message":"","data":{"key":null,"list":[],"map":null,"array":[],"array2":[],"now":1450167205726}}
成功!
参考资料:
http://www.baeldung.com/jackson-json-view-annotation
http://blog.csdn.net/zshake/article/details/17582691
========================华丽的分割线=======================================================
基于最近好几个人问我如果不用spring-boot,普通的spring-mvc怎么进行xml配置,特此总结一下
思路是使用spring的定义bean的方式,通过工厂的方式进行定义
1.首先创建一个工厂:
public class MappingJackson2HttpMessageConverterFactory { public MappingJackson2HttpMessageConverter init() {
final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = converter.getObjectMapper();
// 为mapper注册一个带有SerializerModifier的Factory,此modifier主要做的事情为:当序列化类型为array,list、set时,当值为空时,序列化成[]
mapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()); converter.setSupportedMediaTypes(ImmutableList.of(MediaType.TEXT_HTML, MediaType.APPLICATION_JSON));
return converter;
}
}
2.进行spring-mvc.xml的配置
<context:annotation-config />
<!-- 激活@Controller模式 -->
<mvc:annotation-driven >
<!-- 消息转换器 -->
<mvc:message-converters register-defaults="true" >
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes" value="text/plain;charset=UTF-8" />
</bean>
<bean factory-bean="mappingJackson2HttpMessageConverterFactory" factory-method="init"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" >
</bean>
</mvc:message-converters>
</mvc:annotation-driven> <bean id="mappingJackson2HttpMessageConverterFactory" class = "com.lclc.core.MappingJackson2HttpMessageConverterFactory" />
<!-- 对包中的所有类进行扫描,以完成Bean创建和自动依赖注入的功能 需要更改 -->
<context:component-scan base-package="com.lclc" /> <!--其他配置 -->
【私人定制jackson】定制jackson的自定义序列化(null值的处理)的更多相关文章
- 定制jackson的自定义序列化(null值的处理)
http://www.cnblogs.com/lic309/p/5048631.html
- jackSon注解– @JsonInclude 注解不返回null值字段
@Data @JsonInclude(JsonInclude.Include.NON_NULL) public class OrderDTO { private String orderId; @Js ...
- c# 通过json.net中的JsonConverter进行自定义序列化与反序列化
https://www.cnblogs.com/yijiayi/p/10051284.html 相信大家在工作中会经常遇见对json进行序列化与反序列化吧,但通常的序列化与反序列化中的json结构与c ...
- 源码分析springboot自定义jackson序列化,默认null值个性化处理返回值
最近项目要实现一种需求,对于后端返回给前端的json格式的一种规范,不允许缺少字段和字段值都为null,所以琢磨了一下如何进行将springboot的Jackson序列化自定义一下,先看看如何实现,再 ...
- Java下利用Jackson进行JSON解析和序列化
Java下利用Jackson进行JSON解析和序列化 Java下常见的Json类库有Gson.JSON-lib和Jackson等,Jackson相对来说比较高效,在项目中主要使用Jackson进行 ...
- 47.Odoo产品分析 (五) – 定制板块(2) – 为业务自定义odoo(2)
查看Odoo产品分析系列--目录 Odoo产品分析 (五) – 定制板块(2) – 为业务自定义odoo(1) 4 添加自定义字段 定制odoo的最普通的原因就是指定到公司的附加信息.如果您正在运行一 ...
- JSON数据处理框架Jackson精解第一篇-序列化与反序列化核心用法
Jackson是Spring Boot默认的JSON数据处理框架,但是其并不依赖于任何的Spring 库.有的小伙伴以为Jackson只能在Spring框架内使用,其实不是的,没有这种限制.它提供了很 ...
- java 自定义序列化
pom.xml 导包 创建自己的序列化类,继承 com.fasterxml.jackson.databind.JsonSerializer<T> 抽象类 重写 serialize() 方法 ...
- Jackson中处理map中的null key 或者null value 及实体字段中的null value
1.map中有null key时的序列化 当有null key时,jackson序列化会报 Null key for a Map not allowed in JSON (use a convert ...
随机推荐
- 搭建fedora开发环境 common lisp, c++, go
第三方软件库: http://download1.rpmfusion.org/free/fedora/releases/25/Everything/x86_64/os/repoview/index.h ...
- MaxScript重启3dsMax的重新思考
前天看到一位大神写用MaxScript实现重启3dsMax的方法,用的是.net临时编译一个exe出来,然后用这个新的进程来关闭并开启新的max.感觉这种思路不错,或许可以用在别的地方.不过谈及max ...
- “不是有效WIN32程序”
写了个控制台,放到服务器上面运行,运行失败,出现"不是有效win32程序" 不知道原因. 猜测: 1.程序只支持64位的处理器,修改了项目的属性,发现支持any cpu.排除 2. ...
- 解析json
String json = "{\"elements\":[{\"distance\":{\"text\":\"1364 ...
- python基础知识---正则
一.python正则简介 python的re模块,让python能够支持perl正则 perl正则的字符集("." "[abc]" "(abc) ...
- ASP.NET Misconfiguration: Debug Information
Abstract: Debugging messages help attackers learn about the system and plan a form of attack. Explan ...
- scrum.4
1.准备看板. 形式参考图4. 2.任务认领,并把认领人标注在看板上的任务标签上. 先由个人主动领任务,PM根据具体情况进行任务的平衡. 然后每个人都着手实现自己的任务. 3.为了团队合作愉快进展顺利 ...
- 锋利的js前端分页之jQuery
大家在作分页时,多数是在后台返回一个导航条的html字符串,其实在前端用js也很好实现. 调用pager方法,输入参数,会返回一个导航条的html字符串.方法的内部比较简单. /** * pageSi ...
- shell编程学习
1.项目中用到Linux的crontrab Linux下的定时执行主要是使用crontab文件中加入定制计划来执行,但是也不是非常复杂,基本上用过一遍就能记住了,关键是要记住/var/spool/cr ...
- .net 实现上传文件分割,断点续传上传文件
一 介绍 断点续传搜索大部分都是下载的断点续传,涉及到HTTP协议1.1的Range和Content-Range头. 来个简单的介绍 所谓断点续传,也就是要从文件已经下载的地方开始继续下载.在以前版本 ...