通过message-converters自定义json序列化
问题描述
最近写项目遇到了一个问题,由于这个服务之前是对接线下mis,所以返回json的时候,之前同事复写了消息转换器,使全局的json都从驼峰自动转成了下划线。
现在由于需要拆分服务,根据业务部分接口需要对接web,则需要过滤掉一部分的url,使返回的requestBody中的json不需要驼峰转下划线。
修改前代码
spring-context.xml
<mvc:annotation-driven>
<mvc:message-converters>
<!--这里是自定义的json序列化转换器-->
<bean class="com.razera.sp.component.jackson.DefaultMappingJacksonConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
DefaultMappingJacksonConverter
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
/**
* @Author razera
* @Description json序列化驼峰转下划线配置
*/
public class DefaultMappingJacksonConverter extends MappingJackson2HttpMessageConverter {
public DefaultMappingJacksonConverter() {
ObjectMapper mapper = this.getObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
//设置输出时包含属性的风格
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//禁止使用int代表Enum的order()來反序列化Enum,非常危險
mapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
}
}
解决问题
解决json序列化考虑到了两种方式
- 重写writeInternal方法
- 通过FilterProvider自定义过滤注解
因为FilterProvider是基于实体类的,项目中通过new JsonDate方式向前端返回值,如果要使用这种方式,需要自定义一个新的JsonDate类,有点麻烦。
这里我们选择重写DefaultMappingJacksonConverter类的writeInternal方法来实现这个功能
步骤
- 通过静态代码块来初始化ObjectMapper
- 定义静态集合,来存放需要过滤的url
- 复写writeInternal方法,整体和父类的writeInternal方法差不多。本质上就是将新的ObjectMapper进行初始化,避开了之前的驼峰转下划线设置
修改后的代码
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
/**
* @Author razera
* @Description json序列化驼峰转下划线配置
*/
public class DefaultMappingJacksonConverter extends MappingJackson2HttpMessageConverter {
//需要过滤的url集合
private static List<String> FILTER_URLS = Arrays.asList(
"/privates/spPayMethod/query",
"/privates/spPayMethod/download"
);
//新的自定义ObjectMapper
private final static ObjectMapper mapper = new ObjectMapper();
//通过静态代码块进行初始化
static {
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//禁止使用int代表Enum的order()來反序列化Enum,非常危險
mapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
}
public DefaultMappingJacksonConverter() {
ObjectMapper mapper = this.getObjectMapper();
//设置驼峰转下划线
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
//设置输出时包含属性的风格
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//禁止使用int代表Enum的order()來反序列化Enum,非常危險
mapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, true);
}
//复写writeInternal方法
@Override
protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
if (FILTER_URLS.contains(StringUtils.remove(request.getRequestURI(),request.getContextPath()))) {
MediaType contentType = outputMessage.getHeaders().getContentType();
JsonEncoding encoding = getJsonEncoding(contentType);
ObjectWriter objectWriter = mapper.writer();
JsonGenerator generator = mapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
objectWriter.writeValue(generator, object);
generator.flush();
return;
}
super.writeInternal(object, type, outputMessage);
}
}
注意:这里使用复写writeInternal方法而不是在初始化的时候判断,是因为时机问题,writeInternal,是在response的body的序列化才起作用