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.propertiesspring.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*/4jpublic 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();//获取请求ipString ip = JudgeUtils.getIpAddrByRequest (request);//获取请求内容String bodyOld = getBodyStr(request);//如果开启 蛇形命名转驼峰-将请求内容按要求转换if(openCamel && (url.contains ("third/") || url.contains ("api/third")|| url.contains ("web/api"))){//如果请求是jsonObjtry {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) {//否则为arraylog.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;}//得到所有待处理的keySet<String> keys = json.keySet ();//key转数组String[] array = keys.toArray (new String[ 0 ]);//循环遍历处理所有keyfor (String key : array) {//得到keyObject value = json.get (key);//根据_分割为数组String[] keyArray = key.toLowerCase ().split ("_");//定义接收处理好格式的ObjHashMap<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;}//得到所有待处理的keySet<String> keys = json.keySet ();//key转数组String[] array = keys.toArray (new String[ 0 ]);//循环遍历处理所有keyfor (String key : array) {//根据_分割为数组String[] keyArray = key.toLowerCase ().split ("_");//定义接收处理好格式的ObjHashMap<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;}
至此实现参数驼峰与蛇式的两种方式到此结束,通过接口测试工具调用即可验证。
如果对您有帮助 请点个关注,万分感谢
(微信技术交流群 请加图图微信)
评论
