码字不易,请大佬们点点关注,谢谢~
一、字段过滤与命名策略概述
1.1 核心功能与应用场景
Gson的字段过滤与命名策略是数据序列化和反序列化过程中的重要机制,主要解决以下问题:
- 字段过滤:控制哪些字段应该被序列化或反序列化,常用于敏感数据保护、接口数据裁剪等场景。
- 命名策略:实现Java字段名与JSON字段名之间的映射转换,支持如蛇形命名(snake_case)、大驼峰命名(PascalCase)等多种格式。
1.2 关键组件与实现层次
这两个功能主要由以下组件协同实现:
- ExclusionStrategy:字段过滤的核心接口,允许自定义过滤规则。
- FieldNamingStrategy:命名策略的核心接口,定义字段名转换规则。
- Annotation:通过
@Expose
、@SerializedName
等注解实现细粒度控制。 - ReflectiveTypeAdapterFactory:反射序列化的核心工厂,集成了字段过滤和命名逻辑。
二、字段过滤机制原理
2.1 内置过滤策略
Gson提供了多种内置过滤方式:
2.1.1 @Expose注解
@Expose
注解允许精确控制字段的序列化和反序列化:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Expose {
// 序列化时是否包含该字段,默认为true
boolean serialize() default true;
// 反序列化时是否包含该字段,默认为true
boolean deserialize() default true;
}
使用示例:
public class User {
@Expose
private String username;
@Expose(serialize = false, deserialize = false)
private String password; // 不参与序列化和反序列化
private String email; // 默认参与序列化和反序列化
}
2.1.2 transient关键字
Java原生的transient
关键字也会被Gson识别,标记为transient
的字段不会被序列化:
public class User {
private String username;
private transient String token; // 不会被序列化
}
2.1.3 Modifier过滤
Gson默认会过滤掉static
和transient
修饰的字段:
// ReflectiveTypeAdapterFactory.java
private boolean excludeField(Field field, boolean serialize) {
// 获取字段修饰符
int modifiers = field.getModifiers();
// 过滤掉static和transient字段
if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) {
return true;
}
// 处理@Expose注解
if (exposeAnnotationPolicy != null) {
Expose expose = field.getAnnotation(Expose.class);
if (expose == null) {
return exposeAnnotationPolicy == AnnotationPolicy.EXPOSE;
}
if (serialize ? !expose.serialize() : !expose.deserialize()) {
return true;
}
}
// 应用自定义排除策略
if (excludeField(field, serialize, exclusionStrategies)) {
return true;
}
return false;
}
2.2 自定义排除策略
开发者可以通过实现ExclusionStrategy
接口创建自定义过滤规则:
public interface ExclusionStrategy {
// 序列化时是否排除该类型
boolean shouldSkipClass(Class<?> clazz);
// 序列化时是否排除该字段
boolean shouldSkipField(FieldAttributes f);
}
注册自定义策略:
Gson gson = new GsonBuilder()
.addSerializationExclusionStrategy(new CustomExclusionStrategy())
.addDeserializationExclusionStrategy(new CustomExclusionStrategy())
.create();
2.3 排除策略的应用流程
在字段绑定阶段,Gson会应用排除策略:
// ReflectiveTypeAdapterFactory.java
private Map<String, BoundField> getBoundFields(
Gson context, TypeToken<?> type, Class<?> raw) {
Map<String, BoundField> result = new LinkedHashMap<>();
if (raw.isInterface()) {
return result;
}
// 获取父类的TypeToken
Type declaredType = type.getType();
while (raw != Object.class) {
Field[] fields = raw.getDeclaredFields();
for (Field field : fields) {
// 检查字段是否应被排除
boolean serialize = !excludeField(field, true);
boolean deserialize = !excludeField(field, false);
if (!serialize && !deserialize) {
continue;
}
// 字段绑定逻辑...
}
// 处理父类
type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
raw = type.getRawType();
}
return result;
}
三、命名策略实现原理
3.1 内置命名策略
Gson提供了多种内置命名策略:
3.1.1 IDENTITY(默认策略)
直接使用Java字段名作为JSON字段名:
public class IdentityNamingPolicy implements FieldNamingStrategy {
@Override
public String translateName(Field f) {
return f.getName();
}
}
3.1.2 LOWER_CASE_WITH_UNDERSCORES(蛇形命名)
将Java字段名转换为小写并使用下划线分隔:
public class LowerCaseWithUnderscoresNamingPolicy implements FieldNamingStrategy {
@Override
public String translateName(Field f) {
return separateCamelCase(f.getName(), "_").toLowerCase();
}
private String separateCamelCase(String name, String separator) {
StringBuilder translation = new StringBuilder();
for (int i = 0; i < name.length(); i++) {
char character = name.charAt(i);
if (Character.isUpperCase(character) && translation.length() != 0) {
translation.append(separator);
}
translation.append(character);
}
return translation.toString();
}
}
3.1.3 UPPER_CAMEL_CASE(大驼峰命名)
将Java字段名转换为大驼峰格式:
public class UpperCamelCaseNamingPolicy implements FieldNamingStrategy {
@Override
public String translateName(Field f) {
return modifyString(f.getName());
}
private String modifyString(String fieldName) {
StringBuilder modified = new StringBuilder();
boolean nextUpperCase = false;
for (int i = 0; i < fieldName.length(); i++) {
char currentChar = fieldName.charAt(i);
if (currentChar == '_') {
nextUpperCase = true;
} else if (nextUpperCase) {
modified.append(Character.toUpperCase(currentChar));
nextUpperCase = false;
} else {
modified.append(Character.toLowerCase(currentChar));
}
}
return capitalizeFirstLetter(modified.toString());
}
private String capitalizeFirstLetter(String s) {
if (s.length() == 0) return s;
char first = s.charAt(0);
if (Character.isUpperCase(first)) return s;
else return Character.toUpperCase(first) + s.substring(1);
}
}
3.2 @SerializedName注解
@SerializedName
注解允许为字段指定自定义JSON名称:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SerializedName {
// 主名称
String value();
// 备选名称,用于反序列化时匹配多个可能的JSON字段名
String[] alternate() default {};
}
使用示例:
public class User {
@SerializedName("user_name")
private String username;
@SerializedName(value = "reg_time", alternate = {"registration_time"})
private Date registerTime;
}
3.3 命名策略的应用流程
在字段绑定阶段,Gson会应用命名策略:
// ReflectiveTypeAdapterFactory.java
private Map<String, BoundField> getBoundFields(
Gson context, TypeToken<?> type, Class<?> raw) {
Map<String, BoundField> result = new LinkedHashMap<>();
// ...
for (Field field : fields) {
// ...
// 获取字段的JSON名称
List<String> fieldNames = getFieldNames(field);
// 创建BoundField并绑定到名称
BoundField previous = null;
for (String name : fieldNames) {
previous = result.put(name, boundField);
if (previous != null) {
throw new IllegalArgumentException(
"Duplicate field name: " + name + " for type " + declaredType);
}
}
}
return result;
}
private List<String> getFieldNames(Field field) {
// 检查是否存在@SerializedName注解
SerializedName annotation = field.getAnnotation(SerializedName.class);
if (annotation == null) {
// 应用命名策略
String name = fieldNamingPolicy.translateName(field);
return Collections.singletonList(name);
}
// 处理@SerializedName注解的主名称和备选名称
String serializedName = annotation.value();
String[] alternates = annotation.alternate();
if (alternates.length == 0) {
return Collections.singletonList(serializedName);
}
List<String> fieldNames = new ArrayList<>(alternates.length + 1);
fieldNames.add(serializedName);
for (String alternate : alternates) {
fieldNames.add(alternate);
}
return fieldNames;
}
四、字段过滤与命名策略的协同工作
4.1 配置与使用示例
以下示例展示如何同时配置字段过滤和命名策略:
Gson gson = new GsonBuilder()
// 设置命名策略为蛇形命名
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
// 添加序列化排除策略
.addSerializationExclusionStrategy(new ExclusionStrategy() {
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
@Override
public boolean shouldSkipField(FieldAttributes f) {
// 排除以"internal"开头的字段
return f.getName().startsWith("internal");
}
})
.create();
// 序列化对象
User user = new User("john_doe", "password123", "john@example.com");
String json = gson.toJson(user);
// 输出: {"user_name":"john_doe","email":"john@example.com"}
4.2 优先级规则
当多种过滤和命名规则同时存在时,优先级如下:
- @SerializedName注解:最高优先级,直接指定JSON名称
- @Expose注解:控制字段是否参与序列化/反序列化
- 自定义ExclusionStrategy:自定义过滤规则
- 命名策略:默认命名转换规则
- transient关键字:Java原生字段排除机制
五、高级应用与扩展
5.1 基于注解的高级过滤
可以通过自定义注解实现更复杂的过滤逻辑:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface SensitiveData {
boolean expose() default false;
}
// 自定义排除策略
class SensitiveDataExclusionStrategy implements ExclusionStrategy {
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return clazz.getAnnotation(SensitiveData.class) != null;
}
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(SensitiveData.class) != null;
}
}
5.2 动态命名策略
通过实现FieldNamingStrategy
接口,可以创建动态命名策略:
public class DynamicNamingStrategy implements FieldNamingStrategy {
private final Map<Class<?>, String> prefixMap;
public DynamicNamingStrategy(Map<Class<?>, String> prefixMap) {
this.prefixMap = prefixMap;
}
@Override
public String translateName(Field f) {
Class<?> declaringClass = f.getDeclaringClass();
String prefix = prefixMap.getOrDefault(declaringClass, "");
return prefix + f.getName();
}
}
六、性能考量
6.1 反射开销
字段过滤和命名策略都依赖反射机制,可能带来性能开销:
- 字段访问:通过
Field.setAccessible(true)
绕过访问检查 - 注解解析:运行时解析字段上的注解
- 命名转换:动态转换字段名称
6.2 优化建议
- 缓存策略:对于频繁使用的命名策略,缓存转换结果
- 减少反射:尽量使用编译时生成的代码替代反射
- 优先使用注解:
@SerializedName
和@Expose
比自定义策略效率更高 - 避免复杂逻辑:自定义策略中的复杂逻辑会增加处理时间
七、总结与展望
7.1 核心机制总结
Gson的字段过滤和命名策略通过以下机制实现:
- 排除策略链:多种排除策略按优先级组合应用
- 命名转换:通过策略模式实现灵活的名称映射
- 注解驱动:提供
@SerializedName
和@Expose
等注解简化配置 - 反射机制:通过反射获取字段信息并应用过滤和命名规则
7.2 未来发展方向
- Kotlin支持增强:更好地支持Kotlin的属性和注解
- 编译时代码生成:减少运行时反射开销
- 更灵活的配置方式:支持更丰富的声明式配置
- 与数据验证集成:结合字段过滤实现数据验证功能
- 性能优化:进一步减少反射调用,提高处理效率
通过不断优化和扩展,Gson将继续为开发者提供高效、灵活的JSON处理解决方案,满足日益复杂的应用需求。