SpringBoot或OpenFeign中 Jackson 配置参数名蛇形、小驼峰、大驼峰、自定义命名
SpringBoot或OpenFeign中 Jackson 配置参数名蛇形、小驼峰、大驼峰、自定义命名
前言
在调用外部接口时,对方给出的接口文档中,入参参数名一会大写加下划线,一会又是驼峰命名。
示例如下:
{"MOF_DIV_CODE": "xxxxx",....."yyyList": ["VOU_DET_ID": "xxxxx",......]
}
至于为什么非要这样写,不去深究它。背后默默 diss 一下。
当我利用OpenFeign调用接口时,传入参数定义的DTO对象都是大写的,如下:
public class XxxReqDTO {private String MOF_DIV_CODE;....private String XXX_XXX_XXX;....private List<YyyReqDTO> yyyList;
}
/**
* 这里没有使用@Data注解
* 用IDEA生成的setter()、getter()方法
* 如果@Data 生成的出现问题,可以改成自己生成。
**/
也配置了MyBatis-Plus不自动转驼峰命名
mybatis-plus:configuration:map-underscore-to-camel-case: false
但是控制台打印入参时,却变成了小写、下划线、大写,示例如下:
{mOF_DIV_CODE : "xxxxx"......yyyList:["vOU_DET_ID": "xxxxx",]
}
因为之前遇到过类似的问题,想到时SpringBoot Jackson序列化、反序列化及配置问题。
以下记录我的解决方法与过程。
解决办法
数据库字段重命名
这里以PostgreSQL为例(本次开发使用指定数据库),其他数据库自测下。本方法为偷懒做法,多的时间用于摸鱼
select
pk_detail "VOU_ID"
from table;
在查询时,直接重命名字段,记住一定要加引号,不然数据库里查询结果列名字段也会是小写。
不加引号效果如下:
这样使得让查询的结果与定义入参对象都是大写+下划线的参数名。
配置Jackson
特地去看了下 PropertyNamingStrategy 的源码,没找到我想要的命名方式。
源码如下:
package com.fasterxml.jackson.databind;import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;/*** Class that defines how names of JSON properties ("external names")* are derived from names of POJO methods and fields ("internal names"),* in cases where no explicit annotations exist for naming.* Methods are passed information about POJO member for which name is needed,* as well as default name that would be used if no custom strategy was used.*<p>* Default (empty) implementation returns suggested ("implicit" or "default") name unmodified*<p>* Note that the strategy is guaranteed to be called once per logical property* (which may be represented by multiple members; such as pair of a getter and* a setter), but may be called for each: implementations should not count on* exact number of times, and should work for any member that represent a* property.* Also note that calls are made during construction of serializers and deserializers* which are typically cached, and not for every time serializer or deserializer* is called.*<p>* In absence of a registered custom strategy, the default Java property naming strategy* is used, which leaves field names as is, and removes set/get/is prefix* from methods (as well as lower-cases initial sequence of capitalized* characters).*<p>* NOTE! Since 2.12 sub-classes defined here (as well as static singleton instances thereof)* are deprecated due to* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>.* Please use constants and classes in {@link PropertyNamingStrategies} instead.* */
@SuppressWarnings("serial")
public class PropertyNamingStrategy // NOTE: was abstract until 2.7implements java.io.Serializable
{private static final long serialVersionUID = 2L;/*** @deprecated Since 2.12 deprecated. Use {@link PropertyNamingStrategies#LOWER_CAMEL_CASE} instead.* See* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reasons for deprecation.*/@Deprecated // since 2.12public static final PropertyNamingStrategy LOWER_CAMEL_CASE = new PropertyNamingStrategy();/*** @deprecated Since 2.12 deprecated. Use {@link PropertyNamingStrategies#UPPER_CAMEL_CASE} instead.* See* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reasons for deprecation.*/@Deprecated // since 2.12public static final PropertyNamingStrategy UPPER_CAMEL_CASE = new UpperCamelCaseStrategy();/*** @deprecated Since 2.12 deprecated. Use {@link PropertyNamingStrategies#SNAKE_CASE} instead.* See* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reasons for deprecation.*/@Deprecated // since 2.12public static final PropertyNamingStrategy SNAKE_CASE = new SnakeCaseStrategy();/*** @deprecated Since 2.12 deprecated. Use {@link PropertyNamingStrategies#LOWER_CASE} instead.* See* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reasons for deprecation.*/@Deprecated // since 2.12public static final PropertyNamingStrategy LOWER_CASE = new LowerCaseStrategy();/*** @deprecated Since 2.12 deprecated. Use {@link PropertyNamingStrategies#KEBAB_CASE} instead.* See* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reasons for deprecation.*/@Deprecated // since 2.12public static final PropertyNamingStrategy KEBAB_CASE = new KebabCaseStrategy();/*** @deprecated Since 2.12 deprecated. Use {@link PropertyNamingStrategies#LOWER_DOT_CASE} instead.* See* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reasons for deprecation.*/@Deprecated // since 2.12public static final PropertyNamingStrategy LOWER_DOT_CASE = new LowerDotCaseStrategy();/*/**********************************************************/* API/***********************************************************//*** Method called to find external name (name used in JSON) for given logical* POJO property,* as defined by given field.* * @param config Configuration in used: either <code>SerializationConfig</code>* or <code>DeserializationConfig</code>, depending on whether method is called* during serialization or deserialization* @param field Field used to access property* @param defaultName Default name that would be used for property in absence of custom strategy* * @return Logical name to use for property that the field represents*/public String nameForField(MapperConfig<?> config, AnnotatedField field,String defaultName){return defaultName;}/*** Method called to find external name (name used in JSON) for given logical* POJO property,* as defined by given getter method; typically called when building a serializer.* (but not always -- when using "getter-as-setter", may be called during* deserialization)* * @param config Configuration in used: either <code>SerializationConfig</code>* or <code>DeserializationConfig</code>, depending on whether method is called* during serialization or deserialization* @param method Method used to access property.* @param defaultName Default name that would be used for property in absence of custom strategy* * @return Logical name to use for property that the method represents*/public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method,String defaultName){return defaultName;}/*** Method called to find external name (name used in JSON) for given logical* POJO property,* as defined by given setter method; typically called when building a deserializer* (but not necessarily only then).* * @param config Configuration in used: either <code>SerializationConfig</code>* or <code>DeserializationConfig</code>, depending on whether method is called* during serialization or deserialization* @param method Method used to access property.* @param defaultName Default name that would be used for property in absence of custom strategy* * @return Logical name to use for property that the method represents*/public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method,String defaultName){return defaultName;}/*** Method called to find external name (name used in JSON) for given logical* POJO property,* as defined by given constructor parameter; typically called when building a deserializer* (but not necessarily only then).* * @param config Configuration in used: either <code>SerializationConfig</code>* or <code>DeserializationConfig</code>, depending on whether method is called* during serialization or deserialization* @param ctorParam Constructor parameter used to pass property.* @param defaultName Default name that would be used for property in absence of custom strategy*/public String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam,String defaultName){return defaultName;}/*/**********************************************************/* Public base class for simple implementations/***********************************************************//*** @deprecated Since 2.12 deprecated. See* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reasons for deprecation.*/@Deprecatedpublic static abstract class PropertyNamingStrategyBase extends PropertyNamingStrategy{@Overridepublic String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName){return translate(defaultName);}@Overridepublic String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName){return translate(defaultName);}@Overridepublic String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName){return translate(defaultName);}@Overridepublic String nameForConstructorParameter(MapperConfig<?> config, AnnotatedParameter ctorParam,String defaultName){return translate(defaultName);}public abstract String translate(String propertyName);/*** Helper method to share implementation between snake and dotted case.*/protected static String translateLowerCaseWithSeparator(final String input, final char separator){if (input == null) {return input; // garbage in, garbage out}final int length = input.length();if (length == 0) {return input;}final StringBuilder result = new StringBuilder(length + (length >> 1));int upperCount = 0;for (int i = 0; i < length; ++i) {char ch = input.charAt(i);char lc = Character.toLowerCase(ch);if (lc == ch) { // lower-case letter means we can get new word// but need to check for multi-letter upper-case (acronym), where assumption// is that the last upper-case char is start of a new wordif (upperCount > 1) {// so insert hyphen before the last character nowresult.insert(result.length() - 1, separator);}upperCount = 0;} else {// Otherwise starts new word, unless beginning of stringif ((upperCount == 0) && (i > 0)) {result.append(separator);}++upperCount;}result.append(lc);}return result.toString();}}/*/**********************************************************/* Standard implementations /***********************************************************//*** @deprecated Since 2.12 use {@link PropertyNamingStrategies.SnakeCaseStrategy} instead* (see* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reason for deprecation)*/@Deprecated // since 2.12public static class SnakeCaseStrategy extends PropertyNamingStrategyBase{@Overridepublic String translate(String input){if (input == null) return input; // garbage in, garbage outint length = input.length();StringBuilder result = new StringBuilder(length * 2);int resultLength = 0;boolean wasPrevTranslated = false;for (int i = 0; i < length; i++){char c = input.charAt(i);if (i > 0 || c != '_') // skip first starting underscore{if (Character.isUpperCase(c)){if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_'){result.append('_');resultLength++;}c = Character.toLowerCase(c);wasPrevTranslated = true;}else{wasPrevTranslated = false;}result.append(c);resultLength++;}}return resultLength > 0 ? result.toString() : input;}}/*** @deprecated Since 2.12 use {@link PropertyNamingStrategies.UpperCamelCaseStrategy} instead* (see* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reason for deprecation)*/@Deprecated // since 2.12public static class UpperCamelCaseStrategy extends PropertyNamingStrategyBase{/*** Converts camelCase to PascalCase* * For example, "userName" would be converted to* "UserName".** @param input formatted as camelCase string* @return input converted to PascalCase format*/@Overridepublic String translate(String input) {if (input == null || input.isEmpty()){return input; // garbage in, garbage out}// Replace first lower-case letter with upper-case equivalentchar c = input.charAt(0);char uc = Character.toUpperCase(c);if (c == uc) {return input;}StringBuilder sb = new StringBuilder(input);sb.setCharAt(0, uc);return sb.toString();}}/*** @deprecated Since 2.12 use {@link PropertyNamingStrategies.LowerCaseStrategy} instead* (see* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reason for deprecation)*/@Deprecated // since 2.12public static class LowerCaseStrategy extends PropertyNamingStrategyBase{@Overridepublic String translate(String input) {return input.toLowerCase();}}/*** @deprecated Since 2.12 use {@link PropertyNamingStrategies.KebabCaseStrategy} instead* (see* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reason for deprecation)*/@Deprecated // since 2.12public static class KebabCaseStrategy extends PropertyNamingStrategyBase{@Overridepublic String translate(String input) {return translateLowerCaseWithSeparator(input, '-');}}/*** @deprecated Since 2.12 use {@link PropertyNamingStrategies.LowerDotCaseStrategy} instead* (see* <a href="https://github.com/FasterXML/jackson-databind/issues/2715">databind#2715</a>* for reason for deprecation)*/@Deprecated // since 2.12public static class LowerDotCaseStrategy extends PropertyNamingStrategyBase {@Overridepublic String translate(String input){return translateLowerCaseWithSeparator(input, '.');}}/*/**********************************************************/* Deprecated variants, aliases/***********************************************************//*** @deprecated Since 2.7 use {@link PropertyNamingStrategies#SNAKE_CASE} instead.*/@Deprecated // since 2.7public static final PropertyNamingStrategy CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES = SNAKE_CASE;/*** @deprecated Since 2.7 use {@link PropertyNamingStrategies#UPPER_CAMEL_CASE} instead;*/@Deprecated // since 2.7public static final PropertyNamingStrategy PASCAL_CASE_TO_CAMEL_CASE = UPPER_CAMEL_CASE;/*** @deprecated In 2.7 use {@link PropertyNamingStrategies.SnakeCaseStrategy} instead*/@Deprecated // since 2.7public static class LowerCaseWithUnderscoresStrategy extends SnakeCaseStrategy {}/*** @deprecated In 2.7 use {@link PropertyNamingStrategies.UpperCamelCaseStrategy} instead*/@Deprecated // since 2.7public static class PascalCaseStrategy extends UpperCamelCaseStrategy { }
}
命名方式说明:
LOWER_CAMEL_CASE(小驼峰命名法)例如:userName、userName
UPPER_CAMEL_CASE(大驼峰命名法) 例如:ServiceDiscovery、LruCacheFactory
SNAKE_CASE(蛇形命名法,就是常用的小写字母加下划线命名法)例如: user_name
LOWER_CASE (小写命名法,字母均小写,单词之间通常用下划线(_)连接)例如:first_name
KEBAB_CASE (采用小写字母和短横线(-)连接单词,形似烤肉串)例如:user-profile
LOWER_DOT_CASE (所有字母均为小写,并用点(.)连接字符的命名方式) 例如:lower.case
// SNAKE_CASE 和 LOWER_CASE 似乎一样,不知道理解没,自行百度一下。
以上都不是我想要,那就自定义。
自定义命名方式
UpperSnakeCaseStrategy.java
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;/*** 配置字段名全大写和下划线分隔(SCREAMING_SNAKE_CASE 大写蛇形命名法)*/
public class UpperSnakeCaseStrategy extends PropertyNamingStrategy {@Overridepublic String nameForField(MapperConfig<?> config, AnnotatedField field, String defaultName) {return toUpperSnakeCase(defaultName);}@Overridepublic String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {return toUpperSnakeCase(defaultName);}@Overridepublic String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {return toUpperSnakeCase(defaultName);}private String toUpperSnakeCase(String input) {if (input == null) return null;return input.replaceAll("([a-z0-9])([A-Z])", "$1_$2").toUpperCase();}
}
全局配置Jackson
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class JacksonConfig {@Beanpublic ObjectMapper objectMapper() {ObjectMapper mapper = new ObjectMapper();// 配置大写蛇形命名法mapper.setPropertyNamingStrategy(new UpperSnakeCaseStrategy());return mapper;}
}
单个类配置
@JsonNaming(UpperSnakeCaseStrategy.class)
public class Xxx{}
针对这一个小驼峰命名,特殊处理
@JsonProperty("yyylList")private List<YyyReqDTO> yyyList;
对需要保留驼峰命名的字段单独使用@JsonProperty注解
针对蛇形命名、小驼峰、大驼峰等正常的命名字段单独使用@JsonProperty注解去实现。
例如:
@JsonProperty("yyy_list")
@JsonProperty("yyyList")
@JsonProperty("YyyList")
蛇形+首字母大写
自测自测,应该也是能行的。
Jackson 提供了UPPER_CAMEL_CASE策略,但需要结合SNAKE_CASE进行额外配置
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class JacksonConfig {@Beanpublic ObjectMapper objectMapper() {ObjectMapper mapper = new ObjectMapper();// 配置大写蛇形命名法mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE.withUpperCaseFirstLetter(true) // 首字母大写return mapper;// 注意:这种方式生成的是 "My_Field_Name",如果需要全大写还需自定义}
}