Java通过注解或拦截器实现出入参驼峰与蛇式互转
一个努力中的公众号
长的好看的人都关注了
Java语言遵循的开发规范为使用驼峰式命名,如columnA,然而数据库表字段一般使用蛇式命名,如column_a,且前端一般也是使用数据库的字段格式进行交互。因此参数传递和返回就需要对驼峰和蛇式的参数进行转化。
一:注解式
com.fasterxml.jackson.core
包
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.2</version>
</dependency>
# application.properties
spring.jackson.property-naming-strategy=SNAKE_CASE
JsonInclude
注解可以过滤为null
的数据
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TestDTO implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String userName;
private Integer createTime;
}
上面注解就可以实现输入与输出都为蛇式,但是这种情况不能满足自定义开关,以及同时兼容驼峰与蛇式(因为第三方接口交互时有些要求采用驼峰,有的又要求蛇式)
所以还有另外一种方式
二:自定义拦截器统一处理
替换过滤器内request,防止参数丢失
SpringBoot也是通过获取request的输入流来获取参数,如果不替换过滤 器来到Controller请求参数就没了,这是因为 InputStream read方法 内部有一个,postion,标志当前流读取到的位置,每读取一次,位置就 会移动一次,如果读到最后,InputStream.read方法会返回-1,标志已 经读取完了,如果想再次读取,可以调用inputstream.reset方法, position就会移动到上次调用mark的位置,mark默认是0,所以就能从头 再读了。但是呢是否能reset又是由markSupported决定的,为true能 reset,为false就不能reset,从源码可以看到,markSupported是为 false的,而且一调用reset就是直接异常所以这也就代表,InpuStream 只能被读取一次,后面就读取不到了。因此我们在过滤器的时候,已经将 InputStream读取过了一次,当来到Controller,SpringBoot读取 InputStream的时候自然是什么都读取不到了
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @Description: 重写过滤器Filter
* @Author: bigearchart
* @CreateTime: 2023-02-22
* @Version: 1.0
*/
public class HttpServletRequestReplacedFilter implements Filter{
public void destroy() {
}
/**
* @description: 替换过滤器内request,防止参数丢失
* @author: bigearchart
* @date: 2023/2/22 14:50
* @param: [request, response, chain]
* @return: void
**/
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if (request instanceof HttpServletRequest) {
requestWrapper = new RequestReaderHttpServletRequestWrapper((HttpServletRequest) request);
}
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
// 在chain.doFiler方法中传递新的request对象
if (requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
public void init(FilterConfig arg0) throws ServletException {
}
}
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fz.common.util.JudgeUtils;
import com.fz.common.util.sys.JsonObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;
/**
* @Description: 获取所有请求参数执行蛇形转驼峰
* @Author: bigearchart
* @CreateTime: 2023-02-22
* @Version: 1.0
*/
4j
public class RequestReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private String body = null;
"${open_camel}") (
private boolean openCamel;
/**
* @description: 从request中获取所有请求参数将蛇形命名转为驼峰
* @author: bigearchart
* @date: 2023/2/22 14:47
* @param: [request]
* @return:
**/
public RequestReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
JSONObject js = new JSONObject ();
//获取请求路径
String url = request.getRequestURI();
//获取请求ip
String ip = JudgeUtils.getIpAddrByRequest (request);
//获取请求内容
String bodyOld = getBodyStr(request);
//如果开启 蛇形命名转驼峰-将请求内容按要求转换
if(openCamel && (url.contains ("third/") || url.contains ("api/third")|| url.contains ("web/api"))){
//如果请求是jsonObj
try {
JSONObject jsonObject = JSONObject.parseObject (bodyOld);
//如果开启 蛇形命名转驼峰-将请求内容按要求转换
if(openCamel){
log.info ("================开启蛇形命名转驼峰");
jsonObject = JsonObjectUtil.humpConvertJSONObject (JSONObject.parseObject (bodyOld));
}
if (jsonObject != null) {
js = jsonObject;
}
//重构入参
body = js.toJSONString();
//将ip塞入请求参数内
js.put("ip", ip);
} catch (Exception e) {
//否则为array
log.info ("=======array=========开启蛇形命名转驼峰");
JSONArray jsonArray = JSONObject.parseArray (bodyOld);
JSONArray valArray = new JSONArray ();
JSONObject jsonObject ;
//数组长度大于1才执行转换
if (jsonArray != null && jsonArray.size () > 0) {
for(int i = 0;i < jsonArray.size(); i++){
valArray.set (i, JsonObjectUtil.humpConvertJSONObject (jsonArray.getJSONObject (i)));
}
//重构入参
body = valArray.toJSONString();
}else{
body = bodyOld;
}
//将ip塞入请求参数内
js.put("ip", ip);
}
}else {
body = bodyOld;
}
}
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes(Charset.forName("UTF-8")));
return new ServletInputStream() {
public int read() throws IOException {
return bais.read();
}
public boolean isFinished() {
return false;
}
public boolean isReady() {
return false;
}
public void setReadListener(ReadListener readListener) {
}
};
}
/**
* 获取post请求body参数
* @param request HttpServletRequest
* @return String
*/
public static String getBodyStr(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = request.getInputStream();
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
}
下划线转驼峰或者驼峰转下划线
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Description: 驼峰转换Util类
* @Author: bigearchart
* @CreateTime: 2023-02-22
* @Version: 1.0
*/
public class JsonObjectUtil {
private static Pattern humpPattern = Pattern.compile("[A-Z]");
/**
* @param json 下划线转驼峰
* @Description 驼峰命名转换
* @Throws
* @Return com.alibaba.fastjson.JSONObject
* @Date 2021-09-17 17:04:31
* @Author bigearchart
**/
public static JSONObject humpConvertJSONObject (JSONObject json) {
if (null == json) {
return null;
}
//得到所有待处理的key
Set<String> keys = json.keySet ();
//key转数组
String[] array = keys.toArray (new String[ 0 ]);
//循环遍历处理所有key
for (String key : array) {
//得到key
Object value = json.get (key);
//根据_分割为数组
String[] keyArray = key.toLowerCase ().split ("_");
//定义接收处理好格式的Obj
HashMap<String, Object> objMap;
//如果是数组 进行数组转换
if(json.get (key) instanceof JSONArray){
JSONArray vArray = (JSONArray) json.get (key);
//数组长度大于1才执行转换
if (keyArray.length > 0) {
JSONArray newArray = new JSONArray();
for(int i = 0;i < vArray.size(); i++){
objMap = objToCamel(vArray.getJSONObject (i));
// vArray.remove (i);
newArray.set (i, objMap);
value = newArray;
}
}
}else if(json.get (key) instanceof JSONObject){
//否则执行obj转换
objMap = objToCamel(json.getJSONObject (key));
json.put (key, objMap);
value = objMap;
}
//如果含有蛇形命名执行转换
if (isUpperCase (key) && ! key.contains ("_")) {
json.remove (key);
json.put (key.toLowerCase (), value);
continue;
}
//如果含有多个蛇形命名执行转换
if (keyArray.length > 1) {
StringBuilder sb = new StringBuilder ();
for (int i = 0; i < keyArray.length; i++) {
String ks = keyArray[ i ];
if (! "".equals (ks)){
if (i == 0) {
sb.append (ks);
} else {
int c = ks.charAt (0);
if (c >= 97 && c <= 122) {
int v = c - 32;
sb.append ((char) v);
if (ks.length () > 1) {
sb.append (ks.substring (1));
}
} else {
sb.append (ks);
}
}
}
}
json.remove (key);
json.put (sb.toString (), value);
}
}
return json;
}
/**
* @param objectList obj驼峰转下划线
* @Description 驼峰转换
* @Throws
* @Return java.util.List<com.alibaba.fastjson.JSONObject>
* @Date 2021-09-17 17:09:15
* @Author bigearchart
**/
public static List<JSONObject> humpConvertListObject (List<JSONObject> objectList) {
if (null == objectList || objectList.size () <= 0) {
return null;
}
List<JSONObject> data = new ArrayList<> ();
for (JSONObject object : objectList) {
data.add (humpConvertJSONObject (object));
}
return data;
}
/**
* @param array
* @Description array驼峰转下划线
* @Throws
* @Return java.util.List<com.alibaba.fastjson.JSONObject>
* @Date 2021-09-17 17:13:22
* @Author bigearchart
**/
public static List<JSONObject> humpConvertJSONArray (JSONArray array) {
if (null == array || array.size () <= 0) {
return null;
}
List<JSONObject> data = new ArrayList<> ();
for (int i = 0; i < array.size (); i++) {
data.add (humpConvertJSONObject (array.getJSONObject (i)));
}
return data;
}
/**
* @param str
* @Description 判断字符串字母是否为大写
* @Throws
* @Return boolean
* @Date 2021-09-17 17:03:50
* @Author bigearchart
**/
public static boolean isUpperCase (String str) {
for (int i = 0; i < str.length (); i++) {
char c = str.charAt (i); if (c >= 97 && c <= 122) { return false; }
} return true;
}
/**
* @param json obj驼峰转下划线
* @Description 驼峰转换
* @Throws
* @Return java.util.List<com.alibaba.fastjson.JSONObject>
* @Date 2021-09-17 17:09:15
* @Author bigearchart
**/
public static JSONObject humpConvertJson (JSONObject json) {
if (null == json || json.size () <= 0) {
return null;
}
//得到所有待处理的key
Set<String> keys = json.keySet ();
//key转数组
String[] array = keys.toArray (new String[ 0 ]);
//循环遍历处理所有key
for (String key : array) {
//根据_分割为数组
String[] keyArray = key.toLowerCase ().split ("_");
//定义接收处理好格式的Obj
HashMap<String, Object> objMap;
//如果是数组 进行数组转换
if(json.get (key) instanceof JSONArray){
JSONArray vArray = (JSONArray) json.get (key);
//数组长度大于1才执行转换
if (keyArray.length > 0) {
for(int i = 0;i < vArray.size(); i++){
Set<Map.Entry<String, Object>> objEntry = vArray.getJSONObject (i).entrySet ();
objMap = new HashMap<> ();
for (Map.Entry<String, Object> entry : objEntry) {
String oKey = entry.getKey ();
Object oVal = entry.getValue ();
objMap.put (humpToLine2 (oKey), oVal);
}
vArray.remove (i);
vArray.set (i, objMap);
}
}
}else{
//否则执行obj转换
Set<Map.Entry<String, Object>> objEntry = json.getJSONObject (key).entrySet ();
objMap = new HashMap<> ();
for (Map.Entry<String, Object> entry : objEntry) {
String oKey = entry.getKey ();
Object oVal = entry.getValue ();
objMap.put (humpToLine2 (oKey), oVal);
}
json.put (key, objMap);
}
}
return json;
}
/**
* @description: 驼峰转下划线,
* @author: bigearchart
* @date: 2023/2/22 16:41
* @param: [str]
* @return: java.lang.String
**/
public static String humpToLine2(String str){
Matcher matcher = humpPattern.matcher (str);
StringBuffer sb = new StringBuffer ();
while (matcher.find ()) {
matcher.appendReplacement (sb, "_" + matcher.group (0).toLowerCase ());
}
matcher.appendTail (sb);
return sb.toString ();
}
/**
* 功能:下划线命名转驼峰命名
* 将下划线替换为空格,将字符串根据空格分割成数组,再将每个单词首字母大写
* @param s
* @return
*/
private static String under2camel(String s){
String separator = "_";
String under = "";
//判断如果有蛇形命名才执行
if (s.contains (separator)) {
s = s.toLowerCase ().replace (separator, " ");
String sarr[] = s.split (" ");
for (int i = 0; i < sarr.length; i++) {
if (i > 0) {
String w = sarr[ i ].substring (0, 1).toUpperCase () + sarr[ i ].substring (1);
under += w;
} else {
under = sarr[ i ];
}
}
} else {
under = s;
}
return under;
}
/**
* @description: obj蛇形转驼峰
* @author: bigearchart
* @date: 2023/2/22 14:23
* @param: []
* @return: java.util.HashMap<java.lang.String,java.lang.Object>
**/
private static HashMap<String, Object> objToCamel(JSONObject obj) {
Set<Map.Entry<String, Object>> objEntry = obj.entrySet();
HashMap<String, Object> objMap = new HashMap<> ();
for (Map.Entry<String, Object> entry : objEntry) {
String oKey = entry.getKey ();
Object oVal = entry.getValue ();
objMap.put (under2camel (oKey), oVal);
}
return objMap;
}
}
/**
* @description: 注册过滤器到启动类
* @author: bigearchart
* @date: 2023/2/22 14:54
* @param: []
* @return: org.springframework.boot.web.servlet.FilterRegistrationBean
**/
public FilterRegistrationBean httpServletRequestReplacedRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new HttpServletRequestReplacedFilter ());
registration.addUrlPatterns("/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("httpServletRequestReplacedFilter");
registration.setOrder(1);
return registration;
}
至此实现参数驼峰与蛇式的两种方式到此结束,通过接口测试工具调用即可验证。
如果对您有帮助 请点个关注,万分感谢
(微信技术交流群 请加图图微信)
评论